Merge "Adds app enumeration support to CompanionDeviceManager" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 11f5c41..5e068ee 100644
--- a/Android.bp
+++ b/Android.bp
@@ -481,6 +481,7 @@
"framework-platform-compat-config",
"libcore-platform-compat-config",
"services-platform-compat-config",
+ "documents-ui-compat-config",
],
static_libs: [
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
@@ -496,7 +497,6 @@
"//frameworks/base/apex/appsearch/framework",
"//frameworks/base/apex/blobstore/framework",
"//frameworks/base/apex/jobscheduler/framework",
- "//frameworks/base/apex/statsd/service",
"//frameworks/base/packages/Tethering/tests/unit",
],
}
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
index d7428cf..761e930 100644
--- a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
+++ b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
@@ -31,6 +31,7 @@
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,6 +59,12 @@
final Context context = InstrumentationRegistry.getInstrumentation().getContext();
}
+ @Before
+ public void setup() {
+ PackageManager.disableApplicationInfoCache();
+ PackageManager.disablePackageInfoCache();
+ }
+
@Test
@DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
public void testCheckPermissionExists() {
diff --git a/apex/Android.bp b/apex/Android.bp
index 362cf95..051986e 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -31,12 +31,12 @@
priv_apps = " " +
"--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
"\\) "
module_libs = " " +
" --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
+ "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
"\\) "
stubs_defaults {
@@ -48,6 +48,7 @@
stubs_defaults {
name: "framework-module-stubs-defaults-systemapi",
args: mainline_stubs_args + priv_apps,
+ srcs: [":framework-annotations"],
installable: false,
}
@@ -59,11 +60,13 @@
stubs_defaults {
name: "framework-module-api-defaults-module_libs_api",
args: mainline_stubs_args + module_libs,
+ srcs: [":framework-annotations"],
installable: false,
}
stubs_defaults {
name: "framework-module-stubs-defaults-module_libs_api",
args: mainline_stubs_args + module_libs + priv_apps,
+ srcs: [":framework-annotations"],
installable: false,
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 4c98b5f..62c90dfa 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -54,7 +54,8 @@
*
* @hide
*/
- public static final int REASON_RESTRAINED = JobProtoEnums.STOP_REASON_RESTRAINED; // 6.
+ public static final int REASON_RESTRICTED_BUCKET =
+ JobProtoEnums.STOP_REASON_RESTRICTED_BUCKET; // 6.
/**
* All the stop reason codes. This should be regarded as an immutable array at runtime.
@@ -72,7 +73,7 @@
REASON_TIMEOUT,
REASON_DEVICE_IDLE,
REASON_DEVICE_THERMAL,
- REASON_RESTRAINED,
+ REASON_RESTRICTED_BUCKET,
};
/**
@@ -88,7 +89,7 @@
case REASON_TIMEOUT: return "timeout";
case REASON_DEVICE_IDLE: return "device_idle";
case REASON_DEVICE_THERMAL: return "thermal";
- case REASON_RESTRAINED: return "restrained";
+ case REASON_RESTRICTED_BUCKET: return "restricted_bucket";
default: return "unknown:" + reasonCode;
}
}
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 c1e529f..b86aba6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1074,7 +1074,15 @@
uId, null, jobStatus.getBatteryName(),
FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
JobProtoEnums.STOP_REASON_CANCELLED, jobStatus.getStandbyBucket(),
- jobStatus.getJobId());
+ jobStatus.getJobId(),
+ jobStatus.hasChargingConstraint(),
+ jobStatus.hasBatteryNotLowConstraint(),
+ jobStatus.hasStorageNotLowConstraint(),
+ jobStatus.hasTimingDelayConstraint(),
+ jobStatus.hasDeadlineConstraint(),
+ jobStatus.hasIdleConstraint(),
+ jobStatus.hasConnectivityConstraint(),
+ jobStatus.hasContentTriggerConstraint());
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
@@ -1955,9 +1963,19 @@
continue;
}
if (!running.isReady()) {
- serviceContext.cancelExecutingJobLocked(
- JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
- "cancelled due to unsatisfied constraints");
+ // If a restricted job doesn't have dynamic constraints satisfied, assume that's
+ // the reason the job is being stopped, instead of because of other constraints
+ // not being satisfied.
+ if (running.getEffectiveStandbyBucket() == RESTRICTED_INDEX
+ && !running.areDynamicConstraintsSatisfied()) {
+ serviceContext.cancelExecutingJobLocked(
+ JobParameters.REASON_RESTRICTED_BUCKET,
+ "cancelled due to restricted bucket");
+ } else {
+ serviceContext.cancelExecutingJobLocked(
+ JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
+ "cancelled due to unsatisfied constraints");
+ }
} else {
final JobRestriction restriction = checkIfRestricted(running);
if (restriction != null) {
@@ -3015,6 +3033,10 @@
return 0;
}
+ void resetExecutionQuota(@NonNull String pkgName, int userId) {
+ mQuotaController.clearAppStats(pkgName, userId);
+ }
+
void resetScheduleQuota() {
mQuotaTracker.clear();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index 6becf04..9571708 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -66,6 +66,8 @@
return getJobState(pw);
case "heartbeat":
return doHeartbeat(pw);
+ case "reset-execution-quota":
+ return resetExecutionQuota(pw);
case "reset-schedule-quota":
return resetScheduleQuota(pw);
case "trigger-dock-state":
@@ -346,6 +348,40 @@
return -1;
}
+ private int resetExecutionQuota(PrintWriter pw) throws Exception {
+ checkPermission("reset execution quota");
+
+ int userId = UserHandle.USER_SYSTEM;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-u":
+ case "--user":
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+
+ default:
+ pw.println("Error: unknown option '" + opt + "'");
+ return -1;
+ }
+ }
+
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = ActivityManager.getCurrentUser();
+ }
+
+ final String pkgName = getNextArgRequired();
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mInternal.resetExecutionQuota(pkgName, userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return 0;
+ }
+
private int resetScheduleQuota(PrintWriter pw) throws Exception {
checkPermission("reset schedule quota");
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 26db4a3..565ed95 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -22,6 +22,7 @@
import android.app.job.IJobService;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
+import android.app.job.JobProtoEnums;
import android.app.job.JobWorkItem;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
@@ -45,6 +46,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.job.controllers.JobStatus;
@@ -273,9 +275,20 @@
return false;
}
mJobPackageTracker.noteActive(job);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
+ job.getSourceUid(), null, job.getBatteryName(),
+ FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
+ JobProtoEnums.STOP_REASON_UNKNOWN, job.getStandbyBucket(), job.getJobId(),
+ job.hasChargingConstraint(),
+ job.hasBatteryNotLowConstraint(),
+ job.hasStorageNotLowConstraint(),
+ job.hasTimingDelayConstraint(),
+ job.hasDeadlineConstraint(),
+ job.hasIdleConstraint(),
+ job.hasConnectivityConstraint(),
+ job.hasContentTriggerConstraint());
try {
- mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid(),
- job.getStandbyBucket(), job.getJobId());
+ mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
} catch (RemoteException e) {
// Whatever.
}
@@ -779,10 +792,21 @@
applyStoppedReasonLocked(reason);
completedJob = mRunningJob;
mJobPackageTracker.noteInactive(completedJob, mParams.getStopReason(), reason);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
+ completedJob.getSourceUid(), null, completedJob.getBatteryName(),
+ FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
+ mParams.getStopReason(), completedJob.getStandbyBucket(), completedJob.getJobId(),
+ completedJob.hasChargingConstraint(),
+ completedJob.hasBatteryNotLowConstraint(),
+ completedJob.hasStorageNotLowConstraint(),
+ completedJob.hasTimingDelayConstraint(),
+ completedJob.hasDeadlineConstraint(),
+ completedJob.hasIdleConstraint(),
+ completedJob.hasConnectivityConstraint(),
+ completedJob.hasContentTriggerConstraint());
try {
- mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
- mRunningJob.getSourceUid(), mParams.getStopReason(),
- mRunningJob.getStandbyBucket(), mRunningJob.getJobId());
+ mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
+ mParams.getStopReason());
} catch (RemoteException e) {
// Whatever.
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index aa3d74a..d7b1e07 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -16,6 +16,8 @@
package com.android.server.job.controllers;
+import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
+
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
@@ -196,6 +198,9 @@
} else {
isActive = (activeState == KNOWN_ACTIVE);
}
+ if (isActive && jobStatus.getStandbyBucket() == NEVER_INDEX) {
+ Slog.wtf(TAG, "App became active but still in NEVER bucket");
+ }
boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
didChange |= jobStatus.setUidActive(isActive);
return didChange;
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 b63cc19..81dbc87 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
@@ -1271,7 +1271,8 @@
// sessions (exempt from dynamic restrictions), we need the additional check to ensure
// that NEVER jobs don't run.
// TODO: cleanup quota and standby bucket management so we don't need the additional checks
- if ((!mReadyWithinQuota && !mReadyDynamicSatisfied) || standbyBucket == NEVER_INDEX) {
+ if ((!mReadyWithinQuota && !mReadyDynamicSatisfied)
+ || getEffectiveStandbyBucket() == NEVER_INDEX) {
return false;
}
// Deadline constraint trumps other constraints besides quota and dynamic (except for
@@ -1293,6 +1294,11 @@
CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW
| CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE;
+ /** Returns true whenever all dynamically set constraints are satisfied. */
+ public boolean areDynamicConstraintsSatisfied() {
+ return mReadyDynamicSatisfied;
+ }
+
/**
* @return Whether the constraints set on this job are satisfied.
*/
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 8eefac8..4393a95 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
@@ -579,23 +579,7 @@
Slog.wtf(TAG, "Told app removed but given null package name.");
return;
}
- final int userId = UserHandle.getUserId(uid);
- mTrackedJobs.delete(userId, packageName);
- Timer timer = mPkgTimers.get(userId, packageName);
- if (timer != null) {
- if (timer.isActive()) {
- Slog.wtf(TAG, "onAppRemovedLocked called before Timer turned off.");
- timer.dropEverythingLocked();
- }
- mPkgTimers.delete(userId, packageName);
- }
- mTimingSessions.delete(userId, packageName);
- QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
- if (alarmListener != null) {
- mAlarmManager.cancel(alarmListener);
- mInQuotaAlarmListeners.delete(userId, packageName);
- }
- mExecutionStatsCache.delete(userId, packageName);
+ clearAppStats(packageName, UserHandle.getUserId(uid));
mForegroundUids.delete(uid);
mUidToPackageCache.remove(uid);
}
@@ -610,6 +594,26 @@
mUidToPackageCache.clear();
}
+ /** Drop all historical stats and stop tracking any active sessions for the specified app. */
+ public void clearAppStats(@NonNull String packageName, int userId) {
+ mTrackedJobs.delete(userId, packageName);
+ Timer timer = mPkgTimers.get(userId, packageName);
+ if (timer != null) {
+ if (timer.isActive()) {
+ Slog.e(TAG, "clearAppStats called before Timer turned off.");
+ timer.dropEverythingLocked();
+ }
+ mPkgTimers.delete(userId, packageName);
+ }
+ mTimingSessions.delete(userId, packageName);
+ QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+ if (alarmListener != null) {
+ mAlarmManager.cancel(alarmListener);
+ mInQuotaAlarmListeners.delete(userId, packageName);
+ }
+ mExecutionStatsCache.delete(userId, packageName);
+ }
+
private boolean isUidInForeground(int uid) {
if (UserHandle.isCore(uid)) {
return true;
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 a4ab31d..7a1b4f2 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1844,7 +1844,7 @@
break;
case MSG_REPORT_SYNC_SCHEDULED:
- final boolean exempted = msg.arg1 > 0 ? true : false;
+ final boolean exempted = msg.arg2 > 0 ? true : false;
if (exempted) {
reportExemptedSyncScheduled((String) msg.obj, msg.arg1);
} else {
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 11d3a68..566f4cd 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -1114,20 +1114,22 @@
static {
// Using a LinkedHashMap to keep the insertion order when iterating over the keys.
LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>();
- extractorFactoriesByName.put("exo.Ac3Parser", Ac3Extractor::new);
- extractorFactoriesByName.put("exo.Ac4Parser", Ac4Extractor::new);
- extractorFactoriesByName.put("exo.AdtsParser", AdtsExtractor::new);
- extractorFactoriesByName.put("exo.AmrParser", AmrExtractor::new);
- extractorFactoriesByName.put("exo.FlacParser", FlacExtractor::new);
- extractorFactoriesByName.put("exo.FlvParser", FlvExtractor::new);
- extractorFactoriesByName.put("exo.FragmentedMp4Parser", FragmentedMp4Extractor::new);
+ // Parsers are ordered to match ExoPlayer's DefaultExtractorsFactory extractor ordering,
+ // which in turn aims to minimize the chances of incorrect extractor selections.
extractorFactoriesByName.put("exo.MatroskaParser", MatroskaExtractor::new);
- extractorFactoriesByName.put("exo.Mp3Parser", Mp3Extractor::new);
+ extractorFactoriesByName.put("exo.FragmentedMp4Parser", FragmentedMp4Extractor::new);
extractorFactoriesByName.put("exo.Mp4Parser", Mp4Extractor::new);
+ extractorFactoriesByName.put("exo.Mp3Parser", Mp3Extractor::new);
+ extractorFactoriesByName.put("exo.AdtsParser", AdtsExtractor::new);
+ extractorFactoriesByName.put("exo.Ac3Parser", Ac3Extractor::new);
+ extractorFactoriesByName.put("exo.TsParser", TsExtractor::new);
+ extractorFactoriesByName.put("exo.FlvParser", FlvExtractor::new);
extractorFactoriesByName.put("exo.OggParser", OggExtractor::new);
extractorFactoriesByName.put("exo.PsParser", PsExtractor::new);
- extractorFactoriesByName.put("exo.TsParser", TsExtractor::new);
extractorFactoriesByName.put("exo.WavParser", WavExtractor::new);
+ extractorFactoriesByName.put("exo.AmrParser", AmrExtractor::new);
+ extractorFactoriesByName.put("exo.Ac4Parser", Ac4Extractor::new);
+ extractorFactoriesByName.put("exo.FlacParser", FlacExtractor::new);
EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName);
HashMap<String, Class> expectedTypeByParameterName = new HashMap<>();
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
index 205ffc2..30a8b45 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
@@ -246,7 +246,7 @@
permissionState.isGranted() && (permissionState.getFlags()
& PackageManager.FLAG_PERMISSION_ONE_TIME) == 0));
serializer.attribute(null, ATTRIBUTE_FLAGS, Integer.toHexString(
- permissionState.getFlags() & ~PackageManager.FLAG_PERMISSION_ONE_TIME));
+ permissionState.getFlags()));
serializer.endTag(null, TAG_PERMISSION);
}
}
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index 2f3e2ac..4c96263 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -63,11 +63,7 @@
shared_libs: [
"libnativehelper", // Has stable abi - should not be copied into apex.
"liblog", // Has a stable abi - should not be copied into apex.
- ],
- static_libs: [
- //TODO: make shared - need libstatssocket to also live in the apex.
"libstatssocket",
- "libcutils", // TODO: remove - needed by libstatssocket
],
//TODO: is libc++_static correct?
stl: "libc++_static",
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 0f52f15..1bd770a 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -69,6 +69,8 @@
"android.util",
],
+ plugins: ["java_api_finder"],
+
hostdex: true, // for hiddenapi check
visibility: [
"//frameworks/base/apex/statsd:__subpackages__",
diff --git a/apex/statsd/service/Android.bp b/apex/statsd/service/Android.bp
index 0f325e3..ff56bb5 100644
--- a/apex/statsd/service/Android.bp
+++ b/apex/statsd/service/Android.bp
@@ -31,6 +31,8 @@
"android_module_lib_stubs_current",
],
+ plugins: ["java_api_finder"],
+
apex_available: [
"com.android.os.statsd",
"test_com.android.os.statsd",
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index cb167c3..dc61f2ae 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -21,11 +21,13 @@
import android.app.AlarmManager.OnAlarmListener;
import android.app.StatsManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -85,12 +87,6 @@
public static final int DEATH_THRESHOLD = 10;
- // TODO(b/149090705): Implement an alternative to sending broadcast with @hide flag
- // FLAG_RECEIVER_INCLUDE_BACKGROUND. Instead of using the flag, find the
- // list of registered broadcast receivers and send them directed broadcasts
- // to wake them up. See b/147374337.
- private static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
-
static final class CompanionHandler extends Handler {
CompanionHandler(Looper looper) {
super(looper);
@@ -498,9 +494,25 @@
Log.d(TAG, "learned that statsdReady");
}
sayHiToStatsd(); // tell statsd that we're ready too and link to it
- mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED)
- .addFlags(FLAG_RECEIVER_INCLUDE_BACKGROUND),
- UserHandle.SYSTEM, android.Manifest.permission.DUMP);
+
+ final Intent intent = new Intent(StatsManager.ACTION_STATSD_STARTED);
+ // Retrieve list of broadcast receivers for this broadcast & send them directed broadcasts
+ // to wake them up (if they're in background).
+ List<ResolveInfo> resolveInfos =
+ mContext.getPackageManager().queryBroadcastReceiversAsUser(
+ intent, 0, UserHandle.SYSTEM);
+ if (resolveInfos == null || resolveInfos.isEmpty()) {
+ return; // No need to send broadcast.
+ }
+
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ Intent intentToSend = new Intent(intent);
+ intentToSend.setComponent(new ComponentName(
+ resolveInfo.activityInfo.applicationInfo.packageName,
+ resolveInfo.activityInfo.name));
+ mContext.sendBroadcastAsUser(intentToSend, UserHandle.SYSTEM,
+ android.Manifest.permission.DUMP);
+ }
}
@Override
diff --git a/api/current.txt b/api/current.txt
index 1227006..2ec3bcc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2992,9 +2992,7 @@
method public int getNonInteractiveUiTimeoutMillis();
method public android.content.pm.ResolveInfo getResolveInfo();
method public String getSettingsActivityName();
- method @Nullable public android.graphics.drawable.Drawable loadAnimatedImage(@NonNull android.content.Context);
method public String loadDescription(android.content.pm.PackageManager);
- method @Nullable public String loadHtmlDescription(@NonNull android.content.pm.PackageManager);
method public CharSequence loadSummary(android.content.pm.PackageManager);
method public void setInteractiveUiTimeoutMillis(@IntRange(from=0) int);
method public void setNonInteractiveUiTimeoutMillis(@IntRange(from=0) int);
@@ -8189,6 +8187,7 @@
field public static final String OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth";
field public static final String OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight";
field public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
+ field public static final String OPTION_APPWIDGET_RESTORE_COMPLETED = "appWidgetRestoreCompleted";
}
public class AppWidgetProvider extends android.content.BroadcastReceiver {
@@ -29356,9 +29355,9 @@
public abstract class TvInputService extends android.app.Service {
ctor public TvInputService();
method public final android.os.IBinder onBind(android.content.Intent);
- method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(String);
+ method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String);
method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String, @NonNull String);
- method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(String);
+ method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(@NonNull String);
method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String);
field public static final int PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND = 100; // 0x64
field public static final int PRIORITY_HINT_USE_CASE_TYPE_LIVE = 400; // 0x190
@@ -31105,6 +31104,7 @@
public class ScanResult implements android.os.Parcelable {
ctor public ScanResult(@NonNull android.net.wifi.ScanResult);
+ ctor public ScanResult();
method public int describeContents();
method @NonNull public java.util.List<android.net.wifi.ScanResult.InformationElement> getInformationElements();
method public int getWifiStandard();
@@ -31376,6 +31376,15 @@
field public static final int LINK_SPEED_UNKNOWN = -1; // 0xffffffff
}
+ public static final class WifiInfo.Builder {
+ ctor public WifiInfo.Builder();
+ method @NonNull public android.net.wifi.WifiInfo build();
+ method @NonNull public android.net.wifi.WifiInfo.Builder setBssid(@NonNull String);
+ method @NonNull public android.net.wifi.WifiInfo.Builder setNetworkId(int);
+ method @NonNull public android.net.wifi.WifiInfo.Builder setRssi(int);
+ method @NonNull public android.net.wifi.WifiInfo.Builder setSsid(@NonNull byte[]);
+ }
+
public class WifiManager {
method @Deprecated public int addNetwork(android.net.wifi.WifiConfiguration);
method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public int addNetworkSuggestions(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>);
@@ -31559,6 +31568,20 @@
public final class WifiNetworkSuggestion implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public android.net.MacAddress getBssid();
+ method @Nullable public android.net.wifi.WifiEnterpriseConfig getEnterpriseConfig();
+ method @Nullable public String getPassphrase();
+ method @Nullable public android.net.wifi.hotspot2.PasspointConfiguration getPasspointConfig();
+ method @IntRange(from=0) public int getPriority();
+ method @Nullable public String getSsid();
+ method public boolean isAppInteractionRequired();
+ method public boolean isCredentialSharedWithUser();
+ method public boolean isEnhancedOpen();
+ method public boolean isHiddenSsid();
+ method public boolean isInitialAutojoinEnabled();
+ method public boolean isMetered();
+ method public boolean isUntrusted();
+ method public boolean isUserInteractionRequired();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiNetworkSuggestion> CREATOR;
}
@@ -48146,6 +48169,7 @@
method public String getMmsUserAgent();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNai();
method public String getNetworkCountryIso();
+ method @NonNull public String getNetworkCountryIso(int);
method public String getNetworkOperator();
method public String getNetworkOperatorName();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode();
@@ -56954,6 +56978,7 @@
method @NonNull public android.view.inline.InlinePresentationSpec getPresentationSpec();
method @NonNull public String getSource();
method @NonNull public String getType();
+ method public boolean isPinned();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InlineSuggestionInfo> CREATOR;
field public static final String SOURCE_AUTOFILL = "android:autofill";
diff --git a/api/system-current.txt b/api/system-current.txt
index f84b415..9a1aacd 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4944,8 +4944,24 @@
package android.media.tv.tuner.dvr {
- public class Dvr implements java.lang.AutoCloseable {
- ctor protected Dvr(int);
+ public class DvrPlayback implements java.lang.AutoCloseable {
+ method public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter);
+ method public void close();
+ method public int configure(@NonNull android.media.tv.tuner.dvr.DvrSettings);
+ method public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter);
+ method public int flush();
+ method public long read(long);
+ method public long read(@NonNull byte[], long, long);
+ method public void setFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
+ method public int start();
+ method public int stop();
+ field public static final int PLAYBACK_STATUS_ALMOST_EMPTY = 2; // 0x2
+ field public static final int PLAYBACK_STATUS_ALMOST_FULL = 4; // 0x4
+ field public static final int PLAYBACK_STATUS_EMPTY = 1; // 0x1
+ field public static final int PLAYBACK_STATUS_FULL = 8; // 0x8
+ }
+
+ public class DvrRecorder implements java.lang.AutoCloseable {
method public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter);
method public void close();
method public int configure(@NonNull android.media.tv.tuner.dvr.DvrSettings);
@@ -4954,20 +4970,6 @@
method public void setFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
method public int start();
method public int stop();
- field public static final int TYPE_PLAYBACK = 1; // 0x1
- field public static final int TYPE_RECORD = 0; // 0x0
- }
-
- public class DvrPlayback extends android.media.tv.tuner.dvr.Dvr {
- method public long read(long);
- method public long read(@NonNull byte[], long, long);
- field public static final int PLAYBACK_STATUS_ALMOST_EMPTY = 2; // 0x2
- field public static final int PLAYBACK_STATUS_ALMOST_FULL = 4; // 0x4
- field public static final int PLAYBACK_STATUS_EMPTY = 1; // 0x1
- field public static final int PLAYBACK_STATUS_FULL = 8; // 0x8
- }
-
- public class DvrRecorder extends android.media.tv.tuner.dvr.Dvr {
method public long write(long);
method public long write(@NonNull byte[], long, long);
}
@@ -7169,7 +7171,6 @@
}
public class ScanResult implements android.os.Parcelable {
- ctor public ScanResult();
field public static final int CIPHER_CCMP = 3; // 0x3
field public static final int CIPHER_GCMP_256 = 4; // 0x4
field public static final int CIPHER_NONE = 0; // 0x0
@@ -7314,8 +7315,8 @@
method @Deprecated public int getDisableReasonCounter(int);
method @Deprecated public long getDisableTime();
method @Deprecated public static int getMaxNetworkSelectionDisableReason();
- method @Deprecated @Nullable public static String getNetworkDisableReasonString(int);
method @Deprecated public int getNetworkSelectionDisableReason();
+ method @Deprecated @Nullable public static String getNetworkSelectionDisableReasonString(int);
method @Deprecated public int getNetworkSelectionStatus();
method @Deprecated @NonNull public String getNetworkStatusString();
method @Deprecated public boolean hasEverConnected();
@@ -7377,15 +7378,6 @@
field public static final int INVALID_RSSI = -127; // 0xffffff81
}
- public static final class WifiInfo.Builder {
- ctor public WifiInfo.Builder();
- method @NonNull public android.net.wifi.WifiInfo build();
- method @NonNull public android.net.wifi.WifiInfo.Builder setBssid(@NonNull String);
- method @NonNull public android.net.wifi.WifiInfo.Builder setNetworkId(int);
- method @NonNull public android.net.wifi.WifiInfo.Builder setRssi(int);
- method @NonNull public android.net.wifi.WifiInfo.Builder setSsid(@NonNull byte[]);
- }
-
public class WifiManager {
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean);
@@ -7528,7 +7520,7 @@
}
public static interface WifiManager.ScoreChangeCallback {
- method public void onScoreChange(int, @NonNull android.net.NetworkScore);
+ method public void onScoreChange(int, int);
method public void onTriggerUpdateOfWifiUsabilityStats(int);
}
@@ -7554,6 +7546,52 @@
method public void stop(int);
}
+ public final class WifiMigration {
+ method @Nullable public static android.net.wifi.WifiMigration.ConfigStoreMigrationData loadFromConfigStore();
+ method @NonNull public static android.net.wifi.WifiMigration.SettingsMigrationData loadFromSettings(@NonNull android.content.Context);
+ method public static void removeConfigStore();
+ }
+
+ public static final class WifiMigration.ConfigStoreMigrationData implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public java.util.List<android.net.wifi.WifiConfiguration> getUserSavedNetworkConfigurations();
+ method @Nullable public android.net.wifi.SoftApConfiguration getUserSoftApConfiguration();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiMigration.ConfigStoreMigrationData> CREATOR;
+ }
+
+ public static final class WifiMigration.ConfigStoreMigrationData.Builder {
+ ctor public WifiMigration.ConfigStoreMigrationData.Builder();
+ method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData build();
+ method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>);
+ method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
+ }
+
+ public static final class WifiMigration.SettingsMigrationData implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getP2pDeviceName();
+ method public boolean isP2pFactoryResetPending();
+ method public boolean isScanAlwaysAvailable();
+ method public boolean isScanThrottleEnabled();
+ method public boolean isSoftApTimeoutEnabled();
+ method public boolean isVerboseLoggingEnabled();
+ method public boolean isWakeUpEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiMigration.SettingsMigrationData> CREATOR;
+ }
+
+ public static final class WifiMigration.SettingsMigrationData.Builder {
+ ctor public WifiMigration.SettingsMigrationData.Builder();
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData build();
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setP2pDeviceName(@Nullable String);
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setP2pFactoryResetPending(boolean);
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setScanAlwaysAvailable(boolean);
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setScanThrottleEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setSoftApTimeoutEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setVerboseLoggingEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setWakeUpEnabled(boolean);
+ }
+
public class WifiNetworkConnectionStatistics implements android.os.Parcelable {
ctor public WifiNetworkConnectionStatistics(int, int);
ctor public WifiNetworkConnectionStatistics();
@@ -7570,7 +7608,6 @@
}
public final class WifiNetworkSuggestion implements android.os.Parcelable {
- method @Nullable public android.net.wifi.hotspot2.PasspointConfiguration getPasspointConfiguration();
method @NonNull public android.net.wifi.WifiConfiguration getWifiConfiguration();
}
@@ -7578,51 +7615,6 @@
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int);
}
- public final class WifiOemMigrationHook {
- method @Nullable public static android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData loadFromConfigStore();
- method @NonNull public static android.net.wifi.WifiOemMigrationHook.SettingsMigrationData loadFromSettings(@NonNull android.content.Context);
- }
-
- public static final class WifiOemMigrationHook.ConfigStoreMigrationData implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public java.util.List<android.net.wifi.WifiConfiguration> getUserSavedNetworkConfigurations();
- method @Nullable public android.net.wifi.SoftApConfiguration getUserSoftApConfiguration();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData> CREATOR;
- }
-
- public static final class WifiOemMigrationHook.ConfigStoreMigrationData.Builder {
- ctor public WifiOemMigrationHook.ConfigStoreMigrationData.Builder();
- method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData build();
- method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
- }
-
- public static final class WifiOemMigrationHook.SettingsMigrationData implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public String getP2pDeviceName();
- method public boolean isP2pFactoryResetPending();
- method public boolean isScanAlwaysAvailable();
- method public boolean isScanThrottleEnabled();
- method public boolean isSoftApTimeoutEnabled();
- method public boolean isVerboseLoggingEnabled();
- method public boolean isWakeUpEnabled();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemMigrationHook.SettingsMigrationData> CREATOR;
- }
-
- public static final class WifiOemMigrationHook.SettingsMigrationData.Builder {
- ctor public WifiOemMigrationHook.SettingsMigrationData.Builder();
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData build();
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setP2pDeviceName(@Nullable String);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setP2pFactoryResetPending(boolean);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setScanAlwaysAvailable(boolean);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setScanThrottleEnabled(boolean);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setSoftApTimeoutEnabled(boolean);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setVerboseLoggingEnabled(boolean);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setWakeUpEnabled(boolean);
- }
-
public class WifiScanner {
method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]);
method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
@@ -9939,6 +9931,7 @@
method @NonNull public android.service.autofill.augmented.FillResponse build();
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow);
+ method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@Nullable java.util.List<android.service.autofill.InlinePresentation>);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>);
}
@@ -9966,28 +9959,6 @@
method @NonNull @WorkerThread public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
}
- public abstract class CarrierMessagingServiceWrapper {
- ctor public CarrierMessagingServiceWrapper();
- method public boolean bindToCarrierMessagingService(@NonNull android.content.Context, @NonNull String);
- method public void disposeConnection(@NonNull android.content.Context);
- method public void downloadMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public void filterSms(@NonNull android.service.carrier.MessagePdu, @NonNull String, int, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public abstract void onServiceReady();
- method public void sendDataSms(@NonNull byte[], int, @NonNull String, int, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public void sendMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public void sendMultipartTextSms(@NonNull java.util.List<java.lang.String>, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public void sendTextSms(@NonNull String, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- }
-
- public abstract static class CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper {
- ctor public CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper();
- method public void onDownloadMmsComplete(int);
- method public void onFilterComplete(int);
- method public void onSendMmsComplete(int, @Nullable byte[]);
- method public void onSendMultipartSmsComplete(int, @Nullable int[]);
- method public void onSendSmsComplete(int, int);
- }
-
}
package android.service.contentcapture {
@@ -11738,7 +11709,6 @@
method public int getMaxNumberOfSimultaneouslyActiveSims();
method public static long getMaxNumberVerificationTimeoutMillis();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getMergedImsisFromGroup();
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getNetworkCountryIso(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
method public int getSimApplicationState();
@@ -11796,7 +11766,6 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAlwaysReportSignalStrength(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallForwarding(@NonNull android.telephony.CallForwardingInfo);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallWaitingStatus(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
@@ -11811,7 +11780,6 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(@NonNull String, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPolicyDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRadioEnabled(boolean);
@@ -11940,45 +11908,6 @@
field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0
}
- public class TelephonyRegistryManager {
- method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
- method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
- method public void listenForSubscriber(int, @NonNull String, @NonNull String, @NonNull android.telephony.PhoneStateListener, int, boolean);
- method public void notifyActiveDataSubIdChanged(int);
- method public void notifyBarringInfoChanged(int, int, @NonNull android.telephony.BarringInfo);
- method public void notifyCallForwardingChanged(int, boolean);
- method public void notifyCallQualityChanged(int, int, @NonNull android.telephony.CallQuality, int);
- method public void notifyCallStateChanged(int, int, int, @Nullable String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
- method public void notifyCarrierNetworkChange(boolean);
- method public void notifyCellInfoChanged(int, @NonNull java.util.List<android.telephony.CellInfo>);
- method public void notifyCellLocation(int, @NonNull android.telephony.CellIdentity);
- method public void notifyDataActivationStateChanged(int, int, int);
- method public void notifyDataActivityChanged(int, int);
- method public void notifyDataConnectionForSubscriber(int, int, int, @Nullable android.telephony.PreciseDataConnectionState);
- method public void notifyDisconnectCause(int, int, int, int);
- method public void notifyDisplayInfoChanged(int, int, @NonNull android.telephony.DisplayInfo);
- method public void notifyEmergencyNumberList(int, int);
- method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo);
- method public void notifyMessageWaitingChanged(int, int, boolean);
- method public void notifyOpportunisticSubscriptionInfoChanged();
- method public void notifyOutgoingEmergencyCall(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
- method public void notifyOutgoingEmergencySms(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
- method public void notifyPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
- method public void notifyPreciseCallState(int, int, int, int, int);
- method public void notifyPreciseDataConnectionFailed(int, int, int, @Nullable String, int);
- method public void notifyRadioPowerStateChanged(int, int, int);
- method public void notifyRegistrationFailed(int, int, @NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
- method public void notifyServiceStateChanged(int, int, @NonNull android.telephony.ServiceState);
- method public void notifySignalStrengthChanged(int, int, @NonNull android.telephony.SignalStrength);
- method public void notifySrvccStateChanged(int, int);
- method public void notifySubscriptionInfoChanged();
- method public void notifyUserMobileDataStateChanged(int, int, boolean);
- method public void notifyVoiceActivationStateChanged(int, int, int);
- method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
- method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
- }
-
public final class UiccAccessRule implements android.os.Parcelable {
ctor public UiccAccessRule(byte[], @Nullable String, long);
method public int describeContents();
diff --git a/api/test-current.txt b/api/test-current.txt
index 2d15c0e..f25f108 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -118,7 +118,6 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void requestPictureInPictureMode(@NonNull android.os.IBinder);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeDockedStack(android.graphics.Rect, android.graphics.Rect);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePinnedStack(int, android.graphics.Rect, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setDisplayToSingleTaskInstance(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
@@ -413,6 +412,12 @@
field public static final String COLUMN_MEDIASTORE_URI = "mediastore_uri";
}
+ public class DreamManager {
+ method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void setActiveDream(@NonNull android.content.ComponentName);
+ method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void startDream(@NonNull android.content.ComponentName);
+ method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void stopDream();
+ }
+
public final class NotificationChannel implements android.os.Parcelable {
method public int getOriginalImportance();
method public boolean isBlockableSystem();
@@ -792,6 +797,7 @@
field public static final String BUGREPORT_SERVICE = "bugreport";
field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
+ field public static final String DREAM_SERVICE = "dream";
field public static final String ETHERNET_SERVICE = "ethernet";
field public static final String NETWORK_STACK_SERVICE = "network_stack";
field public static final String PERMISSION_SERVICE = "permission";
@@ -975,6 +981,14 @@
field @Nullable public final String backgroundPermission;
}
+ public final class ProviderInfoList implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public static android.content.pm.ProviderInfoList fromList(@NonNull java.util.List<android.content.pm.ProviderInfo>);
+ method @NonNull public java.util.List<android.content.pm.ProviderInfo> getList();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ProviderInfoList> CREATOR;
+ }
+
public final class ShortcutInfo implements android.os.Parcelable {
method public boolean isVisibleToPublisher();
}
@@ -2463,7 +2477,9 @@
}
public final class Parcel {
+ method public boolean allowSquashing();
method public int readExceptionCode();
+ method public void restoreAllowSquashing(boolean);
}
public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -3254,6 +3270,7 @@
method @NonNull public android.service.autofill.augmented.FillResponse build();
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow);
+ method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@Nullable java.util.List<android.service.autofill.InlinePresentation>);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>);
}
@@ -3753,7 +3770,6 @@
method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
method public int getEmergencyNumberDbVersion();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
- method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNetworkCountryIso(int);
method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
@@ -5196,7 +5212,7 @@
}
public final class InlineSuggestionInfo implements android.os.Parcelable {
- method @NonNull public static android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(@NonNull android.view.inline.InlinePresentationSpec, @NonNull String, @Nullable String[]);
+ method @NonNull public static android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(@NonNull android.view.inline.InlinePresentationSpec, @NonNull String, @Nullable String[], @NonNull String, boolean);
}
public final class InlineSuggestionsResponse implements android.os.Parcelable {
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 95de6c5..1e200c5 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -146,6 +146,8 @@
struct uhid_event ev = {};
ev.type = UHID_CREATE2;
strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name));
+ std::string uniq = android::base::StringPrintf("Id: %d", id);
+ strlcpy(reinterpret_cast<char*>(ev.u.create2.uniq), uniq.c_str(), sizeof(ev.u.create2.uniq));
memcpy(&ev.u.create2.rd_data, descriptor.data(), size * sizeof(ev.u.create2.rd_data[0]));
ev.u.create2.rd_size = size;
ev.u.create2.bus = bus;
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 66f5c39..645dc93 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -36,18 +36,7 @@
],
host_supported: true,
srcs: [
- "libidmap2/BinaryStreamVisitor.cpp",
- "libidmap2/CommandLineOptions.cpp",
- "libidmap2/FileUtils.cpp",
- "libidmap2/Idmap.cpp",
- "libidmap2/Policies.cpp",
- "libidmap2/PrettyPrintVisitor.cpp",
- "libidmap2/RawPrintVisitor.cpp",
- "libidmap2/ResourceMapping.cpp",
- "libidmap2/ResourceUtils.cpp",
- "libidmap2/Result.cpp",
- "libidmap2/XmlParser.cpp",
- "libidmap2/ZipFile.cpp",
+ "libidmap2/**/*.cpp",
],
export_include_dirs: ["include"],
target: {
@@ -61,6 +50,7 @@
"libcutils",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
},
host: {
@@ -73,6 +63,37 @@
"libcutils",
"libutils",
"libziparchive",
+ "libidmap2_policies",
+ ],
+ },
+ },
+}
+
+cc_library {
+ name: "libidmap2_policies",
+ defaults: [
+ "idmap2_defaults",
+ ],
+ host_supported: true,
+ export_include_dirs: ["libidmap2_policies/include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ android: {
+ static: {
+ enabled: false,
+ },
+ shared_libs: [
+ "libandroidfw",
+ ],
+ },
+ host: {
+ shared: {
+ enabled: false,
+ },
+ static_libs: [
+ "libandroidfw",
],
},
},
@@ -118,6 +139,7 @@
"libutils",
"libz",
"libziparchive",
+ "libidmap2_policies",
],
},
host: {
@@ -129,6 +151,7 @@
"liblog",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
shared_libs: [
"libz",
@@ -162,6 +185,7 @@
"libidmap2",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
},
host: {
@@ -173,12 +197,14 @@
"liblog",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
shared_libs: [
"libz",
],
},
},
+
}
cc_binary {
@@ -199,6 +225,7 @@
"libidmap2",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
static_libs: [
"libidmap2daidl",
@@ -231,3 +258,16 @@
],
path: "idmap2d/aidl",
}
+
+aidl_interface {
+ name: "overlayable_policy_aidl",
+ srcs: [":overlayable_policy_aidl_files"],
+}
+
+filegroup {
+ name: "overlayable_policy_aidl_files",
+ srcs: [
+ "idmap2d/aidl/android/os/OverlayablePolicy.aidl",
+ ],
+ path: "idmap2d/aidl",
+}
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index 3ff6d35..9682b6e 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -20,15 +20,14 @@
#include <fstream>
#include <memory>
#include <ostream>
-#include <sstream>
-#include <string>
#include <vector>
+#include "androidfw/ResourceTypes.h"
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/CommandLineOptions.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
#include "idmap2/SysTrace.h"
using android::ApkAssets;
@@ -36,14 +35,15 @@
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
-using android::idmap2::PoliciesToBitmask;
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Unit;
using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::PoliciesToBitmaskResult;
using android::idmap2::utils::UidHasWriteAccessToPath;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
Result<Unit> Create(const std::vector<std::string>& args) {
SYSTRACE << "Create " << args;
std::string target_apk_path;
@@ -78,7 +78,7 @@
}
PolicyBitmask fulfilled_policies = 0;
- auto conv_result = PoliciesToBitmask(policies);
+ auto conv_result = PoliciesToBitmaskResult(policies);
if (conv_result) {
fulfilled_policies |= *conv_result;
} else {
@@ -86,7 +86,7 @@
}
if (fulfilled_policies == 0) {
- fulfilled_policies |= PolicyFlags::POLICY_PUBLIC;
+ fulfilled_policies |= PolicyFlags::PUBLIC;
}
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp
index 0b0541f..ba4ca62 100644
--- a/cmds/idmap2/idmap2/CreateMultiple.cpp
+++ b/cmds/idmap2/idmap2/CreateMultiple.cpp
@@ -20,8 +20,6 @@
#include <fstream>
#include <memory>
#include <ostream>
-#include <sstream>
-#include <string>
#include <vector>
#include "android-base/stringprintf.h"
@@ -30,7 +28,9 @@
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
#include "idmap2/SysTrace.h"
+#include "Commands.h"
using android::ApkAssets;
using android::base::StringPrintf;
@@ -38,13 +38,11 @@
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
-using android::idmap2::PoliciesToBitmask;
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Unit;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::PoliciesToBitmaskResult;
using android::idmap2::utils::UidHasWriteAccessToPath;
Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
@@ -80,7 +78,7 @@
}
PolicyBitmask fulfilled_policies = 0;
- auto conv_result = PoliciesToBitmask(policies);
+ auto conv_result = PoliciesToBitmaskResult(policies);
if (conv_result) {
fulfilled_policies |= *conv_result;
} else {
@@ -88,7 +86,7 @@
}
if (fulfilled_policies == 0) {
- fulfilled_policies |= PolicyFlags::POLICY_PUBLIC;
+ fulfilled_policies |= PolicyFlags::PUBLIC;
}
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
@@ -105,32 +103,34 @@
continue;
}
- const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- if (!overlay_apk) {
- LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str();
- continue;
- }
+ if (!Verify(std::vector<std::string>({"--idmap-path", idmap_path}))) {
+ const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str();
+ continue;
+ }
- const auto idmap =
- Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
- if (!idmap) {
- LOG(WARNING) << "failed to create idmap";
- continue;
- }
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
+ if (!idmap) {
+ LOG(WARNING) << "failed to create idmap";
+ continue;
+ }
- umask(kIdmapFilePermissionMask);
- std::ofstream fout(idmap_path);
- if (fout.fail()) {
- LOG(WARNING) << "failed to open idmap path " << idmap_path.c_str();
- continue;
- }
+ umask(kIdmapFilePermissionMask);
+ std::ofstream fout(idmap_path);
+ if (fout.fail()) {
+ LOG(WARNING) << "failed to open idmap path " << idmap_path.c_str();
+ continue;
+ }
- BinaryStreamVisitor visitor(fout);
- (*idmap)->accept(&visitor);
- fout.close();
- if (fout.fail()) {
- LOG(WARNING) << "failed to write to idmap path %s" << idmap_path.c_str();
- continue;
+ BinaryStreamVisitor visitor(fout);
+ (*idmap)->accept(&visitor);
+ fout.close();
+ if (fout.fail()) {
+ LOG(WARNING) << "failed to write to idmap path %s" << idmap_path.c_str();
+ continue;
+ }
}
idmap_paths.emplace_back(idmap_path);
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index b4fdd0b..da04532 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -20,7 +20,6 @@
#include <memory>
#include <ostream>
#include <set>
-#include <sstream>
#include <string>
#include <utility>
#include <vector>
@@ -34,25 +33,24 @@
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
#include "idmap2/XmlParser.h"
-#include "idmap2/ZipFile.h"
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
-using android::idmap2::kPolicyOdm;
-using android::idmap2::kPolicyOem;
-using android::idmap2::kPolicyProduct;
-using android::idmap2::kPolicyPublic;
-using android::idmap2::kPolicySystem;
-using android::idmap2::kPolicyVendor;
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Unit;
+using android::idmap2::policy::kPolicyOdm;
+using android::idmap2::policy::kPolicyOem;
+using android::idmap2::policy::kPolicyProduct;
+using android::idmap2::policy::kPolicyPublic;
+using android::idmap2::policy::kPolicySystem;
+using android::idmap2::policy::kPolicyVendor;
using android::idmap2::utils::ExtractOverlayManifestInfo;
using android::idmap2::utils::FindFiles;
using android::idmap2::utils::OverlayManifestInfo;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
namespace {
struct InputOverlay {
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 4aabf83..f84e4b5 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -33,7 +33,6 @@
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
#include "idmap2/SysTrace.h"
#include "utils/String8.h"
@@ -42,11 +41,12 @@
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::Idmap;
using android::idmap2::IdmapHeader;
-using android::idmap2::PolicyBitmask;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
using android::idmap2::utils::UidHasWriteAccessToPath;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
namespace {
Status ok() {
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
index cd474c0..f4cf651 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -20,14 +20,6 @@
* @hide
*/
interface IIdmap2 {
- const int POLICY_PUBLIC = 0x00000001;
- const int POLICY_SYSTEM_PARTITION = 0x00000002;
- const int POLICY_VENDOR_PARTITION = 0x00000004;
- const int POLICY_PRODUCT_PARTITION = 0x00000008;
- const int POLICY_SIGNATURE = 0x00000010;
- const int POLICY_ODM_PARTITION = 0x00000020;
- const int POLICY_OEM_PARTITION = 0x00000040;
-
@utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
boolean verifyIdmap(@utf8InCpp String overlayApkPath, int fulfilledPolicies,
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl b/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl
new file mode 100644
index 0000000..02b27a8
--- /dev/null
+++ b/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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 android.os;
+
+/**
+ * @see ResourcesTypes.h ResTable_overlayable_policy_header::PolicyFlags
+ * @hide
+ */
+interface OverlayablePolicy {
+ const int PUBLIC = 0x00000001;
+ const int SYSTEM_PARTITION = 0x00000002;
+ const int VENDOR_PARTITION = 0x00000004;
+ const int PRODUCT_PARTITION = 0x00000008;
+ const int SIGNATURE = 0x00000010;
+ const int ODM_PARTITION = 0x00000020;
+ const int OEM_PARTITION = 0x00000040;
+ const int ACTOR_SIGNATURE = 0x00000080;
+}
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index d4a0c322..2e4836e 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -73,7 +73,6 @@
#include "androidfw/ApkAssets.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
-#include "idmap2/Policies.h"
#include "idmap2/ResourceMapping.h"
namespace android::idmap2 {
diff --git a/cmds/idmap2/include/idmap2/PolicyUtils.h b/cmds/idmap2/include/idmap2/PolicyUtils.h
new file mode 100644
index 0000000..b95b8b4
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/PolicyUtils.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_POLICYUTILS_H_
+#define IDMAP2_INCLUDE_IDMAP2_POLICYUTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "androidfw/ResourceTypes.h"
+#include "idmap2/Policies.h"
+#include "idmap2/Result.h"
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
+namespace android::idmap2::utils {
+
+// Returns a Result object containing a policy flag bitmask built from a list of policy strings.
+// On error will contain a human readable message listing the invalid policies.
+Result<PolicyBitmask> PoliciesToBitmaskResult(const std::vector<std::string>& policies);
+
+// Converts a bitmask of policy flags into a list of their string representation as would be written
+// into XML
+std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask);
+
+} // namespace android::idmap2::utils
+
+#endif // IDMAP2_INCLUDE_IDMAP2_POLICYUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
index 86dfab2..5869409 100644
--- a/cmds/idmap2/include/idmap2/ResourceMapping.h
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -30,6 +30,8 @@
using android::idmap2::utils::OverlayManifestInfo;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
namespace android::idmap2 {
struct TargetValue {
diff --git a/cmds/idmap2/libidmap2/Policies.cpp b/cmds/idmap2/libidmap2/Policies.cpp
deleted file mode 100644
index 495fe61..0000000
--- a/cmds/idmap2/libidmap2/Policies.cpp
+++ /dev/null
@@ -1,88 +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.
- */
-
-#include "idmap2/Policies.h"
-
-#include <iterator>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "androidfw/ResourceTypes.h"
-#include "idmap2/Idmap.h"
-#include "idmap2/Result.h"
-
-namespace android::idmap2 {
-
-Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies) {
- static const std::unordered_map<android::StringPiece, PolicyFlags> kStringToFlag = {
- {kPolicyOdm, PolicyFlags::POLICY_ODM_PARTITION},
- {kPolicyOem, PolicyFlags::POLICY_OEM_PARTITION},
- {kPolicyPublic, PolicyFlags::POLICY_PUBLIC},
- {kPolicyProduct, PolicyFlags::POLICY_PRODUCT_PARTITION},
- {kPolicySignature, PolicyFlags::POLICY_SIGNATURE},
- {kPolicySystem, PolicyFlags::POLICY_SYSTEM_PARTITION},
- {kPolicyVendor, PolicyFlags::POLICY_VENDOR_PARTITION},
- };
-
- PolicyBitmask bitmask = 0;
- for (const std::string& policy : policies) {
- const auto iter = kStringToFlag.find(policy);
- if (iter != kStringToFlag.end()) {
- bitmask |= iter->second;
- } else {
- return Error("unknown policy \"%s\"", policy.c_str());
- }
- }
-
- return Result<PolicyBitmask>(bitmask);
-}
-
-std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask) {
- std::vector<std::string> policies;
-
- if ((bitmask & PolicyFlags::POLICY_ODM_PARTITION) != 0) {
- policies.emplace_back(kPolicyOdm);
- }
-
- if ((bitmask & PolicyFlags::POLICY_OEM_PARTITION) != 0) {
- policies.emplace_back(kPolicyOem);
- }
-
- if ((bitmask & PolicyFlags::POLICY_PUBLIC) != 0) {
- policies.emplace_back(kPolicyPublic);
- }
-
- if ((bitmask & PolicyFlags::POLICY_PRODUCT_PARTITION) != 0) {
- policies.emplace_back(kPolicyProduct);
- }
-
- if ((bitmask & PolicyFlags::POLICY_SIGNATURE) != 0) {
- policies.emplace_back(kPolicySignature);
- }
-
- if ((bitmask & PolicyFlags::POLICY_SYSTEM_PARTITION) != 0) {
- policies.emplace_back(kPolicySystem);
- }
-
- if ((bitmask & PolicyFlags::POLICY_VENDOR_PARTITION) != 0) {
- policies.emplace_back(kPolicyVendor);
- }
-
- return policies;
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/PolicyUtils.cpp b/cmds/idmap2/libidmap2/PolicyUtils.cpp
new file mode 100644
index 0000000..fc5182a
--- /dev/null
+++ b/cmds/idmap2/libidmap2/PolicyUtils.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#include "include/idmap2/PolicyUtils.h"
+
+#include <sstream>
+
+#include "android-base/strings.h"
+#include "idmap2/Policies.h"
+
+using android::idmap2::policy::kPolicyStringToFlag;
+
+namespace android::idmap2::utils {
+
+Result<PolicyBitmask> PoliciesToBitmaskResult(const std::vector<std::string>& policies) {
+ std::vector<std::string> unknown_policies;
+ PolicyBitmask bitmask = 0;
+ for (const std::string& policy : policies) {
+ const auto result = std::find_if(kPolicyStringToFlag.begin(), kPolicyStringToFlag.end(),
+ [policy](const auto& it) { return policy == it.first; });
+ if (result != kPolicyStringToFlag.end()) {
+ bitmask |= result->second;
+ } else {
+ unknown_policies.emplace_back(policy.empty() ? "empty" : policy);
+ }
+ }
+
+ if (unknown_policies.empty()) {
+ return Result<PolicyBitmask>(bitmask);
+ }
+
+ auto prefix = unknown_policies.size() == 1 ? "policy" : "policies";
+ return Error("unknown %s: \"%s\"", prefix, android::base::Join(unknown_policies, ",").c_str());
+}
+
+std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask) {
+ std::vector<std::string> policies;
+
+ for (const auto& policy : kPolicyStringToFlag) {
+ if ((bitmask & policy.second) != 0) {
+ policies.emplace_back(policy.first.to_string());
+ }
+ }
+
+ return policies;
+}
+
+} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 43cfec3..f82c8f1 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -24,11 +24,16 @@
#include <vector>
#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
+#include "idmap2/PolicyUtils.h"
#include "idmap2/ResourceUtils.h"
using android::base::StringPrintf;
+using android::idmap2::utils::BitmaskToPolicies;
using android::idmap2::utils::IsReference;
using android::idmap2::utils::ResToTypeEntryName;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
@@ -55,9 +60,8 @@
const PolicyBitmask& fulfilled_policies,
const ResourceId& target_resource) {
static constexpr const PolicyBitmask sDefaultPolicies =
- PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION |
- PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE;
+ PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION |
+ PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE;
// If the resource does not have an overlayable definition, allow the resource to be overlaid if
// the overlay is preinstalled or signed with the same signature as the target.
diff --git a/cmds/idmap2/include/idmap2/Policies.h b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
similarity index 60%
rename from cmds/idmap2/include/idmap2/Policies.h
rename to cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
index 87edd35..4973b76 100644
--- a/cmds/idmap2/include/idmap2/Policies.h
+++ b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
@@ -14,18 +14,22 @@
* limitations under the License.
*/
-#include <string>
-#include <vector>
-
-#include "Result.h"
-#include "androidfw/ResourceTypes.h"
-#include "androidfw/StringPiece.h"
-
#ifndef IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
#define IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
-namespace android::idmap2 {
+#include <array>
+#include <string>
+#include <vector>
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
+namespace android::idmap2::policy {
+
+constexpr const char* kPolicyActor = "actor";
constexpr const char* kPolicyOdm = "odm";
constexpr const char* kPolicyOem = "oem";
constexpr const char* kPolicyProduct = "product";
@@ -34,15 +38,16 @@
constexpr const char* kPolicySystem = "system";
constexpr const char* kPolicyVendor = "vendor";
-using PolicyFlags = ResTable_overlayable_policy_header::PolicyFlags;
-using PolicyBitmask = uint32_t;
-
-// Parses the string representations of policies into a bitmask.
-Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies);
-
-// Retrieves the string representations of policies in the bitmask.
-std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask);
-
-} // namespace android::idmap2
+inline static const std::array<std::pair<StringPiece, PolicyFlags>, 8> kPolicyStringToFlag = {
+ std::pair{kPolicyActor, PolicyFlags::ACTOR_SIGNATURE},
+ {kPolicyOdm, PolicyFlags::ODM_PARTITION},
+ {kPolicyOem, PolicyFlags::OEM_PARTITION},
+ {kPolicyProduct, PolicyFlags::PRODUCT_PARTITION},
+ {kPolicyPublic, PolicyFlags::PUBLIC},
+ {kPolicySignature, PolicyFlags::SIGNATURE},
+ {kPolicySystem, PolicyFlags::SYSTEM_PARTITION},
+ {kPolicyVendor, PolicyFlags::VENDOR_PARTITION},
+};
+} // namespace android::idmap2::policy
#endif // IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index b535f30..d896cf9 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -34,6 +34,7 @@
#include <string>
#include <vector>
+#include "R.h"
#include "TestHelpers.h"
#include "androidfw/PosixUtils.h"
#include "gmock/gmock.h"
@@ -127,10 +128,14 @@
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos);
- ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020000 string/str1"), std::string::npos);
- ASSERT_NE(result->stdout.find("0x7f02000e -> 0x7f020001 string/str3"), std::string::npos);
- ASSERT_NE(result->stdout.find("0x7f02000f -> 0x7f020002 string/str4"), std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1"),
+ std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000 string/str1"),
+ std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001 string/str3"),
+ std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002 string/str4"),
+ std::string::npos);
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -297,7 +302,7 @@
"lookup",
"--idmap-path", GetIdmapPath(),
"--config", "",
- "--resid", "0x7f02000c"}); // string/str1
+ "--resid", R::target::string::literal::str1});
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index a2c1560..87da36c 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -22,6 +22,8 @@
#include <utility>
#include <vector>
+#include "R.h"
+#include "TestConstants.h"
#include "TestHelpers.h"
#include "android-base/macros.h"
#include "androidfw/ApkAssets.h"
@@ -36,6 +38,8 @@
using ::testing::IsNull;
using ::testing::NotNull;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android::idmap2 {
#define ASSERT_TARGET_ENTRY(entry, target_resid, type, value) \
@@ -168,7 +172,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
@@ -177,8 +181,8 @@
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x03U);
- ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x76a20829);
- ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xc054fb26);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
}
@@ -220,7 +224,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
@@ -234,17 +238,21 @@
const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 4U);
- ASSERT_TARGET_ENTRY(target_entries[0], 0x7f010000, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f010000);
- ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020000);
- ASSERT_TARGET_ENTRY(target_entries[2], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020001);
- ASSERT_TARGET_ENTRY(target_entries[3], 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020002);
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
+ Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay::integer::int1);
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str1);
+ ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str3);
+ ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str4);
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 4U);
- ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f010000, 0x7f010000);
- ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x7f020000, 0x7f02000c);
- ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x7f020001, 0x7f02000e);
- ASSERT_OVERLAY_ENTRY(overlay_entries[3], 0x7f020002, 0x7f02000f);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay::integer::int1, R::target::integer::int1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], R::overlay::string::str1, R::target::string::str1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], R::overlay::string::str3, R::target::string::str3);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay::string::str4, R::target::string::str4);
}
TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
@@ -257,7 +265,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
@@ -271,17 +279,25 @@
const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 4U);
- ASSERT_TARGET_ENTRY(target_entries[0], 0x7f010000, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00010000);
- ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020000);
- ASSERT_TARGET_ENTRY(target_entries[2], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020001);
- ASSERT_TARGET_ENTRY(target_entries[3], 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020002);
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
+ Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay_shared::integer::int1);
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay_shared::string::str1);
+ ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay_shared::string::str3);
+ ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay_shared::string::str4);
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 4U);
- ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x00010000, 0x7f010000);
- ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x00020000, 0x7f02000c);
- ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x00020001, 0x7f02000e);
- ASSERT_OVERLAY_ENTRY(overlay_entries[3], 0x00020002, 0x7f02000f);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay_shared::integer::int1,
+ R::target::integer::int1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], R::overlay_shared::string::str1,
+ R::target::string::str1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], R::overlay_shared::string::str3,
+ R::target::string::str3);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay_shared::string::str4,
+ R::target::string::str4);
}
TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
@@ -290,7 +306,7 @@
info.target_name = "TestResources";
info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
@@ -298,14 +314,14 @@
const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 2U);
- ASSERT_TARGET_ENTRY(target_entries[0], 0x7f02000c, Res_value::TYPE_REFERENCE,
- 0x0104000a); // string/str1 -> android:string/ok
- ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE,
- 0x7f020001); // string/str3 -> string/str4
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::string::str1, Res_value::TYPE_REFERENCE,
+ 0x0104000a); // -> android:string/ok
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str3);
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(overlay_entries.size(), 1U);
- ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f020001, 0x7f02000e); // string/str3 <- string/str4
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay::string::str3, R::target::string::str3);
}
TEST(IdmapTests, CreateIdmapDataInlineResources) {
@@ -314,7 +330,7 @@
info.target_name = "TestResources";
info.resource_mapping = 0x7f030002; // xml/overlays_inline
auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
@@ -323,10 +339,10 @@
constexpr size_t overlay_string_pool_size = 8U;
const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 2U);
- ASSERT_TARGET_ENTRY(target_entries[0], 0x7f010000, Res_value::TYPE_INT_DEC,
- 73U); // integer/int1 -> 73
- ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_STRING,
- overlay_string_pool_size + 0U); // string/str1 -> "Hello World"
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, Res_value::TYPE_INT_DEC,
+ 73U); // -> 73
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_STRING,
+ overlay_string_pool_size + 0U); // -> "Hello World"
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(overlay_entries.size(), 0U);
@@ -346,7 +362,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ const auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_FALSE(result);
}
@@ -362,7 +378,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(result);
const auto idmap = std::move(*result);
diff --git a/cmds/idmap2/tests/PoliciesTests.cpp b/cmds/idmap2/tests/PoliciesTests.cpp
index eca7404..1b27759 100644
--- a/cmds/idmap2/tests/PoliciesTests.cpp
+++ b/cmds/idmap2/tests/PoliciesTests.cpp
@@ -17,76 +17,96 @@
#include <string>
#include "TestHelpers.h"
+#include "androidfw/ResourceTypes.h"
#include "gtest/gtest.h"
-#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
+using android::idmap2::utils::BitmaskToPolicies;
+using android::idmap2::utils::PoliciesToBitmaskResult;
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
-TEST(PoliciesTests, PoliciesToBitmasks) {
- const auto bitmask1 = PoliciesToBitmask({"system"});
+TEST(PoliciesTests, PoliciesToBitmaskResults) {
+ const auto bitmask1 = PoliciesToBitmaskResult({"system"});
ASSERT_TRUE(bitmask1);
- ASSERT_EQ(*bitmask1, PolicyFlags::POLICY_SYSTEM_PARTITION);
+ ASSERT_EQ(*bitmask1, PolicyFlags::SYSTEM_PARTITION);
- const auto bitmask2 = PoliciesToBitmask({"system", "vendor"});
+ const auto bitmask2 = PoliciesToBitmaskResult({"system", "vendor"});
ASSERT_TRUE(bitmask2);
- ASSERT_EQ(*bitmask2, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+ ASSERT_EQ(*bitmask2, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
- const auto bitmask3 = PoliciesToBitmask({"vendor", "system"});
+ const auto bitmask3 = PoliciesToBitmaskResult({"vendor", "system"});
ASSERT_TRUE(bitmask3);
- ASSERT_EQ(*bitmask3, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+ ASSERT_EQ(*bitmask3, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
- const auto bitmask4 = PoliciesToBitmask({"odm", "oem", "public", "product", "system", "vendor"});
+ const auto bitmask4 =
+ PoliciesToBitmaskResult({"odm", "oem", "public", "product", "system", "vendor"});
ASSERT_TRUE(bitmask4);
- ASSERT_EQ(*bitmask4, PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION |
- PolicyFlags::POLICY_VENDOR_PARTITION);
+ ASSERT_EQ(*bitmask4, PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION |
+ PolicyFlags::PUBLIC | PolicyFlags::PRODUCT_PARTITION |
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
- const auto bitmask5 = PoliciesToBitmask({"system", "system", "system"});
+ const auto bitmask5 = PoliciesToBitmaskResult({"system", "system", "system"});
ASSERT_TRUE(bitmask5);
- ASSERT_EQ(*bitmask5, PolicyFlags::POLICY_SYSTEM_PARTITION);
+ ASSERT_EQ(*bitmask5, PolicyFlags::SYSTEM_PARTITION);
- const auto bitmask6 = PoliciesToBitmask({""});
+ const auto bitmask6 = PoliciesToBitmaskResult({""});
ASSERT_FALSE(bitmask6);
- const auto bitmask7 = PoliciesToBitmask({"foo"});
+ const auto bitmask7 = PoliciesToBitmaskResult({"foo"});
ASSERT_FALSE(bitmask7);
- const auto bitmask8 = PoliciesToBitmask({"system", "foo"});
+ const auto bitmask8 = PoliciesToBitmaskResult({"system", "foo"});
ASSERT_FALSE(bitmask8);
- const auto bitmask9 = PoliciesToBitmask({"system", ""});
+ const auto bitmask9 = PoliciesToBitmaskResult({"system", ""});
ASSERT_FALSE(bitmask9);
- const auto bitmask10 = PoliciesToBitmask({"system "});
+ const auto bitmask10 = PoliciesToBitmaskResult({"system "});
ASSERT_FALSE(bitmask10);
+
+ const auto bitmask11 = PoliciesToBitmaskResult({"signature"});
+ ASSERT_TRUE(bitmask11);
+ ASSERT_EQ(*bitmask11, PolicyFlags::SIGNATURE);
+
+ const auto bitmask12 = PoliciesToBitmaskResult({"actor"});
+ ASSERT_TRUE(bitmask12);
+ ASSERT_EQ(*bitmask12, PolicyFlags::ACTOR_SIGNATURE);
}
TEST(PoliciesTests, BitmaskToPolicies) {
- const auto policies1 = BitmaskToPolicies(PolicyFlags::POLICY_PUBLIC);
+ const auto policies1 = BitmaskToPolicies(PolicyFlags::PUBLIC);
ASSERT_EQ(1, policies1.size());
ASSERT_EQ(policies1[0], "public");
- const auto policies2 = BitmaskToPolicies(PolicyFlags::POLICY_SYSTEM_PARTITION |
- PolicyFlags::POLICY_VENDOR_PARTITION);
+ const auto policies2 =
+ BitmaskToPolicies(PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
ASSERT_EQ(2, policies2.size());
ASSERT_EQ(policies2[0], "system");
ASSERT_EQ(policies2[1], "vendor");
- const auto policies3 = BitmaskToPolicies(
- PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+ const auto policies3 =
+ BitmaskToPolicies(PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION |
+ PolicyFlags::PUBLIC | PolicyFlags::PRODUCT_PARTITION |
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
ASSERT_EQ(2, policies2.size());
ASSERT_EQ(policies3[0], "odm");
ASSERT_EQ(policies3[1], "oem");
- ASSERT_EQ(policies3[2], "public");
- ASSERT_EQ(policies3[3], "product");
+ ASSERT_EQ(policies3[2], "product");
+ ASSERT_EQ(policies3[3], "public");
ASSERT_EQ(policies3[4], "system");
ASSERT_EQ(policies3[5], "vendor");
+
+ const auto policies4 = BitmaskToPolicies(PolicyFlags::SIGNATURE);
+ ASSERT_EQ(1, policies4.size());
+ ASSERT_EQ(policies4[0], "signature");
+
+ const auto policies5 = BitmaskToPolicies(PolicyFlags::ACTOR_SIGNATURE);
+ ASSERT_EQ(1, policies5.size());
+ ASSERT_EQ(policies5[0], "actor");
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index 1d34e42..9a10079 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -18,19 +18,22 @@
#include <sstream>
#include <string>
+#include "R.h"
#include "TestHelpers.h"
#include "androidfw/ApkAssets.h"
#include "androidfw/Idmap.h"
+#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
#include "idmap2/PrettyPrintVisitor.h"
using ::testing::NotNull;
using android::ApkAssets;
-using android::idmap2::PolicyBitmask;
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
@@ -43,7 +46,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
@@ -53,7 +56,8 @@
ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
- ASSERT_NE(stream.str().find("0x7f010000 -> 0x7f010000 integer/int1\n"), std::string::npos);
+ ASSERT_NE(stream.str().find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1\n"),
+ std::string::npos);
}
TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
diff --git a/cmds/idmap2/tests/R.h b/cmds/idmap2/tests/R.h
new file mode 100644
index 0000000..aed263a
--- /dev/null
+++ b/cmds/idmap2/tests/R.h
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#ifndef IDMAP2_TESTS_R_H
+#define IDMAP2_TESTS_R_H
+
+#include <idmap2/ResourceUtils.h>
+
+namespace android::idmap2 {
+
+static std::string hexify(ResourceId id) {
+ std::stringstream stream;
+ stream << std::hex << static_cast<uint32_t>(id);
+ return stream.str();
+}
+
+// clang-format off
+namespace R::target {
+ namespace integer {
+ constexpr ResourceId int1 = 0x7f010000;
+
+ namespace literal {
+ inline const std::string int1 = hexify(R::target::integer::int1);
+ }
+ }
+
+ namespace string {
+ constexpr ResourceId not_overlayable = 0x7f020003;
+ constexpr ResourceId other = 0x7f020004;
+ constexpr ResourceId policy_actor = 0x7f020005;
+ constexpr ResourceId policy_odm = 0x7f020006;
+ constexpr ResourceId policy_oem = 0x7f020007;
+ constexpr ResourceId policy_product = 0x7f020008;
+ constexpr ResourceId policy_public = 0x7f020009;
+ constexpr ResourceId policy_signature = 0x7f02000a;
+ constexpr ResourceId policy_system = 0x7f02000b;
+ constexpr ResourceId policy_system_vendor = 0x7f02000c;
+ constexpr ResourceId str1 = 0x7f02000d;
+ constexpr ResourceId str3 = 0x7f02000f;
+ constexpr ResourceId str4 = 0x7f020010;
+
+ namespace literal {
+ inline const std::string str1 = hexify(R::target::string::str1);
+ inline const std::string str3 = hexify(R::target::string::str3);
+ inline const std::string str4 = hexify(R::target::string::str4);
+ }
+ }
+}
+
+namespace R::overlay {
+ namespace integer {
+ constexpr ResourceId int1 = 0x7f010000;
+ }
+ namespace string {
+ constexpr ResourceId str1 = 0x7f020000;
+ constexpr ResourceId str3 = 0x7f020001;
+ constexpr ResourceId str4 = 0x7f020002;
+ }
+}
+
+namespace R::overlay_shared {
+ namespace integer {
+ constexpr ResourceId int1 = 0x00010000;
+ }
+ namespace string {
+ constexpr ResourceId str1 = 0x00020000;
+ constexpr ResourceId str3 = 0x00020001;
+ constexpr ResourceId str4 = 0x00020002;
+ }
+}
+
+namespace R::system_overlay::string {
+ constexpr ResourceId policy_public = 0x7f010000;
+ constexpr ResourceId policy_system = 0x7f010001;
+ constexpr ResourceId policy_system_vendor = 0x7f010002;
+}
+
+namespace R::system_overlay_invalid::string {
+ constexpr ResourceId not_overlayable = 0x7f010000;
+ constexpr ResourceId other = 0x7f010001;
+ constexpr ResourceId policy_actor = 0x7f010002;
+ constexpr ResourceId policy_odm = 0x7f010003;
+ constexpr ResourceId policy_oem = 0x7f010004;
+ constexpr ResourceId policy_product = 0x7f010005;
+ constexpr ResourceId policy_public = 0x7f010006;
+ constexpr ResourceId policy_signature = 0x7f010007;
+ constexpr ResourceId policy_system = 0x7f010008;
+ constexpr ResourceId policy_system_vendor = 0x7f010009;
+};
+// clang-format on
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_TESTS_R_H
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index b22fdaf..5c5c81e 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -20,14 +20,20 @@
#include <sstream>
#include <string>
+#include "TestConstants.h"
#include "TestHelpers.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/Idmap.h"
#include "idmap2/RawPrintVisitor.h"
+using android::base::StringPrintf;
using ::testing::NotNull;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android::idmap2 {
#define ASSERT_CONTAINS_REGEX(pattern, str) \
@@ -48,7 +54,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
@@ -59,8 +65,12 @@
#define ADDRESS "[0-9a-f]{8}: "
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 version\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "76a20829 target crc\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "c054fb26 overlay crc\n", stream.str());
+ ASSERT_CONTAINS_REGEX(
+ StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
+ stream.str());
+ ASSERT_CONTAINS_REGEX(
+ StringPrintf(ADDRESS "%s overlay crc\n", android::idmap2::TestConstants::OVERLAY_CRC_STRING),
+ stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count\n", stream.str());
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index 39c4937..de039f4 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -22,7 +22,9 @@
#include <utility>
#include <vector>
+#include "R.h"
#include "TestHelpers.h"
+#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/LogInfo.h"
@@ -31,6 +33,8 @@
using android::Res_value;
using android::idmap2::utils::ExtractOverlayManifestInfo;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android::idmap2 {
#define ASSERT_RESULT(r) \
@@ -106,20 +110,20 @@
info.target_name = "TestResources";
info.resource_mapping = 0U; // no xml
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
- ASSERT_RESULT(MappingExists(res, 0x7f010000, Res_value::TYPE_REFERENCE, 0x7f010000,
- false /* rewrite */)); // integer/int1
- ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_REFERENCE, 0x7f020000,
- false /* rewrite */)); // string/str1
- ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_REFERENCE, 0x7f020001,
- false /* rewrite */)); // string/str3
- ASSERT_RESULT(MappingExists(res, 0x7f02000f, Res_value::TYPE_REFERENCE, 0x7f020002,
- false /* rewrite */)); // string/str4
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE,
+ R::overlay::integer::int1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str3, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str4, false /* rewrite */));
}
TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
@@ -128,18 +132,18 @@
info.target_name = "TestResources";
info.resource_mapping = 0x7f030003; // xml/overlays_swap
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
- ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020002,
- true /* rewrite */)); // string/str1 -> string/str4
- ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020000,
- true /* rewrite */)); // string/str3 -> string/str1
- ASSERT_RESULT(MappingExists(res, 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020001,
- true /* rewrite */)); // string/str4 -> string/str3
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str4, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str1, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str3, true /* rewrite */));
}
TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
@@ -148,17 +152,17 @@
info.target_name = "TestResources";
info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U);
- ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_REFERENCE, 0x0104000a,
- false /* rewrite */)); // string/str1 -> android:string/ok
- ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020001,
- true /* rewrite */)); // string/str3 -> string/str4
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x0104000a,
+ false /* rewrite */)); // -> android:string/ok
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ 0x7f020001, true /* rewrite */));
}
TEST(ResourceMappingTests, InlineResources) {
@@ -167,7 +171,7 @@
info.target_name = "TestResources";
info.resource_mapping = 0x7f030002; // xml/overlays_inline
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
constexpr size_t overlay_string_pool_size = 8U;
@@ -175,108 +179,120 @@
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
- ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_STRING,
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_STRING,
overlay_string_pool_size + 0U,
- false /* rewrite */)); // string/str1 -> "Hello World"
- ASSERT_RESULT(MappingExists(res, 0x7f010000, Res_value::TYPE_INT_DEC, 73U,
- false /* rewrite */)); // string/str1 -> "Hello World"
+ false /* rewrite */)); // -> "Hello World"
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U,
+ false /* rewrite */)); // -> 73
}
TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
auto resources =
TestGetResourceMapping("/target/target.apk", "/system-overlay/system-overlay.apk",
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
- ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010000,
- false /* rewrite */)); // string/policy_public
- ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010001,
- false /* rewrite */)); // string/policy_system
- ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010002,
- false /* rewrite */)); // string/policy_system_vendor
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay::string::policy_public, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay::string::policy_system, false /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay::string::policy_system_vendor, false /* rewrite */));
}
// Resources that are not declared as overlayable and resources that a protected by policies the
// overlay does not fulfill must not map to overlay resources.
TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
- auto resources = TestGetResourceMapping(
- "/target/target.apk", "/system-overlay-invalid/system-overlay-invalid.apk",
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true);
+ auto resources = TestGetResourceMapping("/target/target.apk",
+ "/system-overlay-invalid/system-overlay-invalid.apk",
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
- ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010005,
- false /* rewrite */)); // string/policy_public
- ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010007,
- false /* rewrite */)); // string/policy_system
- ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010008,
- false /* rewrite */)); // string/policy_system_vendor
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
}
// Resources that are not declared as overlayable and resources that a protected by policies the
// overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned
// off.
TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
- auto resources = TestGetResourceMapping(
- "/target/target.apk", "/system-overlay-invalid/system-overlay-invalid.apk",
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ false);
+ auto resources = TestGetResourceMapping("/target/target.apk",
+ "/system-overlay-invalid/system-overlay-invalid.apk",
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
- ASSERT_EQ(res.GetTargetToOverlayMap().size(), 9U);
- ASSERT_RESULT(MappingExists(res, 0x7f020003, Res_value::TYPE_REFERENCE, 0x7f010000,
- false /* rewrite */)); // string/not_overlayable
- ASSERT_RESULT(MappingExists(res, 0x7f020004, Res_value::TYPE_REFERENCE, 0x7f010001,
- false /* rewrite */)); // string/other
- ASSERT_RESULT(MappingExists(res, 0x7f020005, Res_value::TYPE_REFERENCE, 0x7f010002,
- false /* rewrite */)); // string/policy_odm
- ASSERT_RESULT(MappingExists(res, 0x7f020006, Res_value::TYPE_REFERENCE, 0x7f010003,
- false /* rewrite */)); // string/policy_oem
- ASSERT_RESULT(MappingExists(res, 0x7f020007, Res_value::TYPE_REFERENCE, 0x7f010004,
- false /* rewrite */)); // string/policy_product
- ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010005,
- false /* rewrite */)); // string/policy_public
- ASSERT_RESULT(MappingExists(res, 0x7f020009, Res_value::TYPE_REFERENCE, 0x7f010006,
- false /* rewrite */)); // string/policy_signature
- ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010007,
- false /* rewrite */)); // string/policy_system
- ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010008,
- false /* rewrite */)); // string/policy_system_vendor
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 10U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::not_overlayable,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::other, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_actor,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_odm, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_oem, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_product,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_signature,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
}
// Overlays that do not target an <overlayable> tag can overlay resources defined within any
// <overlayable> tag.
TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-no-name.apk",
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
- ASSERT_RESULT(MappingExists(res, 0x7f010000, Res_value::TYPE_REFERENCE, 0x7f010000,
- false /* rewrite */)); // integer/int1
- ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_REFERENCE, 0x7f020000,
- false /* rewrite */)); // string/str1
- ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_REFERENCE, 0x7f020001,
- false /* rewrite */)); // string/str3
- ASSERT_RESULT(MappingExists(res, 0x7f02000f, Res_value::TYPE_REFERENCE, 0x7f020002,
- false /* rewrite */)); // string/str4
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE,
+ R::overlay::integer::int1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str3, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str4, false /* rewrite */));
}
// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
// overlay packages that have not defined overlayable resources.
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
- auto resources =
- TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay-no-name.apk",
- PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true);
+ auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
+ "/overlay/overlay-no-name.apk", PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
@@ -293,33 +309,44 @@
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
- ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 9U);
- ASSERT_RESULT(MappingExists(res, 0x7f020003, Res_value::TYPE_REFERENCE, 0x7f010000,
- false /* rewrite */)); // string/not_overlayable
- ASSERT_RESULT(MappingExists(res, 0x7f020004, Res_value::TYPE_REFERENCE, 0x7f010001,
- false /* rewrite */)); // string/other
- ASSERT_RESULT(MappingExists(res, 0x7f020005, Res_value::TYPE_REFERENCE, 0x7f010002,
- false /* rewrite */)); // string/policy_odm
- ASSERT_RESULT(MappingExists(res, 0x7f020006, Res_value::TYPE_REFERENCE, 0x7f010003,
- false /* rewrite */)); // string/policy_oem
- ASSERT_RESULT(MappingExists(res, 0x7f020007, Res_value::TYPE_REFERENCE, 0x7f010004,
- false /* rewrite */)); // string/policy_product
- ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010005,
- false /* rewrite */)); // string/policy_public
- ASSERT_RESULT(MappingExists(res, 0x7f020009, Res_value::TYPE_REFERENCE, 0x7f010006,
- false /* rewrite */)); // string/policy_signature
- ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010007,
- false /* rewrite */)); // string/policy_system
- ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010008,
- false /* rewrite */)); // string/policy_system_vendor
+ ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 10U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::not_overlayable,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::other, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_actor,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_odm,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_oem,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_product,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_signature,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(
+ res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
};
- CheckEntries(PolicyFlags::POLICY_SIGNATURE);
- CheckEntries(PolicyFlags::POLICY_PRODUCT_PARTITION);
- CheckEntries(PolicyFlags::POLICY_SYSTEM_PARTITION);
- CheckEntries(PolicyFlags::POLICY_VENDOR_PARTITION);
- CheckEntries(PolicyFlags::POLICY_ODM_PARTITION);
- CheckEntries(PolicyFlags::POLICY_OEM_PARTITION);
+ CheckEntries(PolicyFlags::SIGNATURE);
+ CheckEntries(PolicyFlags::PRODUCT_PARTITION);
+ CheckEntries(PolicyFlags::SYSTEM_PARTITION);
+ CheckEntries(PolicyFlags::VENDOR_PARTITION);
+ CheckEntries(PolicyFlags::ODM_PARTITION);
+ CheckEntries(PolicyFlags::OEM_PARTITION);
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/TestConstants.h b/cmds/idmap2/tests/TestConstants.h
new file mode 100644
index 0000000..6bc924e
--- /dev/null
+++ b/cmds/idmap2/tests/TestConstants.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef IDMAP2_TESTS_TESTCONSTANTS_H
+#define IDMAP2_TESTS_TESTCONSTANTS_H
+
+namespace android::idmap2::TestConstants {
+
+constexpr const auto TARGET_CRC = 0x41c60c8c;
+constexpr const auto TARGET_CRC_STRING = "41c60c8c";
+
+constexpr const auto OVERLAY_CRC = 0xc054fb26;
+constexpr const auto OVERLAY_CRC_STRING = "c054fb26";
+
+} // namespace android::idmap2::TestConstants
+
+#endif // IDMAP2_TESTS_TESTCONSTANTS_H
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
index 9ebfae4..7119d82 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
@@ -25,6 +25,7 @@
<string name="policy_signature">policy_signature</string>
<string name="policy_odm">policy_odm</string>
<string name="policy_oem">policy_oem</string>
+ <string name="policy_actor">policy_actor</string>
<!-- Requests to overlay a resource that is not declared as overlayable. -->
<string name="not_overlayable">not_overlayable</string>
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
index 1456e74..bd99098 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
index 8389f56..ad4cd48 100644
--- a/cmds/idmap2/tests/data/target/res/values/overlayable.xml
+++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
@@ -41,6 +41,10 @@
<item type="string" name="policy_oem" />
</policy>
+ <policy type="actor">
+ <item type="string" name="policy_actor" />
+ </policy>
+
<!-- Resources publicly overlayable -->
<policy type="public">
<item type="string" name="policy_public" />
@@ -63,4 +67,4 @@
<item type="string" name="other" />
</policy>
</overlayable>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml
index a892c98..5230e25 100644
--- a/cmds/idmap2/tests/data/target/res/values/values.xml
+++ b/cmds/idmap2/tests/data/target/res/values/values.xml
@@ -36,6 +36,7 @@
<string name="policy_signature">policy_signature</string>
<string name="policy_system">policy_system</string>
<string name="policy_system_vendor">policy_system_vendor</string>
+ <string name="policy_actor">policy_actor</string>
<string name="other">other</string>
</resources>
diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
index 2eb7c47..58504a7 100644
--- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk
+++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
index 251cf46..c80e5eb 100644
--- a/cmds/idmap2/tests/data/target/target.apk
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index 77a56e5..9439e1d 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -237,33 +237,38 @@
Reader::Reader(const int fd)
{
mFile = fdopen(fd, "r");
+ mBuffer = new char[1024];
mStatus = mFile == nullptr ? "Invalid fd " + std::to_string(fd) : "";
}
Reader::~Reader()
{
if (mFile != nullptr) fclose(mFile);
+ free(mBuffer);
}
bool Reader::readLine(std::string* line) {
if (mFile == nullptr) return false;
- char* buf = nullptr;
size_t len = 0;
- ssize_t read = getline(&buf, &len, mFile);
+ ssize_t read = getline(&mBuffer, &len, mFile);
if (read != -1) {
- std::string s(buf);
+ std::string s(mBuffer);
line->assign(trim(s, DEFAULT_NEWLINE));
- } else if (errno == EINVAL) {
- mStatus = "Bad Argument";
+ return true;
}
- free(buf);
- return read != -1;
+ if (!feof(mFile)) {
+ mStatus = "Error reading file. Ferror: " + std::to_string(ferror(mFile));
+ }
+ return false;
}
bool Reader::ok(std::string* error) {
+ if (mStatus.empty()) {
+ return true;
+ }
error->assign(mStatus);
- return mStatus.empty();
+ return false;
}
// ==============================================================================
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index 09dc8e6..5812c60 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -117,6 +117,7 @@
private:
FILE* mFile;
+ char* mBuffer;
std::string mStatus;
};
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 2575542..5535b66 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -393,6 +393,9 @@
WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"];
AppFreezeChanged app_freeze_changed = 254 [(module) = "framework"];
SnapshotMergeReported snapshot_merge_reported = 255;
+ ForegroundServiceAppOpSessionEnded foreground_service_app_op_session_ended =
+ 256 [(module) = "framework"];
+ DisplayJankReported display_jank_reported = 257;
SdkExtensionStatus sdk_extension_status = 354;
}
@@ -978,6 +981,17 @@
// The job id (as assigned by the app).
optional int32 job_id = 6;
+
+ // One flag for each of the API constraints defined by Jobscheduler. Does not include implcit
+ // constraints as they are always assumed to be set.
+ optional bool has_charging_constraint = 7;
+ optional bool has_battery_not_low_constraint = 8;
+ optional bool has_storage_not_low_constraint = 9;
+ optional bool has_timing_delay_constraint = 10;
+ optional bool has_deadline_constraint = 11;
+ optional bool has_idle_constraint = 12;
+ optional bool has_connectivity_constraint = 13;
+ optional bool has_content_trigger_constraint = 14;
}
/**
@@ -3285,12 +3299,12 @@
];
}
-/*
+/**
* Logs foreground service starts and stops.
* Note that this is not when a service starts or stops, but when it is
* considered foreground.
* Logged from
- * //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
+ * frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
*/
message ForegroundServiceStateChanged {
optional int32 uid = 1 [(is_uid) = true];
@@ -3302,6 +3316,49 @@
EXIT = 2;
}
optional State state = 3;
+
+ // Whether the fgs is allowed while-in-use permissions, i.e. is considered 'in-use' to the user.
+ // (If the fgs was started while the app wasn't TOP it usually will be denied these permissions)
+ optional bool allow_while_in_use_permission = 4;
+}
+
+/**
+ * Logs the number of times a uid accesses a sensitive AppOp during a foreground service session.
+ * A foreground service session is any continuous period during which the uid holds at least one
+ * foreground service; the atom will be pushed when the uid no longer holds any foreground services.
+ * Accesses initiated while the uid is in the TOP state are ignored.
+ * Sessions with no attempted accesses are not logged.
+ * Logged from
+ * frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
+ */
+message ForegroundServiceAppOpSessionEnded {
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The operation's name.
+ // To the extent possible, preserve the mapping from AppOpsManager.OP_ constants.
+ // Only these named ops are actually logged.
+ enum AppOpName {
+ OP_NONE = -1; // Also represents UNKNOWN.
+ OP_COARSE_LOCATION = 0;
+ OP_FINE_LOCATION = 1;
+ OP_CAMERA = 26;
+ OP_RECORD_AUDIO = 27;
+ }
+ optional AppOpName app_op_name = 2 [default = OP_NONE];
+
+ // The uid's permission mode for accessing the AppOp during this fgs session.
+ enum Mode {
+ MODE_UNKNOWN = 0;
+ MODE_ALLOWED = 1; // Always allowed
+ MODE_IGNORED = 2; // Denied
+ MODE_FOREGROUND = 3; // Allow-while-in-use (or allowed-one-time)
+ }
+ optional Mode app_op_mode = 3;
+
+ // Number of times this AppOp was requested and allowed.
+ optional int32 count_ops_accepted = 4;
+ // Number of times this AppOp was requested but denied.
+ optional int32 count_ops_rejected = 5;
}
/**
@@ -8183,13 +8240,29 @@
}
/**
+ * Janky event as reported by SurfaceFlinger.
+ * This event is intended to be consumed by a Perfetto subscriber for
+ * automated trace collection.
+ *
+ * Logged from:
+ * frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
+ */
+message DisplayJankReported {
+ // Informational field for how long the janky event lasted in milliseconds
+ optional int64 event_duration_millis = 1;
+ // Number of frame deadlines missed, where SurfaceFlinger failed to update
+ // the display on time.
+ optional int32 present_deadlines_missed = 2;
+}
+
+/**
* Information about camera facing and API level usage.
* Logged from:
* frameworks/base/services/core/java/com/android/server/camera/CameraServiceProxy.java
*/
message CameraActionEvent {
// Camera session duration
- optional int64 duration = 1;
+ optional int64 duration_millis = 1;
// Camera API level used
optional int32 api_level = 2;
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 0edf40b..933f48d 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -66,10 +66,8 @@
{
lock_guard<mutex> lk(*cv_mutex);
for (const StatsEventParcel& parcel: output) {
- uint8_t* buf = reinterpret_cast<uint8_t*>(
- const_cast<int8_t*>(parcel.buffer.data()));
- shared_ptr<LogEvent> event = make_shared<LogEvent>(
- buf, parcel.buffer.size(), /*uid=*/-1, /*pid=*/-1);
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(/*uid=*/-1, /*pid=*/-1);
+ event->parseBuffer((uint8_t*)parcel.buffer.data(), parcel.buffer.size());
sharedData->push_back(event);
}
*pullSuccess = success;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 3e46d13..974e203 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -65,18 +65,6 @@
#define ATTRIBUTION_CHAIN_TYPE 0x09
#define ERROR_TYPE 0x0F
-// Msg is expected to begin at the start of the serialized atom -- it should not
-// include the android_log_header_t or the StatsEventTag.
-LogEvent::LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid)
- : mBuf(msg),
- mRemainingLen(len),
- mLogdTimestampNs(time(nullptr)),
- mLogUid(uid),
- mLogPid(pid)
-{
- initNew();
-}
-
LogEvent::LogEvent(const LogEvent& event) {
mTagId = event.mTagId;
mLogUid = event.mLogUid;
@@ -86,6 +74,12 @@
mValues = event.mValues;
}
+LogEvent::LogEvent(int32_t uid, int32_t pid)
+ : mLogdTimestampNs(time(nullptr)),
+ mLogUid(uid),
+ mLogPid(pid) {
+}
+
LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
mLogdTimestampNs = wallClockTimestampNs;
mElapsedTimestampNs = elapsedTimestampNs;
@@ -189,9 +183,6 @@
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status)));
}
-LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, timestampNs) {
-}
-
LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) {
mLogdTimestampNs = timestampNs;
mTagId = tagId;
@@ -467,7 +458,10 @@
// This parsing logic is tied to the encoding scheme used in StatsEvent.java and
// stats_event.c
-void LogEvent::initNew() {
+bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
+ mBuf = buf;
+ mRemainingLen = (uint32_t)len;
+
int32_t pos[] = {1, 1, 1};
bool last[] = {false, false, false};
@@ -529,6 +523,7 @@
if (mRemainingLen != 0) mValid = false;
mBuf = nullptr;
+ return mValid;
}
uint8_t LogEvent::getTypeId(uint8_t typeInfo) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index e167e67..3940aa8 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -20,7 +20,6 @@
#include <android/util/ProtoOutputStream.h>
#include <private/android_logger.h>
-#include <stats_event.h>
#include <string>
#include <vector>
@@ -61,14 +60,32 @@
};
/**
- * Wrapper for the log_msg structure.
+ * This class decodes the structured, serialized encoding of an atom into a
+ * vector of FieldValues.
*/
class LogEvent {
public:
/**
- * Read a LogEvent from the socket
+ * \param uid user id of the logging caller
+ * \param pid process id of the logging caller
*/
- explicit LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid);
+ explicit LogEvent(int32_t uid, int32_t pid);
+
+ /**
+ * Parses the atomId, timestamp, and vector of values from a buffer
+ * containing the StatsEvent/AStatsEvent encoding of an atom.
+ *
+ * \param buf a buffer that begins at the start of the serialized atom (it
+ * should not include the android_log_header_t or the StatsEventTag)
+ * \param len size of the buffer
+ *
+ * \return success of the initialization
+ */
+ bool parseBuffer(uint8_t* buf, size_t len);
+
+ // TODO(b/149590301): delete unused functions below once LogEvent uses the
+ // new socket schema within test code. Really we would like the only entry
+ // points into LogEvent to be the above constructor and parseBuffer functions.
/**
* Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
@@ -76,9 +93,6 @@
explicit LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs);
// For testing. The timestamp is used as both elapsed real time and logd timestamp.
- explicit LogEvent(int32_t tagId, int64_t timestampNs);
-
- // For testing. The timestamp is used as both elapsed real time and logd timestamp.
explicit LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid);
/**
@@ -192,10 +206,6 @@
return &mValues;
}
- bool isValid() {
- return mValid;
- }
-
inline LogEvent makeCopy() {
return LogEvent(*this);
}
@@ -222,12 +232,6 @@
*/
LogEvent(const LogEvent&);
-
- /**
- * Parsing function for new encoding scheme.
- */
- void initNew();
-
void parseInt32(int32_t* pos, int32_t depth, bool* last);
void parseInt64(int32_t* pos, int32_t depth, bool* last);
void parseString(int32_t* pos, int32_t depth, bool* last);
@@ -238,13 +242,14 @@
void parseAttributionChain(int32_t* pos, int32_t depth, bool* last);
/**
- * mBuf is a pointer to the current location in the buffer being parsed.
- * Because the buffer lives on the StatsSocketListener stack, this pointer
- * is only valid during the LogEvent constructor. It will be set to null at
- * the end of initNew.
+ * The below three variables are only valid during the execution of
+ * parseBuffer. There are no guarantees about the state of these variables
+ * before/after.
+ *
+ * TODO (b/150312423): These shouldn't be member variables. We should pass
+ * them around as parameters.
*/
uint8_t* mBuf;
-
uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed
bool mValid = true; // stores whether the event we received from the socket is valid
diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp
index 8f0f480..b877cc9 100755
--- a/cmds/statsd/src/socket/StatsSocketListener.cpp
+++ b/cmds/statsd/src/socket/StatsSocketListener.cpp
@@ -126,7 +126,10 @@
uint32_t pid = cred->pid;
int64_t oldestTimestamp;
- if (!mQueue->push(std::make_unique<LogEvent>(msg, len, uid, pid), &oldestTimestamp)) {
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(uid, pid);
+ logEvent->parseBuffer(msg, len);
+
+ if (!mQueue->push(std::move(logEvent), &oldestTimestamp)) {
StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp);
}
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 7542faf..7458cbf 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -54,8 +54,9 @@
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
- EXPECT_TRUE(logEvent.isValid());
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
EXPECT_EQ(100, logEvent.GetTagId());
EXPECT_EQ(1000, logEvent.GetUid());
EXPECT_EQ(1001, logEvent.GetPid());
@@ -102,8 +103,9 @@
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
- EXPECT_TRUE(logEvent.isValid());
+ LogEvent logEvent(/*uid=*/ 1000, /*pid=*/ 1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
EXPECT_EQ(100, logEvent.GetTagId());
EXPECT_EQ(1000, logEvent.GetUid());
EXPECT_EQ(1001, logEvent.GetPid());
@@ -137,8 +139,9 @@
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
- EXPECT_TRUE(logEvent.isValid());
+ LogEvent logEvent(/*uid=*/ 1000, /*pid=*/ 1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
EXPECT_EQ(100, logEvent.GetTagId());
EXPECT_EQ(1000, logEvent.GetUid());
EXPECT_EQ(1001, logEvent.GetPid());
@@ -165,8 +168,9 @@
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
- EXPECT_TRUE(logEvent.isValid());
+ LogEvent logEvent(/*uid=*/ 1000, /*pid=*/ 1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
EXPECT_EQ(100, logEvent.GetTagId());
EXPECT_EQ(1000, logEvent.GetUid());
EXPECT_EQ(1001, logEvent.GetPid());
@@ -200,8 +204,9 @@
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
- EXPECT_TRUE(logEvent.isValid());
+ LogEvent logEvent(/*uid=*/ 1000, /*pid=*/ 1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
EXPECT_EQ(100, logEvent.GetTagId());
EXPECT_EQ(1000, logEvent.GetUid());
EXPECT_EQ(1001, logEvent.GetPid());
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 36094b2..8701e17 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "src/condition/SimpleConditionTracker.h"
+#include "stats_event.h"
#include "tests/statsd_test_util.h"
#include <gmock/gmock.h>
@@ -31,6 +32,8 @@
namespace os {
namespace statsd {
+namespace {
+
const ConfigKey kConfigKey(0, 12345);
const int ATTRIBUTION_NODE_FIELD_ID = 1;
@@ -57,24 +60,33 @@
return simplePredicate;
}
-void writeAttributionNodesToEvent(LogEvent* event, const std::vector<int> &uids) {
- std::vector<AttributionNodeInternal> nodes;
- for (size_t i = 0; i < uids.size(); ++i) {
- AttributionNodeInternal node;
- node.set_uid(uids[i]);
- nodes.push_back(node);
+void makeWakeLockEvent(LogEvent* logEvent, uint32_t atomId, uint64_t timestamp,
+ const vector<int>& uids, const string& wl, int acquire) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+ vector<std::string> tags(uids.size()); // vector of empty strings
+ vector<const char*> cTags(uids.size());
+ for (int i = 0; i < cTags.size(); i++) {
+ cTags[i] = tags[i].c_str();
}
- event->write(nodes); // attribution chain.
+ AStatsEvent_writeAttributionChain(statsEvent, reinterpret_cast<const uint32_t*>(uids.data()),
+ cTags.data(), uids.size());
+
+ AStatsEvent_writeString(statsEvent, wl.c_str());
+ AStatsEvent_writeInt32(statsEvent, acquire);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ logEvent->parseBuffer(buf, size);
+
+ AStatsEvent_release(statsEvent);
}
-// TODO(b/149590301): Update this helper to use new socket schema.
-//void makeWakeLockEvent(
-// LogEvent* event, const std::vector<int> &uids, const string& wl, int acquire) {
-// writeAttributionNodesToEvent(event, uids);
-// event->write(wl);
-// event->write(acquire);
-// event->init();
-//}
+} // anonymous namespace
+
std::map<int64_t, HashableDimensionKey> getWakeLockQueryKey(
const Position position,
@@ -265,138 +277,128 @@
EXPECT_TRUE(changedCache[0]);
}
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
-// std::vector<sp<ConditionTracker>> allConditions;
-// for (Position position :
-// { Position::FIRST, Position::LAST}) {
-//
-// SimplePredicate simplePredicate = getWakeLockHeldCondition(
-// true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
-// position);
-// string conditionName = "WL_HELD_BY_UID2";
-//
-// unordered_map<int64_t, int> trackerNameIndexMap;
-// trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
-// trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
-// trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
-//
-// SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
-// 0 /*condition tracker index*/, simplePredicate,
-// trackerNameIndexMap);
-//
-// std::vector<int> uids = {111, 222, 333};
-//
-// LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
-// makeWakeLockEvent(&event, uids, "wl1", 1);
-//
-// // one matched start
-// vector<MatchingState> matcherState;
-// matcherState.push_back(MatchingState::kMatched);
-// matcherState.push_back(MatchingState::kNotMatched);
-// matcherState.push_back(MatchingState::kNotMatched);
-// vector<sp<ConditionTracker>> allPredicates;
-// vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
-// vector<bool> changedCache(1, false);
-//
-// conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
-// changedCache);
-//
-// if (position == Position::FIRST ||
-// position == Position::LAST) {
-// EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-// } else {
-// EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
-// }
-// EXPECT_TRUE(changedCache[0]);
-// if (position == Position::FIRST ||
-// position == Position::LAST) {
-// EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u);
-// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-// } else {
-// EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), uids.size());
-// }
-//
-// // Now test query
-// const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
-// conditionCache[0] = ConditionState::kNotEvaluated;
-//
-// conditionTracker.isConditionMet(queryKey, allPredicates,
-// false,
-// conditionCache);
-// EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
-//
-// // another wake lock acquired by this uid
-// LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
-// makeWakeLockEvent(&event2, uids, "wl2", 1);
-// matcherState.clear();
-// matcherState.push_back(MatchingState::kMatched);
-// matcherState.push_back(MatchingState::kNotMatched);
-// conditionCache[0] = ConditionState::kNotEvaluated;
-// changedCache[0] = false;
-// conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
-// changedCache);
-// EXPECT_FALSE(changedCache[0]);
-// if (position == Position::FIRST ||
-// position == Position::LAST) {
-// EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-// } else {
-// EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
-// }
-// EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
-// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-//
-//
-// // wake lock 1 release
-// LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
-// makeWakeLockEvent(&event3, uids, "wl1", 0); // now release it.
-// matcherState.clear();
-// matcherState.push_back(MatchingState::kNotMatched);
-// matcherState.push_back(MatchingState::kMatched);
-// conditionCache[0] = ConditionState::kNotEvaluated;
-// changedCache[0] = false;
-// conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
-// changedCache);
-// // nothing changes, because wake lock 2 is still held for this uid
-// EXPECT_FALSE(changedCache[0]);
-// if (position == Position::FIRST ||
-// position == Position::LAST) {
-// EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-// } else {
-// EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
-// }
-// EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
-// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-//
-// LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
-// makeWakeLockEvent(&event4, uids, "wl2", 0); // now release it.
-// matcherState.clear();
-// matcherState.push_back(MatchingState::kNotMatched);
-// matcherState.push_back(MatchingState::kMatched);
-// conditionCache[0] = ConditionState::kNotEvaluated;
-// changedCache[0] = false;
-// conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
-// changedCache);
-// EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
-// EXPECT_TRUE(changedCache[0]);
-// if (position == Position::FIRST ||
-// position == Position::LAST) {
-// EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u);
-// EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
-// } else {
-// EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), uids.size());
-// }
-//
-// // query again
-// conditionCache[0] = ConditionState::kNotEvaluated;
-// conditionTracker.isConditionMet(queryKey, allPredicates,
-// false,
-// conditionCache);
-// EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
-// }
-//
-//}
-//
+TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
+ std::vector<sp<ConditionTracker>> allConditions;
+ for (Position position : {Position::FIRST, Position::LAST}) {
+ SimplePredicate simplePredicate = getWakeLockHeldCondition(
+ true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
+ position);
+ string conditionName = "WL_HELD_BY_UID2";
+
+ unordered_map<int64_t, int> trackerNameIndexMap;
+ trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
+ trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
+ trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
+
+ SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+ 0 /*condition tracker index*/, simplePredicate,
+ trackerNameIndexMap);
+
+ std::vector<int> uids = {111, 222, 333};
+
+ LogEvent event(/*uid=*/-1, /*pid=*/-1);
+ makeWakeLockEvent(&event, /*atomId=*/ 1, /*timestamp=*/ 0, uids, "wl1", /*acquire=*/ 1);
+
+ // one matched start
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ vector<sp<ConditionTracker>> allPredicates;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
+ changedCache);
+
+ if (position == Position::FIRST || position == Position::LAST) {
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ } else {
+ EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+ }
+ EXPECT_TRUE(changedCache[0]);
+ if (position == Position::FIRST || position == Position::LAST) {
+ EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u);
+ EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+ } else {
+ EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(),
+ uids.size());
+ }
+
+ // Now test query
+ const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+ // another wake lock acquired by this uid
+ LogEvent event2(/*uid=*/-1, /*pid=*/-1);
+ makeWakeLockEvent(&event2, /*atomId=*/ 1, /*timestamp=*/ 0, uids, "wl2", /*acquire=*/ 1);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
+ changedCache);
+ EXPECT_FALSE(changedCache[0]);
+ if (position == Position::FIRST || position == Position::LAST) {
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ } else {
+ EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+ }
+ EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
+ EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+
+
+ // wake lock 1 release
+ LogEvent event3(/*uid=*/-1, /*pid=*/-1);
+ makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/0);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
+ changedCache);
+ // nothing changes, because wake lock 2 is still held for this uid
+ EXPECT_FALSE(changedCache[0]);
+ if (position == Position::FIRST || position == Position::LAST) {
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ } else {
+ EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+ }
+ EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
+ EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+
+ LogEvent event4(/*uid=*/-1, /*pid=*/-1);
+ makeWakeLockEvent(&event, /*atomId=*/1, /*timestamp=*/ 0, uids, "wl2", /*acquire=*/0);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
+ changedCache);
+ EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+ if (position == Position::FIRST || position == Position::LAST) {
+ EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u);
+ EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
+ } else {
+ EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(),
+ uids.size());
+ }
+
+ // query again
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+ }
+
+}
+
//TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
// std::vector<sp<ConditionTracker>> allConditions;
//
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index e416b4c..1ff6621 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -28,6 +28,7 @@
#include "../metrics/metrics_test_helper.h"
#include "src/stats_log_util.h"
+#include "stats_event.h"
#include "tests/statsd_test_util.h"
#ifdef __ANDROID__
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 2bfce9b..d416f13 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -15,6 +15,7 @@
#include "statsd_test_util.h"
#include <aidl/android/util/StatsEventParcel.h>
+#include "stats_event.h"
using aidl::android::util::StatsEventParcel;
using std::shared_ptr;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 3b0667d..ca22bf4 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,6 +16,8 @@
package android.accessibilityservice;
+import static android.accessibilityservice.util.AccessibilityUtils.getFilteredHtmlText;
+import static android.accessibilityservice.util.AccessibilityUtils.loadSafeAnimatedImage;
import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
import android.annotation.IntDef;
@@ -27,7 +29,6 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -782,12 +783,10 @@
}
/**
- * The animated image resource id.
- * <p>
- * <strong>Statically set from
- * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
- * </p>
+ * Gets the animated image resource id.
+ *
* @return The animated image resource id.
+ *
* @hide
*/
public int getAnimatedImageRes() {
@@ -797,10 +796,14 @@
/**
* The animated image drawable.
* <p>
+ * Image can not exceed the screen size.
* <strong>Statically set from
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
* </p>
- * @return The animated image drawable.
+ * @return The animated image drawable, or null if the resource is invalid or the image
+ * exceed the screen size.
+ *
+ * @hide
*/
@Nullable
public Drawable loadAnimatedImage(@NonNull Context context) {
@@ -808,11 +811,8 @@
return null;
}
- final PackageManager packageManager = context.getPackageManager();
- final String packageName = mComponentName.getPackageName();
- final ApplicationInfo applicationInfo = mResolveInfo.serviceInfo.applicationInfo;
-
- return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo);
+ return loadSafeAnimatedImage(context, mResolveInfo.serviceInfo.applicationInfo,
+ mAnimatedImageRes);
}
/**
@@ -924,16 +924,19 @@
}
/**
- * The localized html description of the accessibility service.
+ * The localized and restricted html description of the accessibility service.
* <p>
+ * Filters the <img> tag which do not meet the custom specification and the <a> tag.
* <strong>Statically set from
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
* </p>
- * @return The localized html description.
+ * @return The localized and restricted html description.
+ *
+ * @hide
*/
@Nullable
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
- if (mHtmlDescriptionRes == 0) {
+ if (mHtmlDescriptionRes == /* invalid */ 0) {
return null;
}
@@ -941,7 +944,7 @@
final CharSequence htmlDescription = packageManager.getText(serviceInfo.packageName,
mHtmlDescriptionRes, serviceInfo.applicationInfo);
if (htmlDescription != null) {
- return htmlDescription.toString().trim();
+ return getFilteredHtmlText(htmlDescription.toString().trim());
}
return null;
}
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index 6209679..d2bdf80 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -16,18 +16,21 @@
package android.accessibilityservice;
+import static android.accessibilityservice.util.AccessibilityUtils.getFilteredHtmlText;
+import static android.accessibilityservice.util.AccessibilityUtils.loadSafeAnimatedImage;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Xml;
@@ -134,7 +137,7 @@
// Gets animated image
mAnimatedImageRes = asAttributes.getResourceId(
com.android.internal.R.styleable
- .AccessibilityShortcutTarget_animatedImageDrawable, 0);
+ .AccessibilityShortcutTarget_animatedImageDrawable, /* defValue= */ 0);
// Gets html description
mHtmlDescriptionRes = asAttributes.getResourceId(
com.android.internal.R.styleable.AccessibilityShortcutTarget_htmlDescription,
@@ -192,7 +195,7 @@
}
/**
- * The animated image resource id of the accessibility shortcut target.
+ * Gets the animated image resource id.
*
* @return The animated image resource id.
*
@@ -205,7 +208,10 @@
/**
* The animated image drawable of the accessibility shortcut target.
*
- * @return The animated image drawable.
+ * @return The animated image drawable, or null if the resource is invalid or the image
+ * exceed the screen size.
+ *
+ * @hide
*/
@Nullable
public Drawable loadAnimatedImage(@NonNull Context context) {
@@ -213,21 +219,22 @@
return null;
}
- final PackageManager packageManager = context.getPackageManager();
- final String packageName = mComponentName.getPackageName();
- final ApplicationInfo applicationInfo = mActivityInfo.applicationInfo;
-
- return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo);
+ return loadSafeAnimatedImage(context, mActivityInfo.applicationInfo, mAnimatedImageRes);
}
/**
- * The localized html description of the accessibility shortcut target.
+ * The localized and restricted html description of the accessibility shortcut target.
+ * It filters the <img> tag which do not meet the custom specification and the <a> tag.
*
- * @return The localized html description.
+ * @return The localized and restricted html description.
+ *
+ * @hide
*/
@Nullable
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
- return loadResourceString(packageManager, mActivityInfo, mHtmlDescriptionRes);
+ final String htmlDescription = loadResourceString(packageManager, mActivityInfo,
+ mHtmlDescriptionRes);
+ return TextUtils.isEmpty(htmlDescription) ? null : getFilteredHtmlText(htmlDescription);
}
/**
diff --git a/core/java/android/accessibilityservice/util/AccessibilityUtils.java b/core/java/android/accessibilityservice/util/AccessibilityUtils.java
new file mode 100644
index 0000000..fa32bb2
--- /dev/null
+++ b/core/java/android/accessibilityservice/util/AccessibilityUtils.java
@@ -0,0 +1,139 @@
+/*
+ * 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.accessibilityservice.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.TypedValue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Collection of utilities for accessibility service.
+ *
+ * @hide
+ */
+public final class AccessibilityUtils {
+ private AccessibilityUtils() {}
+
+ // Used for html description of accessibility service. The <img> src tag must follow the
+ // prefix rule. e.g. <img src="R.drawable.fileName"/>
+ private static final String IMG_PREFIX = "R.drawable.";
+ private static final String ANCHOR_TAG = "a";
+ private static final List<String> UNSUPPORTED_TAG_LIST = new ArrayList<>(
+ Collections.singletonList(ANCHOR_TAG));
+
+ /**
+ * Gets the filtered html string for
+ * {@link android.accessibilityservice.AccessibilityServiceInfo} and
+ * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters
+ * the <img> tag which do not meet the custom specification and the <a> tag.
+ *
+ * @param text the target text is html format.
+ * @return the filtered html string.
+ */
+ public static @NonNull String getFilteredHtmlText(@NonNull String text) {
+ final String replacementStart = "<invalidtag ";
+ final String replacementEnd = "</invalidtag>";
+
+ for (String tag : UNSUPPORTED_TAG_LIST) {
+ final String regexStart = "(?i)<" + tag + "(\\s+|>)";
+ final String regexEnd = "(?i)</" + tag + "\\s*>";
+ text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart);
+ text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd);
+ }
+
+ final String regexInvalidImgTag = "(?i)<img\\s+(?!src\\s*=\\s*\"(?-i)" + IMG_PREFIX + ")";
+ text = Pattern.compile(regexInvalidImgTag).matcher(text).replaceAll(
+ replacementStart);
+
+ return text;
+ }
+
+ /**
+ * Loads the animated image for
+ * {@link android.accessibilityservice.AccessibilityServiceInfo} and
+ * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It checks the resource
+ * whether to exceed the screen size.
+ *
+ * @param context the current context.
+ * @param applicationInfo the current application.
+ * @param resId the animated image resource id.
+ * @return the animated image which is safe.
+ */
+ @Nullable
+ public static Drawable loadSafeAnimatedImage(@NonNull Context context,
+ @NonNull ApplicationInfo applicationInfo, @StringRes int resId) {
+ if (resId == /* invalid */ 0) {
+ return null;
+ }
+
+ final PackageManager packageManager = context.getPackageManager();
+ final String packageName = applicationInfo.packageName;
+ final Drawable bannerDrawable = packageManager.getDrawable(packageName, resId,
+ applicationInfo);
+ if (bannerDrawable == null) {
+ return null;
+ }
+
+ final boolean isImageWidthOverScreenLength =
+ bannerDrawable.getIntrinsicWidth() > getScreenWidthPixels(context);
+ final boolean isImageHeightOverScreenLength =
+ bannerDrawable.getIntrinsicHeight() > getScreenHeightPixels(context);
+
+ return (isImageWidthOverScreenLength || isImageHeightOverScreenLength)
+ ? null
+ : bannerDrawable;
+ }
+
+ /**
+ * Gets the width of the screen.
+ *
+ * @param context the current context.
+ * @return the width of the screen in term of pixels.
+ */
+ private static int getScreenWidthPixels(@NonNull Context context) {
+ final Resources resources = context.getResources();
+ final int screenWidthDp = resources.getConfiguration().screenWidthDp;
+
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenWidthDp,
+ resources.getDisplayMetrics()));
+ }
+
+ /**
+ * Gets the height of the screen.
+ *
+ * @param context the current context.
+ * @return the height of the screen in term of pixels.
+ */
+ private static int getScreenHeightPixels(@NonNull Context context) {
+ final Resources resources = context.getResources();
+ final int screenHeightDp = resources.getConfiguration().screenHeightDp;
+
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp,
+ resources.getDisplayMetrics()));
+ }
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 82fdb90..b51bbdf 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2012,15 +2012,16 @@
/** See {@link android.view.Surface.Rotation} */
@Surface.Rotation
private int mRotation;
+ /** The size of the snapshot before scaling */
+ private final Point mTaskSize;
private final Rect mContentInsets;
- // Whether this snapshot is a down-sampled version of the full resolution, used mainly for
- // low-ram devices
+ // 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;
// Whether or not the snapshot is a real snapshot or an app-theme generated snapshot due to
// the task having a secure window or having previews disabled
private final boolean mIsRealSnapshot;
private final int mWindowingMode;
- private final float mScale;
private final int mSystemUiVisibility;
private final boolean mIsTranslucent;
// Must be one of the named color spaces, otherwise, always use SRGB color space.
@@ -2028,9 +2029,9 @@
public TaskSnapshot(long id,
@NonNull ComponentName topActivityComponent, GraphicBuffer snapshot,
- @NonNull ColorSpace colorSpace, int orientation, int rotation, Rect contentInsets,
- boolean isLowResolution, float scale, boolean isRealSnapshot, int windowingMode,
- int systemUiVisibility, boolean isTranslucent) {
+ @NonNull ColorSpace colorSpace, int orientation, int rotation, Point taskSize,
+ Rect contentInsets, boolean isLowResolution, boolean isRealSnapshot,
+ int windowingMode, int systemUiVisibility, boolean isTranslucent) {
mId = id;
mTopActivityComponent = topActivityComponent;
mSnapshot = snapshot;
@@ -2038,9 +2039,9 @@
? ColorSpace.get(ColorSpace.Named.SRGB) : colorSpace;
mOrientation = orientation;
mRotation = rotation;
+ mTaskSize = new Point(taskSize);
mContentInsets = new Rect(contentInsets);
mIsLowResolution = isLowResolution;
- mScale = scale;
mIsRealSnapshot = isRealSnapshot;
mWindowingMode = windowingMode;
mSystemUiVisibility = systemUiVisibility;
@@ -2057,9 +2058,9 @@
: ColorSpace.get(ColorSpace.Named.SRGB);
mOrientation = source.readInt();
mRotation = source.readInt();
+ mTaskSize = source.readParcelable(null /* classLoader */);
mContentInsets = source.readParcelable(null /* classLoader */);
mIsLowResolution = source.readBoolean();
- mScale = source.readFloat();
mIsRealSnapshot = source.readBoolean();
mWindowingMode = source.readInt();
mSystemUiVisibility = source.readInt();
@@ -2111,6 +2112,14 @@
}
/**
+ * @return The size of the task at the point this snapshot was taken.
+ */
+ @UnsupportedAppUsage
+ public Point getTaskSize() {
+ return mTaskSize;
+ }
+
+ /**
* @return The system/content insets on the snapshot. These can be clipped off in order to
* remove any areas behind system bars in the snapshot.
*/
@@ -2159,14 +2168,6 @@
return mSystemUiVisibility;
}
- /**
- * @return The scale this snapshot was taken in.
- */
- @UnsupportedAppUsage
- public float getScale() {
- return mScale;
- }
-
@Override
public int describeContents() {
return 0;
@@ -2180,9 +2181,9 @@
dest.writeInt(mColorSpace.getId());
dest.writeInt(mOrientation);
dest.writeInt(mRotation);
+ dest.writeParcelable(mTaskSize, 0);
dest.writeParcelable(mContentInsets, 0);
dest.writeBoolean(mIsLowResolution);
- dest.writeFloat(mScale);
dest.writeBoolean(mIsRealSnapshot);
dest.writeInt(mWindowingMode);
dest.writeInt(mSystemUiVisibility);
@@ -2200,9 +2201,11 @@
+ " mColorSpace=" + mColorSpace.toString()
+ " mOrientation=" + mOrientation
+ " mRotation=" + mRotation
+ + " mTaskSize=" + mTaskSize.toString()
+ " mContentInsets=" + mContentInsets.toShortString()
- + " mIsLowResolution=" + mIsLowResolution + " mScale=" + mScale
- + " mIsRealSnapshot=" + mIsRealSnapshot + " mWindowingMode=" + mWindowingMode
+ + " mIsLowResolution=" + mIsLowResolution
+ + " mIsRealSnapshot=" + mIsRealSnapshot
+ + " mWindowingMode=" + mWindowingMode
+ " mSystemUiVisibility=" + mSystemUiVisibility
+ " mIsTranslucent=" + mIsTranslucent;
}
@@ -2224,9 +2227,8 @@
private ColorSpace mColorSpace;
private int mOrientation;
private int mRotation;
+ private Point mTaskSize;
private Rect mContentInsets;
- private boolean mIsLowResolution;
- private float mScaleFraction;
private boolean mIsRealSnapshot;
private int mWindowingMode;
private int mSystemUiVisibility;
@@ -2263,28 +2265,19 @@
return this;
}
+ /**
+ * Sets the original size of the task
+ */
+ public Builder setTaskSize(Point size) {
+ mTaskSize = size;
+ return this;
+ }
+
public Builder setContentInsets(Rect contentInsets) {
mContentInsets = contentInsets;
return this;
}
- /**
- * Set to true if this is a low-resolution snapshot stored in *_reduced.jpg.
- */
- public Builder setIsLowResolution(boolean isLowResolution) {
- mIsLowResolution = isLowResolution;
- return this;
- }
-
- public float getScaleFraction() {
- return mScaleFraction;
- }
-
- public Builder setScaleFraction(float scaleFraction) {
- mScaleFraction = scaleFraction;
- return this;
- }
-
public Builder setIsRealSnapshot(boolean realSnapshot) {
mIsRealSnapshot = realSnapshot;
return this;
@@ -2322,9 +2315,12 @@
mColorSpace,
mOrientation,
mRotation,
+ mTaskSize,
mContentInsets,
- mIsLowResolution,
- mScaleFraction,
+ // When building a TaskSnapshot with the Builder class, isLowResolution
+ // is always false. Low-res snapshots are only created when loading from
+ // disk.
+ false /* isLowResolution */,
mIsRealSnapshot,
mWindowingMode,
mSystemUiVisibility,
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 9ba56cf..fb315ad 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -215,8 +215,7 @@
public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
boolean animate, Rect initialBounds, boolean showRecents) throws SecurityException {
try {
- return getService().setTaskWindowingModeSplitScreenPrimary(taskId, createMode, toTop,
- animate, initialBounds, showRecents);
+ return getService().setTaskWindowingModeSplitScreenPrimary(taskId, toTop);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -362,25 +361,6 @@
}
/**
- * Resize the input stack id to the given bounds with animate setting.
- * @param stackId Id of the stack to resize.
- * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
- * @param animate Whether we should play an animation for resizing stack.
- */
- @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
- public void resizePinnedStack(int stackId, Rect bounds, boolean animate) {
- try {
- if (animate) {
- getService().animateResizePinnedStack(stackId, bounds, -1 /* animationDuration */);
- } else {
- getService().resizePinnedStack(bounds, null /* tempPinnedTaskBounds */);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Resize task to given bounds.
* @param taskId Id of task to resize.
* @param bounds Bounds to resize task.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 92dd91a..0ed5aec5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -68,6 +68,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
+import android.content.pm.ProviderInfoList;
import android.content.pm.ServiceInfo;
import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
@@ -1011,8 +1012,9 @@
sendMessage(H.STOP_SERVICE, token);
}
+ @Override
public final void bindApplication(String processName, ApplicationInfo appInfo,
- List<ProviderInfo> providers, ComponentName instrumentationName,
+ ProviderInfoList providerList, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
@@ -1052,7 +1054,7 @@
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
- data.providers = providers;
+ data.providers = providerList.getList();
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8f02f15..a53fc35 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -6910,11 +6910,7 @@
* Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
*/
public int unsafeCheckOpRaw(@NonNull String op, int uid, @NonNull String packageName) {
- try {
- return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return unsafeCheckOpRawNoThrow(op, uid, packageName);
}
/**
@@ -6923,8 +6919,17 @@
* {@link #MODE_FOREGROUND}.
*/
public int unsafeCheckOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
+ return unsafeCheckOpRawNoThrow(strOpToOp(op), uid, packageName);
+ }
+
+ /**
+ * Returns the <em>raw</em> mode associated with the op.
+ * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
+ * @hide
+ */
+ public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
try {
- return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
+ return mService.checkOperationRaw(op, uid, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 4f41f8b..969ea70 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -675,7 +675,7 @@
@Override
public int checkPermission(String permName, String pkgName) {
return PermissionManager
- .checkPackageNamePermission(permName, pkgName);
+ .checkPackageNamePermission(permName, pkgName, getUserId());
}
@Override
diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java
new file mode 100644
index 0000000..fe13b8f
--- /dev/null
+++ b/core/java/android/app/DreamManager.java
@@ -0,0 +1,102 @@
+/*
+ * 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.app;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+
+/**
+ * @hide
+ */
+@SystemService(Context.DREAM_SERVICE)
+@TestApi
+public class DreamManager {
+ private final IDreamManager mService;
+ private final Context mContext;
+
+ /**
+ * @hide
+ */
+ public DreamManager(Context context) throws ServiceManager.ServiceNotFoundException {
+ mService = IDreamManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(DreamService.DREAM_SERVICE));
+ mContext = context;
+ }
+
+ /**
+ * Starts dream service with name "name".
+ *
+ * <p>This is only used for testing the dream service APIs.
+ *
+ * @hide
+ */
+ @TestApi
+ @UserHandleAware
+ @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+ public void startDream(@NonNull ComponentName name) {
+ try {
+ mService.testDream(mContext.getUserId(), name);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Stops the dream service on the device if one is started.
+ *
+ * <p> This is only used for testing the dream service APIs.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+ public void stopDream() {
+ try {
+ mService.awaken();
+ } 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 setActiveDream(@NonNull ComponentName dreamComponent) {
+ ComponentName[] dreams = {dreamComponent};
+ try {
+ mService.setDreamComponentsForUser(mContext.getUserId(), dreams);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 463c8c9..6f0611e 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -528,29 +528,6 @@
boolean unlockUser(int userid, in byte[] token, in byte[] secret,
in IProgressListener listener);
void killPackageDependents(in String packageName, int userId);
- /**
- * Resizes the docked stack, and all other stacks as the result of the dock stack bounds change.
- *
- * @param dockedBounds The bounds for the docked stack.
- * @param tempDockedTaskBounds The temporary bounds for the tasks in the docked stack, which
- * might be different from the stack bounds to allow more
- * flexibility while resizing, or {@code null} if they should be the
- * same as the stack bounds.
- * @param tempDockedTaskInsetBounds The temporary bounds for the tasks to calculate the insets.
- * When resizing, we usually "freeze" the layout of a task. To
- * achieve that, we also need to "freeze" the insets, which
- * gets achieved by changing task bounds but not bounds used
- * to calculate the insets in this transient state
- * @param tempOtherTaskBounds The temporary bounds for the tasks in all other stacks, or
- * {@code null} if they should be the same as the stack bounds.
- * @param tempOtherTaskInsetBounds Like {@code tempDockedTaskInsetBounds}, but for the other
- * stacks.
- * @throws RemoteException
- */
- @UnsupportedAppUsage
- void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
- in Rect tempDockedTaskInsetBounds,
- in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
@UnsupportedAppUsage
void removeStack(int stackId);
void makePackageIdle(String packageName, int userId);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 5b61402..7c89263 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -98,6 +98,14 @@
in ProfilerInfo profilerInfo, in Bundle options, int userId);
boolean startNextMatchingActivity(in IBinder callingActivity,
in Intent intent, in Bundle options);
+
+ /**
+ * The DreamActivity has to be started in a special way that does not involve the PackageParser.
+ * The DreamActivity is a framework component inserted in the dream application process. Hence,
+ * it is not declared in the application's manifest and cannot be parsed. startDreamActivity
+ * creates the activity and starts it without reaching out to the PackageParser.
+ */
+ boolean startDreamActivity(in Intent intent);
int startActivityIntentSender(in IApplicationThread caller,
in IIntentSender target, in IBinder whitelistToken, in Intent fillInIntent,
in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
@@ -236,32 +244,7 @@
*/
boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
void moveTaskToStack(int taskId, int stackId, boolean toTop);
- /**
- * Resizes the input pinned stack to the given bounds with animation.
- *
- * @param stackId Id of the pinned stack to resize.
- * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
- * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
- * default animation duration should be used.
- * @throws RemoteException
- */
- void animateResizePinnedStack(int stackId, in Rect bounds, int animationDuration);
- boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
- boolean animate, in Rect initialBounds, boolean showRecents);
- /**
- * Use the offset to adjust the stack boundary with animation.
- *
- * @param stackId Id of the stack to adjust.
- * @param compareBounds Offset is only applied if the current pinned stack bounds is equal to
- * the compareBounds.
- * @param xOffset The horizontal offset.
- * @param yOffset The vertical offset.
- * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
- * default animation duration should be used.
- * @throws RemoteException
- */
- void offsetPinnedStackBounds(int stackId, in Rect compareBounds, int xOffset, int yOffset,
- int animationDuration);
+ boolean setTaskWindowingModeSplitScreenPrimary(int taskId, boolean toTop);
/**
* Removes stacks in the input windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
@@ -371,23 +354,10 @@
void startLocalVoiceInteraction(in IBinder token, in Bundle options);
void stopLocalVoiceInteraction(in IBinder token);
boolean supportsLocalVoiceInteraction();
- void notifyPinnedStackAnimationStarted();
- void notifyPinnedStackAnimationEnded();
// Get device configuration
ConfigurationInfo getDeviceConfigurationInfo();
- /**
- * Resizes the pinned stack.
- *
- * @param pinnedBounds The bounds for the pinned stack.
- * @param tempPinnedTaskBounds The temporary bounds for the tasks in the pinned stack, which
- * might be different from the stack bounds to allow more
- * flexibility while resizing, or {@code null} if they should be the
- * same as the stack bounds.
- */
- void resizePinnedStack(in Rect pinnedBounds, in Rect tempPinnedTaskBounds);
-
void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback,
in CharSequence message);
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index c33c515..1f6e4ca 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -30,6 +30,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
+import android.content.pm.ProviderInfoList;
import android.content.pm.ServiceInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -66,7 +67,7 @@
@UnsupportedAppUsage
void scheduleStopService(IBinder token);
void bindApplication(in String packageName, in ApplicationInfo info,
- in List<ProviderInfo> providers, in ComponentName testName,
+ in ProviderInfoList providerList, in ComponentName testName,
in ProfilerInfo profilerInfo, in Bundle testArguments,
IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,
int debugMode, boolean enableBinderTracking, boolean trackAllocation,
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 37bdda0..28b28da 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -46,16 +46,6 @@
void onPinnedActivityRestartAttempt(boolean clearedTask);
/**
- * Called whenever the pinned stack is starting animating a resize.
- */
- void onPinnedStackAnimationStarted();
-
- /**
- * Called whenever the pinned stack is done animating a resize.
- */
- void onPinnedStackAnimationEnded();
-
- /**
* Called when we launched an activity that we forced to be resizable.
*
* @param packageName Package name of the top activity in the task.
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 4369680..d04630c 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -186,7 +186,6 @@
import android.telephony.TelephonyRegistryManager;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.Slog;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.WindowManager;
@@ -223,8 +222,6 @@
public final class SystemServiceRegistry {
private static final String TAG = "SystemServiceRegistry";
- private static final boolean ENABLE_SERVICE_NOT_FOUND_WTF = true;
-
// Service registry information.
// This information is never changed once static initialization has completed.
private static final Map<Class<?>, String> SYSTEM_SERVICE_NAMES =
@@ -1334,6 +1331,13 @@
IBinder b = ServiceManager.getServiceOrThrow(Context.APP_INTEGRITY_SERVICE);
return new AppIntegrityManager(IAppIntegrityManager.Stub.asInterface(b));
}});
+ registerService(Context.DREAM_SERVICE, DreamManager.class,
+ new CachedServiceFetcher<DreamManager>() {
+ @Override
+ public DreamManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ return new DreamManager(ctx);
+ }});
sInitializing = true;
try {
@@ -1370,29 +1374,8 @@
* @hide
*/
public static Object getSystemService(ContextImpl ctx, String name) {
- if (name == null) {
- return null;
- }
- final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
- if (ENABLE_SERVICE_NOT_FOUND_WTF && fetcher == null) {
- // This should be a caller bug.
- Slog.wtf(TAG, "Unknown manager requested: " + name);
- return null;
- }
-
- final Object ret = fetcher.getService(ctx);
- if (ENABLE_SERVICE_NOT_FOUND_WTF && ret == null) {
- // Some services do return null in certain situations, so don't do WTF for them.
- switch (name) {
- case Context.CONTENT_CAPTURE_MANAGER_SERVICE:
- case Context.APP_PREDICTION_SERVICE:
- case Context.INCREMENTAL_SERVICE:
- return null;
- }
- Slog.wtf(TAG, "Manager wrapper not available: " + name);
- return null;
- }
- return ret;
+ ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
+ return fetcher != null ? fetcher.getService(ctx) : null;
}
/**
@@ -1400,15 +1383,7 @@
* @hide
*/
public static String getSystemServiceName(Class<?> serviceClass) {
- if (serviceClass == null) {
- return null;
- }
- final String serviceName = SYSTEM_SERVICE_NAMES.get(serviceClass);
- if (ENABLE_SERVICE_NOT_FOUND_WTF && serviceName == null) {
- // This should be a caller bug.
- Slog.wtf(TAG, "Unknown manager requested: " + serviceClass.getCanonicalName());
- }
- return serviceName;
+ return SYSTEM_SERVICE_NAMES.get(serviceClass);
}
/**
@@ -1705,9 +1680,7 @@
try {
cache.wait();
} catch (InterruptedException e) {
- // This shouldn't normally happen, but if someone interrupts the
- // thread, it will.
- Slog.wtf(TAG, "getService() interrupted");
+ Log.w(TAG, "getService() interrupted");
Thread.currentThread().interrupt();
return null;
}
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index da0aadb..b892b8e 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -58,16 +58,6 @@
@Override
@UnsupportedAppUsage
- public void onPinnedStackAnimationStarted() throws RemoteException {
- }
-
- @Override
- @UnsupportedAppUsage
- public void onPinnedStackAnimationEnded() throws RemoteException {
- }
-
- @Override
- @UnsupportedAppUsage
public void onActivityForcedResizable(String packageName, int taskId, int reason)
throws RemoteException {
}
diff --git a/core/java/android/app/compat/ChangeIdStateCache.java b/core/java/android/app/compat/ChangeIdStateCache.java
new file mode 100644
index 0000000..9ef63f6
--- /dev/null
+++ b/core/java/android/app/compat/ChangeIdStateCache.java
@@ -0,0 +1,86 @@
+/*
+ * 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.app.compat;
+
+import android.app.PropertyInvalidatedCache;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.compat.IPlatformCompat;
+
+/**
+ * Handles caching of calls to {@link com.android.internal.compat.IPlatformCompat}
+ * @hide
+ */
+public final class ChangeIdStateCache
+ extends PropertyInvalidatedCache<ChangeIdStateQuery, Boolean> {
+ private static final String CACHE_KEY = "cache_key.is_compat_change_enabled";
+ private static final int MAX_ENTRIES = 20;
+ private static boolean sDisabled = false;
+
+ /** @hide */
+ public ChangeIdStateCache() {
+ super(MAX_ENTRIES, CACHE_KEY);
+ }
+
+ /**
+ * Disable cache.
+ *
+ * <p>Should only be used in unit tests.
+ * @hide
+ */
+ public static void disable() {
+ sDisabled = true;
+ }
+
+ /**
+ * Invalidate the cache.
+ *
+ * <p>Can only be called by the system server process.
+ * @hide
+ */
+ public static void invalidate() {
+ if (!sDisabled) {
+ PropertyInvalidatedCache.invalidateCache(CACHE_KEY);
+ }
+ }
+
+ @Override
+ protected Boolean recompute(ChangeIdStateQuery query) {
+ IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (query.type == ChangeIdStateQuery.QUERY_BY_PACKAGE_NAME) {
+ return platformCompat.isChangeEnabledByPackageName(query.changeId,
+ query.packageName,
+ query.userId);
+ } else if (query.type == ChangeIdStateQuery.QUERY_BY_UID) {
+ return platformCompat.isChangeEnabledByUid(query.changeId, query.uid);
+ } else {
+ throw new IllegalArgumentException("Invalid query type: " + query.type);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ throw new IllegalStateException("Could not recompute value!");
+ }
+}
diff --git a/core/java/android/app/compat/ChangeIdStateQuery.java b/core/java/android/app/compat/ChangeIdStateQuery.java
new file mode 100644
index 0000000..3c245b1
--- /dev/null
+++ b/core/java/android/app/compat/ChangeIdStateQuery.java
@@ -0,0 +1,90 @@
+/*
+ * 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.app.compat;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+
+/**
+ * A key type for caching calls to {@link com.android.internal.compat.IPlatformCompat}
+ *
+ * <p>For {@link com.android.internal.compat.IPlatformCompat#isChangeEnabledByPackageName}
+ * and {@link com.android.internal.compat.IPlatformCompat#isChangeEnabledByUid}
+ *
+ * @hide
+ */
+@Immutable
+final class ChangeIdStateQuery {
+
+ static final int QUERY_BY_PACKAGE_NAME = 0;
+ static final int QUERY_BY_UID = 1;
+ @IntDef({QUERY_BY_PACKAGE_NAME, QUERY_BY_UID})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface QueryType {}
+
+ public @QueryType int type;
+ public long changeId;
+ public String packageName;
+ public int uid;
+ public int userId;
+
+ private ChangeIdStateQuery(@QueryType int type, long changeId, String packageName,
+ int uid, int userId) {
+ this.type = type;
+ this.changeId = changeId;
+ this.packageName = packageName;
+ this.uid = uid;
+ this.userId = userId;
+ }
+
+ static ChangeIdStateQuery byPackageName(long changeId, @NonNull String packageName,
+ int userId) {
+ return new ChangeIdStateQuery(QUERY_BY_PACKAGE_NAME, changeId, packageName, 0, userId);
+ }
+
+ static ChangeIdStateQuery byUid(long changeId, int uid) {
+ return new ChangeIdStateQuery(QUERY_BY_UID, changeId, null, uid, 0);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if ((other == null) || !(other instanceof ChangeIdStateQuery)) {
+ return false;
+ }
+ final ChangeIdStateQuery that = (ChangeIdStateQuery) other;
+ return this.type == that.type
+ && this.changeId == that.changeId
+ && Objects.equals(this.packageName, that.packageName)
+ && this.uid == that.uid
+ && this.userId == that.userId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, changeId, packageName, uid, userId);
+ }
+}
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index e289a27..0d5e45f 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -19,14 +19,8 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.compat.Compatibility;
-import android.content.Context;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
-import com.android.internal.compat.IPlatformCompat;
-
/**
* CompatChanges APIs - to be used by platform code only (including mainline
* modules).
@@ -35,6 +29,7 @@
*/
@SystemApi
public final class CompatChanges {
+ private static final ChangeIdStateCache QUERY_CACHE = new ChangeIdStateCache();
private CompatChanges() {}
/**
@@ -69,17 +64,8 @@
*/
public static boolean isChangeEnabled(long changeId, @NonNull String packageName,
@NonNull UserHandle user) {
- IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
- final long token = Binder.clearCallingIdentity();
- try {
- return platformCompat.isChangeEnabledByPackageName(changeId, packageName,
- user.getIdentifier());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ return QUERY_CACHE.query(ChangeIdStateQuery.byPackageName(changeId, packageName,
+ user.getIdentifier()));
}
/**
@@ -101,15 +87,7 @@
* @return {@code true} if the change is enabled for the current app.
*/
public static boolean isChangeEnabled(long changeId, int uid) {
- IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
- final long token = Binder.clearCallingIdentity();
- try {
- return platformCompat.isChangeEnabledByUid(changeId, uid);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ return QUERY_CACHE.query(ChangeIdStateQuery.byUid(changeId, uid));
}
+
}
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index f0eedf3..7f43640 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -260,6 +260,7 @@
Log.e(TAG, "Failed to notify app target event", e);
e.rethrowAsRuntimeException();
}
+ mRegisteredCallbacks.clear();
} else {
throw new IllegalStateException("This client has already been destroyed.");
}
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 6dea1c6..ccd8199 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -182,6 +182,16 @@
public static final String EXTRA_APPWIDGET_ID = "appWidgetId";
/**
+ * A bundle extra that contains whether or not an app has finished restoring a widget.
+ * <p> After restore, the app should set OPTION_APPWIDGET_RESTORE_COMPLETED to true on its
+ * widgets followed by calling {@link #updateAppWidget} to update the views.
+ *
+ * @see #updateAppWidgetOptions(int, Bundle)
+ */
+ public static final String OPTION_APPWIDGET_RESTORE_COMPLETED = "appWidgetRestoreCompleted";
+
+
+ /**
* A bundle extra that contains the lower bound on the current width, in dips, of a widget instance.
*/
public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java
index ab91edf..a5d2198 100644
--- a/core/java/android/appwidget/AppWidgetProvider.java
+++ b/core/java/android/appwidget/AppWidgetProvider.java
@@ -200,6 +200,9 @@
* provider can immediately generate new RemoteViews suitable for its newly-restored set
* of instances.
*
+ * <p>In addition, you should set {@link AppWidgetManager#OPTION_APPWIDGET_RESTORE_COMPLETED}
+ * to true indicate if a widget has been restored successfully from the provider's side.
+ *
* {@more}
*
* @param context
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index b748cfa..c7f42cb 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -3827,6 +3827,18 @@
return queryArgs;
}
+ /** @hide */
+ public static @NonNull Bundle includeSqlSelectionArgs(@NonNull Bundle queryArgs,
+ @Nullable String selection, @Nullable String[] selectionArgs) {
+ if (selection != null) {
+ queryArgs.putString(QUERY_ARG_SQL_SELECTION, selection);
+ }
+ if (selectionArgs != null) {
+ queryArgs.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
+ }
+ return queryArgs;
+ }
+
/**
* Returns structured sort args formatted as an SQL sort clause.
*
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c6e84b7..536b6c3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5157,6 +5157,17 @@
public static final String LIGHTS_SERVICE = "lights";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.app.DreamManager} for controlling Dream states.
+ *
+ * @see #getSystemService(String)
+
+ * @hide
+ */
+ @TestApi
+ public static final String DREAM_SERVICE = "dream";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 55a6cab..62815dd 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -138,6 +138,7 @@
*
* @hide
*/
+ @NonNull
public final String packageName;
/**
@@ -145,6 +146,7 @@
*
* @hide
*/
+ @NonNull
public final String targetPackageName;
/**
@@ -165,6 +167,7 @@
* Full path to the base APK for this overlay package
* @hide
*/
+ @NonNull
public final String baseCodePath;
/**
@@ -292,6 +295,7 @@
return targetOverlayableName;
}
+ @SuppressWarnings("ConstantConditions")
private void ensureValidState() {
if (packageName == null) {
throw new IllegalArgumentException("packageName must not be null");
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 9d1c677..4c6fef2 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1630,6 +1630,9 @@
@SuppressWarnings("unchecked")
public void writeToParcel(Parcel dest, int parcelableFlags) {
+ if (dest.maybeWriteSquashed(this)) {
+ return;
+ }
super.writeToParcel(dest, parcelableFlags);
dest.writeString(taskAffinity);
dest.writeString(permission);
@@ -1700,9 +1703,12 @@
public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
= new Parcelable.Creator<ApplicationInfo>() {
+ @Override
public ApplicationInfo createFromParcel(Parcel source) {
- return new ApplicationInfo(source);
+ return source.readSquashed(ApplicationInfo::new);
}
+
+ @Override
public ApplicationInfo[] newArray(int size) {
return new ApplicationInfo[size];
}
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index 8b41c04..362098c 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -20,7 +20,6 @@
import android.content.ComponentName;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
-import android.os.Parcelable;
import android.util.Printer;
/**
@@ -197,12 +196,7 @@
public void writeToParcel(Parcel dest, int parcelableFlags) {
super.writeToParcel(dest, parcelableFlags);
- if ((parcelableFlags & Parcelable.PARCELABLE_ELIDE_DUPLICATES) != 0) {
- dest.writeInt(0);
- } else {
- dest.writeInt(1);
- applicationInfo.writeToParcel(dest, parcelableFlags);
- }
+ applicationInfo.writeToParcel(dest, parcelableFlags);
dest.writeString(processName);
dest.writeString(splitName);
dest.writeInt(descriptionRes);
@@ -213,10 +207,7 @@
protected ComponentInfo(Parcel source) {
super(source);
- final boolean hasApplicationInfo = (source.readInt() != 0);
- if (hasApplicationInfo) {
- applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
- }
+ applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
processName = source.readString();
splitName = source.readString();
descriptionRes = source.readInt();
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 36fa572..85c698f 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -439,6 +439,8 @@
@Override
public void writeToParcel(Parcel dest, int parcelableFlags) {
+ // Allow ApplicationInfo to be squashed.
+ final boolean prevAllowSquashing = dest.allowSquashing();
dest.writeString(packageName);
dest.writeStringArray(splitNames);
dest.writeInt(versionCode);
@@ -457,10 +459,10 @@
dest.writeLong(firstInstallTime);
dest.writeLong(lastUpdateTime);
dest.writeIntArray(gids);
- dest.writeTypedArray(activities, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
- dest.writeTypedArray(receivers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
- dest.writeTypedArray(services, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
- dest.writeTypedArray(providers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
+ dest.writeTypedArray(activities, parcelableFlags);
+ dest.writeTypedArray(receivers, parcelableFlags);
+ dest.writeTypedArray(services, parcelableFlags);
+ dest.writeTypedArray(providers, parcelableFlags);
dest.writeTypedArray(instrumentation, parcelableFlags);
dest.writeTypedArray(permissions, parcelableFlags);
dest.writeStringArray(requestedPermissions);
@@ -488,6 +490,7 @@
dest.writeInt(0);
}
dest.writeBoolean(isApex);
+ dest.restoreAllowSquashing(prevAllowSquashing);
}
public static final @android.annotation.NonNull Parcelable.Creator<PackageInfo> CREATOR
@@ -550,21 +553,5 @@
signingInfo = SigningInfo.CREATOR.createFromParcel(source);
}
isApex = source.readBoolean();
- // The component lists were flattened with the redundant ApplicationInfo
- // instances omitted. Distribute the canonical one here as appropriate.
- if (applicationInfo != null) {
- propagateApplicationInfo(applicationInfo, activities);
- propagateApplicationInfo(applicationInfo, receivers);
- propagateApplicationInfo(applicationInfo, services);
- propagateApplicationInfo(applicationInfo, providers);
- }
- }
-
- private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
- if (components != null) {
- for (ComponentInfo ci : components) {
- ci.applicationInfo = appInfo;
- }
- }
}
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f2ec938..fa751d3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -7853,11 +7853,16 @@
/**
* Returns if the provided drawable represents the default activity icon provided by the system.
*
- * PackageManager provides a default icon for any package/activity if the app itself does not
- * define one or if the system encountered any error when loading the icon.
+ * PackageManager silently returns a default application icon for any package/activity if the
+ * app itself does not define one or if the system encountered any error when loading the icon.
+ *
+ * Developers can use this to check implement app specific logic around retrying or caching.
*
* @return true if the drawable represents the default activity icon, false otherwise
* @see #getDefaultActivityIcon()
+ * @see PackageItemInfo#loadDefaultIcon(PackageManager)
+ * @see #getActivityIcon
+ * @see LauncherActivityInfo#getIcon(int)
*/
public boolean isDefaultApplicationIcon(@NonNull Drawable drawable) {
int resId = drawable instanceof AdaptiveIconDrawable
diff --git a/core/java/android/content/pm/ProviderInfoList.aidl b/core/java/android/content/pm/ProviderInfoList.aidl
new file mode 100644
index 0000000..bb576d7
--- /dev/null
+++ b/core/java/android/content/pm/ProviderInfoList.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable ProviderInfoList;
diff --git a/core/java/android/content/pm/ProviderInfoList.java b/core/java/android/content/pm/ProviderInfoList.java
new file mode 100644
index 0000000..566be2e
--- /dev/null
+++ b/core/java/android/content/pm/ProviderInfoList.java
@@ -0,0 +1,87 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Equivalent to List<ProviderInfo>, but it "squashes" the ApplicationInfo in the elements.
+ *
+ * @hide
+ */
+@TestApi
+public final class ProviderInfoList implements Parcelable {
+ private final List<ProviderInfo> mList;
+
+ private ProviderInfoList(Parcel source) {
+ final ArrayList<ProviderInfo> list = new ArrayList<>();
+ source.readTypedList(list, ProviderInfo.CREATOR);
+ mList = list;
+ }
+
+ private ProviderInfoList(List<ProviderInfo> list) {
+ mList = list;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // Allow ApplicationInfo to be squashed.
+ final boolean prevAllowSquashing = dest.allowSquashing();
+ dest.writeTypedList(mList, flags);
+ dest.restoreAllowSquashing(prevAllowSquashing);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<ProviderInfoList> CREATOR
+ = new Parcelable.Creator<ProviderInfoList>() {
+ @Override
+ public ProviderInfoList createFromParcel(@NonNull Parcel source) {
+ return new ProviderInfoList(source);
+ }
+
+ @Override
+ public ProviderInfoList[] newArray(int size) {
+ return new ProviderInfoList[size];
+ }
+ };
+
+ /**
+ * Return the stored list.
+ */
+ @NonNull
+ public List<ProviderInfo> getList() {
+ return mList;
+ }
+
+ /**
+ * Create a new instance with a {@code list}. The passed list will be shared with the new
+ * instance, so the caller shouldn't modify it.
+ */
+ @NonNull
+ public static ProviderInfoList fromList(@NonNull List<ProviderInfo> list) {
+ return new ProviderInfoList(list);
+ }
+}
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 9e40f46..9da0f20 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -54,7 +54,6 @@
private final int mHash;
- @UnsupportedAppUsage
public ResourcesKey(@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] overlayDirs,
@@ -85,6 +84,18 @@
mHash = hash;
}
+ @UnsupportedAppUsage
+ public ResourcesKey(@Nullable String resDir,
+ @Nullable String[] splitResDirs,
+ @Nullable String[] overlayDirs,
+ @Nullable String[] libDirs,
+ int displayId,
+ @Nullable Configuration overrideConfig,
+ @Nullable CompatibilityInfo compatInfo) {
+ this(resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfig, compatInfo,
+ null);
+ }
+
public boolean hasOverrideConfiguration() {
return !Configuration.EMPTY.equals(mOverrideConfiguration);
}
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 4246b84..34cc856 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -16,6 +16,7 @@
package android.database;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentValues;
@@ -1548,4 +1549,24 @@
}
return -1;
}
+
+ /**
+ * Escape the given argument for use in a {@code LIKE} statement.
+ * @hide
+ */
+ public static String escapeForLike(@NonNull String arg) {
+ // Shamelessly borrowed from com.android.providers.media.util.DatabaseUtils
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < arg.length(); i++) {
+ final char c = arg.charAt(i);
+ switch (c) {
+ case '%': sb.append('\\');
+ break;
+ case '_': sb.append('\\');
+ break;
+ }
+ sb.append(c);
+ }
+ return sb.toString();
+ }
}
diff --git a/core/java/android/debug/AdbManagerInternal.java b/core/java/android/debug/AdbManagerInternal.java
index 0bd9f19..d730129 100644
--- a/core/java/android/debug/AdbManagerInternal.java
+++ b/core/java/android/debug/AdbManagerInternal.java
@@ -53,4 +53,14 @@
* Returns the file that contains all of the ADB keys and their last used time.
*/
public abstract File getAdbTempKeysFile();
+
+ /**
+ * Starts adbd for a transport.
+ */
+ public abstract void startAdbdForTransport(byte transportType);
+
+ /**
+ * Stops adbd for a transport.
+ */
+ public abstract void stopAdbdForTransport(byte transportType);
}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 743ce7b..85ef4a3 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -131,9 +131,17 @@
}
/**
- * Return the list of combinations of currently connected camera devices identifiers, which
+ * Return the set of combinations of currently connected camera device identifiers, which
* support configuring camera device sessions concurrently.
*
+ * <p>The devices in these combinations can be concurrently configured by the same
+ * client camera application. Using these camera devices concurrently by two different
+ * applications is not guaranteed to be supported, however.</p>
+ *
+ * <p>Each device in a combination, is guaranteed to support stream combinations which may be
+ * obtained by querying {@link #getCameraCharacteristics} for the key
+ * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS}.</p>
+ *
* <p>The set of combinations may include camera devices that may be in use by other camera API
* clients.</p>
*
@@ -174,7 +182,7 @@
* to be used for exploring the entire space of supported concurrent stream combinations. The
* available mandatory concurrent stream combinations may be obtained by querying
* {@link #getCameraCharacteristics} for the key
- * SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS. </p>
+ * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS}. </p>
*
* <p>Note that session parameters will be ignored and calls to
* {@link SessionConfiguration#setSessionParameters} are not required.</p>
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
index ef76c62..bf641d7 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
@@ -28,6 +28,7 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Slog;
import android.util.Xml;
@@ -43,6 +44,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
/**
* Enrollment information about the different available keyphrases.
@@ -116,7 +118,12 @@
/**
* List of available keyphrases.
*/
- final private KeyphraseMetadata[] mKeyphrases;
+ private final KeyphraseMetadata[] mKeyphrases;
+
+ /**
+ * Set of UIDs associated with the detected enrollment applications.
+ */
+ private final Set<Integer> mEnrollmentApplicationUids;
/**
* Map between KeyphraseMetadata and the package name of the enrollment app that provides it.
@@ -136,11 +143,13 @@
mParseError = "No enrollment applications found";
mKeyphrasePackageMap = Collections.<KeyphraseMetadata, String>emptyMap();
mKeyphrases = null;
+ mEnrollmentApplicationUids = Collections.emptySet();
return;
}
List<String> parseErrors = new LinkedList<String>();
mKeyphrasePackageMap = new HashMap<KeyphraseMetadata, String>();
+ mEnrollmentApplicationUids = new ArraySet<>();
for (ResolveInfo ri : ris) {
try {
ApplicationInfo ai = pm.getApplicationInfo(
@@ -162,6 +171,7 @@
getKeyphraseMetadataFromApplicationInfo(pm, ai, parseErrors);
if (metadata != null) {
mKeyphrasePackageMap.put(metadata, ai.packageName);
+ mEnrollmentApplicationUids.add(ai.uid);
}
} catch (PackageManager.NameNotFoundException e) {
String error = "error parsing voice enrollment meta-data for "
@@ -372,9 +382,22 @@
return null;
}
+ /**
+ * Tests if the input UID matches a supported enrollment application.
+ *
+ * @param uid UID of the caller to test against.
+ * @return Returns true if input uid matches the uid of a supported enrollment application.
+ * False if not.
+ */
+ public boolean isUidSupportedEnrollmentApplication(int uid) {
+ Log.d(TAG, "isUidSupportedEnrollmentApplication: " + toString());
+ return mEnrollmentApplicationUids.contains(uid);
+ }
+
@Override
public String toString() {
- return "KeyphraseEnrollmentInfo [Keyphrases=" + mKeyphrasePackageMap.toString()
+ return "KeyphraseEnrollmentInfo [KeyphrasePackageMap=" + mKeyphrasePackageMap.toString()
+ + ", enrollmentApplicationUids=" + mEnrollmentApplicationUids.toString()
+ ", ParseError=" + mParseError + "]";
}
}
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 9327b24..e9de274 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -53,6 +53,7 @@
private static final int DO_FINISH_SESSION = 110;
private static final int DO_VIEW_CLICKED = 115;
private static final int DO_NOTIFY_IME_HIDDEN = 120;
+ private static final int DO_REMOVE_IME_SURFACE = 130;
@UnsupportedAppUsage
HandlerCaller mCaller;
@@ -136,6 +137,10 @@
mInputMethodSession.notifyImeHidden();
return;
}
+ case DO_REMOVE_IME_SURFACE: {
+ mInputMethodSession.removeImeSurface();
+ return;
+ }
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -184,6 +189,11 @@
}
@Override
+ public void removeImeSurface() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_IME_SURFACE));
+ }
+
+ @Override
public void updateCursor(Rect newCursor) {
mCaller.executeOrSendMessage(
mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor));
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index f0b1eaa..b52b437 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -219,22 +219,29 @@
case DO_REVOKE_SESSION:
inputMethod.revokeSession((InputMethodSession)msg.obj);
return;
- case DO_SHOW_SOFT_INPUT:
- SomeArgs args = (SomeArgs)msg.obj;
+ case DO_SHOW_SOFT_INPUT: {
+ final SomeArgs args = (SomeArgs)msg.obj;
inputMethod.showSoftInputWithToken(
msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1);
+ args.recycle();
return;
- case DO_HIDE_SOFT_INPUT:
- inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
+ }
+ case DO_HIDE_SOFT_INPUT: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ inputMethod.hideSoftInputWithToken(msg.arg1, (ResultReceiver) args.arg2,
+ (IBinder) args.arg1);
+ args.recycle();
return;
+ }
case DO_CHANGE_INPUTMETHOD_SUBTYPE:
inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj);
return;
case DO_CREATE_INLINE_SUGGESTIONS_REQUEST:
- args = (SomeArgs) msg.obj;
+ final SomeArgs args = (SomeArgs) msg.obj;
inputMethod.onCreateInlineSuggestionsRequest(
(InlineSuggestionsRequestInfo) args.arg1,
(IInlineSuggestionsRequestCallback) args.arg2);
+ args.recycle();
return;
}
@@ -380,9 +387,9 @@
@BinderThread
@Override
- public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
- flags, resultReceiver));
+ public void hideSoftInput(IBinder hideInputToken, int flags, ResultReceiver resultReceiver) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_HIDE_SOFT_INPUT,
+ flags, hideInputToken, resultReceiver));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 20a4ab3..27839e7 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -459,6 +459,16 @@
*/
private IBinder mCurShowInputToken;
+ /**
+ * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#hideSoftInput}
+ * The original app window token is passed from client app window.
+ * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique dummy
+ * token to identify this window.
+ * This dummy token is only valid for a single call to {@link InputMethodImpl#hideSoftInput},
+ * after which it is set {@code null} until next call.
+ */
+ private IBinder mCurHideInputToken;
+
final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
onComputeInsets(mTmpInsets);
if (isExtractViewShown()) {
@@ -500,6 +510,7 @@
public class InputMethodImpl extends AbstractInputMethodImpl {
private boolean mSystemCallingShowSoftInput;
+ private boolean mSystemCallingHideSoftInput;
/**
* {@inheritDoc}
@@ -636,11 +647,32 @@
/**
* {@inheritDoc}
+ * @hide
+ */
+ @MainThread
+ @Override
+ public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
+ IBinder hideInputToken) {
+ mSystemCallingHideSoftInput = true;
+ mCurHideInputToken = hideInputToken;
+ hideSoftInput(flags, resultReceiver);
+ mCurHideInputToken = null;
+ mSystemCallingHideSoftInput = false;
+ }
+
+ /**
+ * {@inheritDoc}
*/
@MainThread
@Override
public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
if (DEBUG) Log.v(TAG, "hideSoftInput()");
+ if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
+ && !mSystemCallingHideSoftInput) {
+ Log.e(TAG, "IME shouldn't call hideSoftInput on itself."
+ + " Use requestHideSelf(int) itself");
+ return;
+ }
final boolean wasVisible = mIsPreRendered
? mDecorViewVisible && mWindowVisible : isInputViewShown();
applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */);
@@ -738,6 +770,15 @@
public void setCurrentShowInputToken(IBinder showInputToken) {
mCurShowInputToken = showInputToken;
}
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public void setCurrentHideInputToken(IBinder hideInputToken) {
+ mCurHideInputToken = hideInputToken;
+ }
}
// TODO(b/137800469): Add detailed docs explaining the inline suggestions process.
@@ -814,6 +855,13 @@
onPreRenderedWindowVisibilityChanged(false /* setVisible */);
}
+ private void removeImeSurface() {
+ if (!mShowInputRequested && !mWindowVisible) {
+ // hiding a window removes its surface.
+ mWindow.hide();
+ }
+ }
+
private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition);
}
@@ -932,6 +980,14 @@
public final void notifyImeHidden() {
InputMethodService.this.notifyImeHidden();
}
+
+ /**
+ * Notify IME that surface can be now removed.
+ * @hide
+ */
+ public final void removeImeSurface() {
+ InputMethodService.this.removeImeSurface();
+ }
}
/**
@@ -2157,7 +2213,8 @@
if (!isVisibilityAppliedUsingInsetsConsumer()) {
return;
}
- mPrivOps.applyImeVisibility(mCurShowInputToken, setVisible);
+ mPrivOps.applyImeVisibility(setVisible
+ ? mCurShowInputToken : mCurHideInputToken, setVisible);
}
private boolean isVisibilityAppliedUsingInsetsConsumer() {
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
index 31c948a..ef138a0 100644
--- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
+++ b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
@@ -296,6 +296,12 @@
// no-op for multi-session since IME is responsible controlling navigation bar buttons.
reportNotSupported();
}
+
+ @Override
+ public void removeImeSurface() {
+ // no-op for multi-session
+ reportNotSupported();
+ }
}
private static final class MultiClientInputMethodSessionImpl
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 1a4dac7..f0b7b5f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -28,6 +28,7 @@
import android.util.Log;
import android.util.Size;
import android.util.SizeF;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -1827,6 +1828,179 @@
}
/**
+ * A map used by {@link #maybeWriteSquashed} to keep track of what parcelables have
+ * been seen, and what positions they were written. The value is the absolute position of
+ * each parcelable.
+ */
+ private ArrayMap<Parcelable, Integer> mWrittenSquashableParcelables;
+
+ private void ensureWrittenSquashableParcelables() {
+ if (mWrittenSquashableParcelables != null) {
+ return;
+ }
+ mWrittenSquashableParcelables = new ArrayMap<>();
+ }
+
+ private boolean mAllowSquashing = false;
+
+ /**
+ * Allow "squashing" writes in {@link #maybeWriteSquashed}. This allows subsequent calls to
+ * {@link #maybeWriteSquashed(Parcelable)} to "squash" the same instances into one in a Parcel.
+ *
+ * Typically, this method is called at the beginning of {@link Parcelable#writeToParcel}. The
+ * caller must retain the return value from this method and call {@link #restoreAllowSquashing}
+ * with it.
+ *
+ * See {@link #maybeWriteSquashed(Parcelable)} for the details.
+ *
+ * @see #restoreAllowSquashing(boolean)
+ * @see #maybeWriteSquashed(Parcelable)
+ * @see #readSquashed(SquashReadHelper)
+ *
+ * @hide
+ */
+ @TestApi
+ public boolean allowSquashing() {
+ boolean previous = mAllowSquashing;
+ mAllowSquashing = true;
+ return previous;
+ }
+
+ /**
+ * @see #allowSquashing()
+ * @hide
+ */
+ @TestApi
+ public void restoreAllowSquashing(boolean previous) {
+ mAllowSquashing = previous;
+ if (!mAllowSquashing) {
+ mWrittenSquashableParcelables = null;
+ }
+ }
+
+ private void resetSqaushingState() {
+ if (mAllowSquashing) {
+ Slog.wtf(TAG, "allowSquashing wasn't restored.");
+ }
+ mWrittenSquashableParcelables = null;
+ mReadSquashableParcelables = null;
+ mAllowSquashing = false;
+ }
+
+ /**
+ * A map used by {@link #readSquashed} to cache parcelables. It's a map from
+ * an absolute position in a Parcel to the parcelable stored at the position.
+ */
+ private ArrayMap<Integer, Parcelable> mReadSquashableParcelables;
+
+ private void ensureReadSquashableParcelables() {
+ if (mReadSquashableParcelables != null) {
+ return;
+ }
+ mReadSquashableParcelables = new ArrayMap<>();
+ }
+
+ /**
+ * Write a parcelable with "squash" -- that is, when the same instance is written to the
+ * same Parcelable multiple times, instead of writing the entire instance multiple times,
+ * only write it once, and in subsequent writes we'll only write the offset to the original
+ * object.
+ *
+ * This approach does not work of the resulting Parcel is copied with {@link #appendFrom} with
+ * a non-zero offset, so we do not enable this behavior by default. Instead, we only enable
+ * it between {@link #allowSquashing} and {@link #restoreAllowSquashing}, in order to make sure
+ * we only do so within each "top level" Parcelable.
+ *
+ * Usage: Use this method in {@link Parcelable#writeToParcel}.
+ * If this method returns TRUE, it's a subsequent call, and the offset is already written,
+ * so the caller doesn't have to do anything. If this method returns FALSE, it's the first
+ * time for the instance to be written to this parcel. The caller has to proceed with its
+ * {@link Parcelable#writeToParcel}.
+ *
+ * (See {@code ApplicationInfo} for the example.)
+ *
+ * @param p the target Parcelable to write.
+ *
+ * @see #allowSquashing()
+ * @see #restoreAllowSquashing(boolean)
+ * @see #readSquashed(SquashReadHelper)
+ *
+ * @hide
+ */
+ public boolean maybeWriteSquashed(@NonNull Parcelable p) {
+ if (!mAllowSquashing) {
+ // Don't squash, and don't put it in the map either.
+ writeInt(0);
+ return false;
+ }
+ ensureWrittenSquashableParcelables();
+ final Integer firstPos = mWrittenSquashableParcelables.get(p);
+ if (firstPos != null) {
+ // Already written.
+ // Write the relative offset from the current position to the first position.
+ final int pos = dataPosition();
+
+ // We want the offset from the next byte of this integer, so we need to +4.
+ writeInt(pos - firstPos + 4);
+ return true;
+ }
+ // First time seen, write a marker.
+ writeInt(0);
+
+ // Remember the position.
+ final int pos = dataPosition();
+ mWrittenSquashableParcelables.put(p, pos);
+
+ // Return false and let the caller actually write the content.
+ return false;
+ }
+
+ /**
+ * Helper function that's used by {@link #readSquashed(SquashReadHelper)}
+ * @hide
+ */
+ public interface SquashReadHelper<T> {
+ /** Read and instantiate {@code T} from a Parcel. */
+ @NonNull
+ T readRawParceled(@NonNull Parcel p);
+ }
+
+ /**
+ * Read a {@link Parcelable} that's written with {@link #maybeWriteSquashed}.
+ *
+ * @param reader a callback function that instantiates an instance from a parcel.
+ * Typicallly, a lambda to the instructor that takes a {@link Parcel} is passed.
+ *
+ * @see #maybeWriteSquashed(Parcelable)
+ *
+ * @hide
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable
+ public <T extends Parcelable> T readSquashed(SquashReadHelper<T> reader) {
+ final int offset = readInt();
+ final int pos = dataPosition();
+
+ if (offset == 0) {
+ // First time read. Unparcel, and remember it.
+ final T p = reader.readRawParceled(this);
+ ensureReadSquashableParcelables();
+ mReadSquashableParcelables.put(pos, p);
+ return p;
+ }
+ // Subsequent read.
+ final int firstAbsolutePos = pos - offset;
+
+ final Parcelable p = mReadSquashableParcelables.get(firstAbsolutePos);
+ if (p == null) {
+ Slog.wtfStack(TAG, "Map doesn't contain offset "
+ + firstAbsolutePos
+ + " : contains=" + new ArrayList<>(mReadSquashableParcelables.keySet()));
+ }
+ return (T) p;
+ }
+
+ /**
* Write a generic serializable object in to a Parcel. It is strongly
* recommended that this method be avoided, since the serialization
* overhead is extremely large, and this approach will be much slower than
@@ -3247,6 +3421,7 @@
}
private void freeBuffer() {
+ resetSqaushingState();
if (mOwnsNativeParcelObject) {
updateNativeSize(nativeFreeBuffer(mNativePtr));
}
@@ -3254,6 +3429,7 @@
}
private void destroy() {
+ resetSqaushingState();
if (mNativePtr != 0) {
if (mOwnsNativeParcelObject) {
nativeDestroy(mNativePtr);
@@ -3261,7 +3437,6 @@
}
mNativePtr = 0;
}
- mReadWriteHelper = null;
}
@Override
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 5cb3361..50d8d80 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -96,9 +96,6 @@
private static final String TAG = "DynSystemClient";
- private static final long DEFAULT_USERDATA_SIZE = (10L << 30);
-
-
/** Listener for installation status updates. */
public interface OnStatusChangedListener {
/**
@@ -386,7 +383,7 @@
@SystemApi
@TestApi
public void start(@NonNull Uri systemUrl, @BytesLong long systemSize) {
- start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
+ start(systemUrl, systemSize, 0 /* Use the default userdata size */);
}
/**
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 5d6dc7b..0bd211d 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -561,21 +561,24 @@
private static final class PackageNamePermissionQuery {
final String permName;
final String pkgName;
+ final int uid;
- PackageNamePermissionQuery(@Nullable String permName, @Nullable String pkgName) {
+ PackageNamePermissionQuery(@Nullable String permName, @Nullable String pkgName, int uid) {
this.permName = permName;
this.pkgName = pkgName;
+ this.uid = uid;
}
@Override
public String toString() {
- return String.format("PackageNamePermissionQuery(pkgName=\"%s\", permName=\"%s\")",
- pkgName, permName);
+ return String.format(
+ "PackageNamePermissionQuery(pkgName=\"%s\", permName=\"%s, uid=%s\")",
+ pkgName, permName, uid);
}
@Override
public int hashCode() {
- return Objects.hashCode(permName) * 13 + Objects.hashCode(pkgName);
+ return Objects.hash(permName, pkgName, uid);
}
@Override
@@ -590,15 +593,17 @@
return false;
}
return Objects.equals(permName, other.permName)
- && Objects.equals(pkgName, other.pkgName);
+ && Objects.equals(pkgName, other.pkgName)
+ && uid == other.uid;
}
}
/* @hide */
- private static int checkPackageNamePermissionUncached(String permName, String pkgName) {
+ private static int checkPackageNamePermissionUncached(
+ String permName, String pkgName, int uid) {
try {
return ActivityThread.getPermissionManager().checkPermission(
- permName, pkgName, UserHandle.myUserId());
+ permName, pkgName, uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -611,7 +616,8 @@
16, CACHE_KEY_PACKAGE_INFO) {
@Override
protected Integer recompute(PackageNamePermissionQuery query) {
- return checkPackageNamePermissionUncached(query.permName, query.pkgName);
+ return checkPackageNamePermissionUncached(
+ query.permName, query.pkgName, query.uid);
}
};
@@ -620,9 +626,9 @@
*
* @hide
*/
- public static int checkPackageNamePermission(String permName, String pkgName) {
+ public static int checkPackageNamePermission(String permName, String pkgName, int uid) {
return sPackageNamePermissionCache.query(
- new PackageNamePermissionQuery(permName, pkgName));
+ new PackageNamePermissionQuery(permName, pkgName, uid));
}
/**
diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
index c389b1a..dd434b4 100644
--- a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
@@ -28,5 +28,5 @@
oneway interface IInlineSuggestionRenderService {
void renderSuggestion(in IInlineSuggestionUiCallback callback,
in InlinePresentation presentation, int width, int height,
- in IBinder hostInputToken);
+ in IBinder hostInputToken, int displayId);
}
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index fb8406e..a9addba 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -17,6 +17,7 @@
package android.service.autofill;
import android.annotation.NonNull;
+import android.annotation.Size;
import android.app.slice.Slice;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,6 +25,8 @@
import com.android.internal.util.DataClass;
+import java.util.List;
+
/**
* Wrapper class holding a {@link Slice} and an {@link InlinePresentationSpec} for rendering UI
* for an Inline Suggestion.
@@ -50,6 +53,18 @@
*/
private final boolean mPinned;
+ /**
+ * Returns the autofill hints set in the slice.
+ *
+ * @hide
+ */
+ @NonNull
+ @Size(min = 0)
+ public String[] getAutofillHints() {
+ List<String> hints = mSlice.getHints();
+ return hints.toArray(new String[hints.size()]);
+ }
+
// Code below generated by codegen v1.0.14.
@@ -214,10 +229,10 @@
};
@DataClass.Generated(
- time = 1579726472535L,
+ time = 1582753782651L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
- inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
+ inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 29069e7..fcdefac 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -31,6 +31,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
+import android.view.Display;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
@@ -61,7 +62,8 @@
private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
private void handleRenderSuggestion(IInlineSuggestionUiCallback callback,
- InlinePresentation presentation, int width, int height, IBinder hostInputToken) {
+ InlinePresentation presentation, int width, int height, IBinder hostInputToken,
+ int displayId) {
if (hostInputToken == null) {
try {
callback.onError();
@@ -70,26 +72,46 @@
}
return;
}
- final SurfaceControlViewHost host = new SurfaceControlViewHost(this, this.getDisplay(),
- hostInputToken);
- final SurfaceControl surface = host.getSurfacePackage().getSurfaceControl();
- final View suggestionView = onRenderSuggestion(presentation, width, height);
-
- final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(this, callback);
- suggestionRoot.addView(suggestionView);
- suggestionRoot.setOnClickListener((v) -> {
- try {
- callback.onAutofill();
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException calling onAutofill()");
+ // When we create the UI it should be for the IME display
+ updateDisplay(displayId);
+ try {
+ final View suggestionView = onRenderSuggestion(presentation, width, height);
+ if (suggestionView == null) {
+ try {
+ callback.onError();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Null suggestion view returned by renderer");
+ }
+ return;
}
- });
- WindowManager.LayoutParams lp =
- new WindowManager.LayoutParams(width, height,
- WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
- host.addView(suggestionRoot, lp);
+ final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(this, callback);
+ suggestionRoot.addView(suggestionView);
+ WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(width, height,
+ WindowManager.LayoutParams.TYPE_APPLICATION, 0,
+ PixelFormat.TRANSPARENT);
+
+ final SurfaceControlViewHost host = new SurfaceControlViewHost(this, getDisplay(),
+ hostInputToken);
+ host.addView(suggestionRoot, lp);
+ suggestionRoot.setOnClickListener((v) -> {
+ try {
+ callback.onAutofill();
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onAutofill()");
+ }
+ });
+
+ sendResult(callback, host.getSurfacePackage().getSurfaceControl());
+ } finally {
+ updateDisplay(Display.DEFAULT_DISPLAY);
+ }
+ }
+
+ private void sendResult(@NonNull IInlineSuggestionUiCallback callback,
+ @Nullable SurfaceControl surface) {
try {
callback.onContent(surface);
} catch (RemoteException e) {
@@ -105,11 +127,11 @@
@Override
public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
@NonNull InlinePresentation presentation, int width, int height,
- @Nullable IBinder hostInputToken) {
+ @Nullable IBinder hostInputToken, int displayId) {
mHandler.sendMessage(obtainMessage(
InlineSuggestionRenderService::handleRenderSuggestion,
InlineSuggestionRenderService.this, callback, presentation,
- width, height, hostInputToken));
+ width, height, hostInputToken, displayId));
}
}.asBinder();
}
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index 68ba63a..b7fdf5a 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -21,6 +21,7 @@
import android.annotation.TestApi;
import android.os.Bundle;
import android.service.autofill.Dataset;
+import android.service.autofill.InlinePresentation;
import com.android.internal.util.DataClass;
@@ -52,6 +53,13 @@
private @Nullable List<Dataset> mInlineSuggestions;
/**
+ * The {@link InlinePresentation}s representing the inline actions. Defaults to null if no
+ * inline actions are provided.
+ */
+ @DataClass.PluralOf("inlineAction")
+ private @Nullable List<InlinePresentation> mInlineActions;
+
+ /**
* The client state that {@link AugmentedAutofillService} implementation can put anything in to
* identify the request and the response when calling
* {@link AugmentedAutofillService#getFillEventHistory()}.
@@ -66,6 +74,10 @@
return null;
}
+ private static List<InlinePresentation> defaultInlineActions() {
+ return null;
+ }
+
private static Bundle defaultClientState() {
return null;
}
@@ -74,6 +86,7 @@
/** @hide */
abstract static class BaseBuilder {
abstract FillResponse.Builder addInlineSuggestion(@NonNull Dataset value);
+ abstract FillResponse.Builder addInlineAction(@NonNull InlinePresentation value);
}
@@ -95,9 +108,11 @@
/* package-private */ FillResponse(
@Nullable FillWindow fillWindow,
@Nullable List<Dataset> inlineSuggestions,
+ @Nullable List<InlinePresentation> inlineActions,
@Nullable Bundle clientState) {
this.mFillWindow = fillWindow;
this.mInlineSuggestions = inlineSuggestions;
+ this.mInlineActions = inlineActions;
this.mClientState = clientState;
// onConstructed(); // You can define this method to get a callback
@@ -125,6 +140,17 @@
}
/**
+ * The {@link InlinePresentation}s representing the inline actions. Defaults to null if no
+ * inline actions are provided.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable List<InlinePresentation> getInlineActions() {
+ return mInlineActions;
+ }
+
+ /**
* The client state that {@link AugmentedAutofillService} implementation can put anything in to
* identify the request and the response when calling
* {@link AugmentedAutofillService#getFillEventHistory()}.
@@ -145,6 +171,7 @@
private @Nullable FillWindow mFillWindow;
private @Nullable List<Dataset> mInlineSuggestions;
+ private @Nullable List<InlinePresentation> mInlineActions;
private @Nullable Bundle mClientState;
private long mBuilderFieldsSet = 0L;
@@ -185,6 +212,27 @@
}
/**
+ * The {@link InlinePresentation}s representing the inline actions. Defaults to null if no
+ * inline actions are provided.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setInlineActions(@Nullable List<InlinePresentation> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mInlineActions = value;
+ return this;
+ }
+
+ /** @see #setInlineActions */
+ @DataClass.Generated.Member
+ @Override
+ @NonNull FillResponse.Builder addInlineAction(@NonNull InlinePresentation value) {
+ if (mInlineActions == null) setInlineActions(new ArrayList<>());
+ mInlineActions.add(value);
+ return this;
+ }
+
+ /**
* The client state that {@link AugmentedAutofillService} implementation can put anything in to
* identify the request and the response when calling
* {@link AugmentedAutofillService#getFillEventHistory()}.
@@ -192,7 +240,7 @@
@DataClass.Generated.Member
public @NonNull Builder setClientState(@Nullable Bundle value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x4;
+ mBuilderFieldsSet |= 0x8;
mClientState = value;
return this;
}
@@ -200,7 +248,7 @@
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull FillResponse build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x8; // Mark builder used
+ mBuilderFieldsSet |= 0x10; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mFillWindow = defaultFillWindow();
@@ -209,17 +257,21 @@
mInlineSuggestions = defaultInlineSuggestions();
}
if ((mBuilderFieldsSet & 0x4) == 0) {
+ mInlineActions = defaultInlineActions();
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
mClientState = defaultClientState();
}
FillResponse o = new FillResponse(
mFillWindow,
mInlineSuggestions,
+ mInlineActions,
mClientState);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x8) != 0) {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -227,10 +279,10 @@
}
@DataClass.Generated(
- time = 1580335256422L,
+ time = 1582682935951L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/service/autofill/augmented/FillResponse.java",
- inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineAction\") @android.annotation.Nullable java.util.List<android.service.autofill.InlinePresentation> mInlineActions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static java.util.List<android.service.autofill.InlinePresentation> defaultInlineActions()\nprivate static android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineAction(android.service.autofill.InlinePresentation)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
index de90b94..2a809b1 100644
--- a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
+++ b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -47,7 +46,6 @@
* CarrierMessagingService.
* @hide
*/
-@SystemApi
public abstract class CarrierMessagingServiceWrapper {
// Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
// prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
@@ -64,7 +62,6 @@
* @return true upon successfully binding to a carrier messaging service, false otherwise
* @hide
*/
- @SystemApi
public boolean bindToCarrierMessagingService(@NonNull Context context,
@NonNull String carrierPackageName) {
Preconditions.checkState(mCarrierMessagingServiceConnection == null);
@@ -82,7 +79,6 @@
* @param context the context
* @hide
*/
- @SystemApi
public void disposeConnection(@NonNull Context context) {
Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
context.unbindService(mCarrierMessagingServiceConnection);
@@ -93,7 +89,6 @@
* Implemented by subclasses to use the carrier messaging service once it is ready.
* @hide
*/
- @SystemApi
public abstract void onServiceReady();
/**
@@ -117,7 +112,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void filterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort,
int subId, @NonNull final CarrierMessagingCallbackWrapper callback) {
if (mICarrierMessagingService != null) {
@@ -142,7 +136,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void sendTextSms(@NonNull String text, int subId, @NonNull String destAddress,
int sendSmsFlag, @NonNull final CarrierMessagingCallbackWrapper callback) {
if (mICarrierMessagingService != null) {
@@ -168,7 +161,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void sendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress,
int destPort, int sendSmsFlag,
@NonNull final CarrierMessagingCallbackWrapper callback) {
@@ -194,7 +186,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void sendMultipartTextSms(@NonNull List<String> parts, int subId,
@NonNull String destAddress, int sendSmsFlag,
@NonNull final CarrierMessagingCallbackWrapper callback) {
@@ -220,7 +211,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void sendMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
@NonNull final CarrierMessagingCallbackWrapper callback) {
if (mICarrierMessagingService != null) {
@@ -244,7 +234,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void downloadMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
@NonNull final CarrierMessagingCallbackWrapper callback) {
if (mICarrierMessagingService != null) {
@@ -276,7 +265,6 @@
* {@link CarrierMessagingServiceWrapper}.
* @hide
*/
- @SystemApi
public abstract static class CarrierMessagingCallbackWrapper {
/**
@@ -289,7 +277,6 @@
* {@see CarrierMessagingService#onReceiveTextSms}.
* @hide
*/
- @SystemApi
public void onFilterComplete(int result) {
}
@@ -304,7 +291,6 @@
* only if result is {@link CarrierMessagingService#SEND_STATUS_OK}.
* @hide
*/
- @SystemApi
public void onSendSmsComplete(int result, int messageRef) {
}
@@ -319,7 +305,6 @@
* {@link CarrierMessagingService#SEND_STATUS_OK}.
* @hide
*/
- @SystemApi
public void onSendMultipartSmsComplete(int result, @Nullable int[] messageRefs) {
}
@@ -334,7 +319,6 @@
* {@link CarrierMessagingService#SEND_STATUS_OK}.
* @hide
*/
- @SystemApi
public void onSendMmsComplete(int result, @Nullable byte[] sendConfPdu) {
}
@@ -346,7 +330,6 @@
* and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
* @hide
*/
- @SystemApi
public void onDownloadMmsComplete(int result) {
}
diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java
index 2d1d0ed..0cffe71 100644
--- a/core/java/android/service/controls/Control.java
+++ b/core/java/android/service/controls/Control.java
@@ -394,6 +394,11 @@
return this;
}
+ /**
+ * @param deviceType the device type for the {@link Control}. Setting an invalid value not
+ * in {@link DeviceTypes} will set it to {@link DeviceTypes#TYPE_UNKNOWN}.
+ * @return {@code this}
+ */
@NonNull
public StatelessBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
if (!DeviceTypes.validDeviceType(deviceType)) {
@@ -416,6 +421,10 @@
return this;
}
+ /**
+ * @param subtitle the user facing subtitle for the {@link Control}
+ * @return {@code this}
+ */
@NonNull
public StatelessBuilder setSubtitle(@NonNull CharSequence subtitle) {
Preconditions.checkNotNull(subtitle);
@@ -423,12 +432,22 @@
return this;
}
+ /**
+ * @param structure the user facing name of the structure for the {@link Control}.
+ * {@code null} indicates that it's not associated with any structure.
+ * @return {@code this}
+ */
@NonNull
public StatelessBuilder setStructure(@Nullable CharSequence structure) {
mStructure = structure;
return this;
}
+ /**
+ * @param zone the user facing name of the zone for the {@link Control}. {@code null}
+ * indicates that it's not associated with any zone.
+ * @return {@code this}
+ */
@NonNull
public StatelessBuilder setZone(@Nullable CharSequence zone) {
mZone = zone;
@@ -446,12 +465,20 @@
return this;
}
+ /**
+ * @param customIcon an {@link Icon} to override the one determined by the device type.
+ * @return {@code this}
+ */
@NonNull
public StatelessBuilder setCustomIcon(@Nullable Icon customIcon) {
mCustomIcon = customIcon;
return this;
}
+ /**
+ * @param customColor a list of colors to override the ones determined by the device type.
+ * @return {@code this}
+ */
@NonNull
public StatelessBuilder setCustomColor(@Nullable ColorStateList customColor) {
mCustomColor = customColor;
@@ -459,7 +486,7 @@
}
/**
- * Build a {@link Control}
+ * Build a stateless {@link Control}
* @return a valid {@link Control}
*/
@NonNull
@@ -482,7 +509,7 @@
/**
* Builder class for {@link Control}.
*
- * This class facilitates the creation of {@link Control}.
+ * This class facilitates the creation of {@link Control} with an associated state.
* It provides the following defaults for non-optional parameters:
* <ul>
* <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
@@ -551,6 +578,11 @@
return this;
}
+ /**
+ * @param deviceType the device type for the {@link Control}. Setting an invalid value not
+ * in {@link DeviceTypes} will set it to {@link DeviceTypes#TYPE_UNKNOWN}.
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
if (!DeviceTypes.validDeviceType(deviceType)) {
@@ -573,6 +605,10 @@
return this;
}
+ /**
+ * @param subtitle the user facing subtitle for the {@link Control}
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setSubtitle(@NonNull CharSequence subtitle) {
Preconditions.checkNotNull(subtitle);
@@ -580,12 +616,22 @@
return this;
}
+ /**
+ * @param structure the user facing name of the structure for the {@link Control}.
+ * {@code null} indicates that it's not associated with any structure.
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setStructure(@Nullable CharSequence structure) {
mStructure = structure;
return this;
}
+ /**
+ * @param zone the user facing name of the zone for the {@link Control}. {@code null}
+ * indicates that it's not associated with any zone.
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setZone(@Nullable CharSequence zone) {
mZone = zone;
@@ -603,18 +649,31 @@
return this;
}
+ /**
+ * @param customIcon an {@link Icon} to override the one determined by the device type.
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setCustomIcon(@Nullable Icon customIcon) {
mCustomIcon = customIcon;
return this;
}
+ /**
+ * @param customColor a list of colors to override the ones determined by the device type.
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setCustomColor(@Nullable ColorStateList customColor) {
mCustomColor = customColor;
return this;
}
+ /**
+ * @param status the status of the {@link Control}. Setting an invalid value not in
+ * {@link Control} will set it to {@link Control#STATUS_UNKNOWN}.
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setStatus(@Status int status) {
if (status < 0 || status >= NUM_STATUS) {
@@ -626,6 +685,10 @@
return this;
}
+ /**
+ * @param controlTemplate a template for the {@link Control}
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setControlTemplate(@NonNull ControlTemplate controlTemplate) {
Preconditions.checkNotNull(controlTemplate);
@@ -633,6 +696,10 @@
return this;
}
+ /**
+ * @param statusText a user facing text representing the status of the {@link Control}.
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setStatusText(@NonNull CharSequence statusText) {
Preconditions.checkNotNull(statusText);
@@ -640,6 +707,10 @@
return this;
}
+ /**
+ * Build a stateless {@link Control}
+ * @return a valid {@link Control}
+ */
@NonNull
public Control build() {
return new Control(mControlId,
diff --git a/core/java/android/service/controls/DeviceTypes.java b/core/java/android/service/controls/DeviceTypes.java
index 8dbb9cf..6594d2c 100644
--- a/core/java/android/service/controls/DeviceTypes.java
+++ b/core/java/android/service/controls/DeviceTypes.java
@@ -21,6 +21,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+/**
+ * Device types for {@link Control}.
+ */
public class DeviceTypes {
// Update this when adding new concrete types. Does not count TYPE_UNKNOWN
diff --git a/core/java/android/service/controls/actions/BooleanAction.java b/core/java/android/service/controls/actions/BooleanAction.java
index 0259335..b794ead 100644
--- a/core/java/android/service/controls/actions/BooleanAction.java
+++ b/core/java/android/service/controls/actions/BooleanAction.java
@@ -19,10 +19,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
+import android.service.controls.Control;
+import android.service.controls.templates.ToggleRangeTemplate;
import android.service.controls.templates.ToggleTemplate;
/**
- * Action sent by a {@link ToggleTemplate}
+ * Action sent by user toggling a {@link Control} between checked/unchecked.
+ *
+ * This action is available when the {@link Control} was constructed with either a
+ * {@link ToggleTemplate} or a {@link ToggleRangeTemplate}.
*/
public final class BooleanAction extends ControlAction {
@@ -40,8 +45,8 @@
}
/**
- * @param templateId the identifier of the {@link ToggleTemplate} that originated this action.
- * @param newState new value for the state displayed by the {@link ToggleTemplate}.
+ * @param templateId the identifier of the template that originated this action.
+ * @param newState new value for the state displayed by the template.
* @param challengeValue a value sent by the user along with the action to authenticate. {@code}
* null is sent when no authentication is needed or has not been
* requested.
@@ -64,8 +69,7 @@
/**
* The new state set for the button in the corresponding {@link ToggleTemplate}.
*
- * @return {@code true} if the button was toggled from an {@code off} state to an {@code on}
- * state.
+ * @return {@code true} if the button was toggled from unchecked to checked.
*/
public boolean getNewState() {
return mNewState;
diff --git a/core/java/android/service/controls/actions/CommandAction.java b/core/java/android/service/controls/actions/CommandAction.java
index 84d6080..a560fa4 100644
--- a/core/java/android/service/controls/actions/CommandAction.java
+++ b/core/java/android/service/controls/actions/CommandAction.java
@@ -19,15 +19,32 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
+import android.service.controls.Control;
+import android.service.controls.templates.StatelessTemplate;
+/**
+ * A simple {@link ControlAction} indicating that the user has interacted with a {@link Control}
+ * created using a {@link StatelessTemplate}.
+ */
public final class CommandAction extends ControlAction {
private static final @ActionType int TYPE = TYPE_COMMAND;
+ /**
+ * @param templateId the identifier of the {@link StatelessTemplate} that originated this
+ * action.
+ * @param challengeValue a value sent by the user along with the action to authenticate. {@code}
+ * null is sent when no authentication is needed or has not been
+ * requested.
+ */
public CommandAction(@NonNull String templateId, @Nullable String challengeValue) {
super(templateId, challengeValue);
}
+ /**
+ * @param templateId the identifier of the {@link StatelessTemplate} that originated this
+ * action.
+ */
public CommandAction(@NonNull String templateId) {
this(templateId, null);
}
@@ -40,6 +57,9 @@
super(b);
}
+ /**
+ * @return {@link ControlAction#TYPE_COMMAND}
+ */
@Override
public int getActionType() {
return TYPE;
diff --git a/core/java/android/service/controls/actions/ControlAction.java b/core/java/android/service/controls/actions/ControlAction.java
index 4141da8..45e63d7 100644
--- a/core/java/android/service/controls/actions/ControlAction.java
+++ b/core/java/android/service/controls/actions/ControlAction.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
+import android.service.controls.Control;
import android.service.controls.IControlsActionCallback;
import android.service.controls.templates.ControlTemplate;
import android.util.Log;
@@ -31,7 +32,7 @@
import java.lang.annotation.RetentionPolicy;
/**
- * An abstract action that is executed from a {@link ControlTemplate}.
+ * An abstract action indicating a user interaction with a {@link Control}.
*
* The action may have a value to authenticate the input, when the provider has requested it to
* complete the action.
@@ -58,6 +59,9 @@
})
public @interface ActionType {};
+ /**
+ * Object returned when there is an unparcelling error.
+ */
public static final @NonNull ControlAction ERROR_ACTION = new ControlAction() {
@Override
public int getActionType() {
@@ -65,6 +69,9 @@
}
};
+ /**
+ * The identifier of {@link #ERROR_ACTION}
+ */
public static final @ActionType int TYPE_ERROR = -1;
/**
@@ -77,10 +84,19 @@
*/
public static final @ActionType int TYPE_FLOAT = 2;
+ /**
+ * The identifier of {@link MultiFloatAction}.
+ */
public static final @ActionType int TYPE_MULTI_FLOAT = 3;
+ /**
+ * The identifier of {@link ModeAction}.
+ */
public static final @ActionType int TYPE_MODE = 4;
+ /**
+ * The identifier of {@link CommandAction}.
+ */
public static final @ActionType int TYPE_COMMAND = 5;
diff --git a/core/java/android/service/controls/actions/ModeAction.java b/core/java/android/service/controls/actions/ModeAction.java
index ca40974..c0e24ad 100644
--- a/core/java/android/service/controls/actions/ModeAction.java
+++ b/core/java/android/service/controls/actions/ModeAction.java
@@ -19,7 +19,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
+import android.service.controls.Control;
+import android.service.controls.templates.TemperatureControlTemplate;
+/**
+ * Action sent by the user to indicate a change of mode.
+ *
+ * This action is available when the {@link Control} was created with a
+ * {@link TemperatureControlTemplate}.
+ */
public final class ModeAction extends ControlAction {
private static final @ActionType int TYPE = TYPE_MODE;
@@ -27,16 +35,32 @@
private final int mNewMode;
+ /**
+ * @return {@link ControlAction#TYPE_MODE}.
+ */
@Override
public int getActionType() {
return TYPE;
}
+ /**
+ * @param templateId the identifier of the {@link TemperatureControlTemplate} that originated
+ * this action.
+ * @param newMode new value for the mode.
+ * @param challengeValue a value sent by the user along with the action to authenticate. {@code}
+ * null is sent when no authentication is needed or has not been
+ * requested.
+ */
public ModeAction(@NonNull String templateId, int newMode, @Nullable String challengeValue) {
super(templateId, challengeValue);
mNewMode = newMode;
}
+ /**
+ * @param templateId the identifier of the {@link TemperatureControlTemplate} that originated
+ * this action.
+ * @param newMode new value for the mode.
+ */
public ModeAction(@NonNull String templateId, int newMode) {
this(templateId, newMode, null);
}
diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java
index a5156e3..30efd80 100644
--- a/core/java/android/service/controls/templates/ControlTemplate.java
+++ b/core/java/android/service/controls/templates/ControlTemplate.java
@@ -57,6 +57,9 @@
}
};
+ /**
+ * Object returned when there is an unparcelling error.
+ */
public static final @NonNull ControlTemplate ERROR_TEMPLATE = new ControlTemplate("") {
@Override
public int getTemplateType() {
@@ -80,6 +83,9 @@
})
public @interface TemplateType {}
+ /**
+ * Type identifier of {@link #ERROR_TEMPLATE}.
+ */
public static final @TemplateType int TYPE_ERROR = -1;
/**
@@ -102,10 +108,19 @@
*/
public static final @TemplateType int TYPE_THUMBNAIL = 3;
+ /**
+ * Type identifier of {@link ToggleRangeTemplate}.
+ */
public static final @TemplateType int TYPE_TOGGLE_RANGE = 6;
+ /**
+ * Type identifier of {@link TemperatureControlTemplate}.
+ */
public static final @TemplateType int TYPE_TEMPERATURE = 7;
+ /**
+ * Type identifier of {@link StatelessTemplate}.
+ */
public static final @TemplateType int TYPE_STATELESS = 8;
private @NonNull final String mTemplateId;
diff --git a/core/java/android/service/controls/templates/RangeTemplate.java b/core/java/android/service/controls/templates/RangeTemplate.java
index fe0d167..0d977d3 100644
--- a/core/java/android/service/controls/templates/RangeTemplate.java
+++ b/core/java/android/service/controls/templates/RangeTemplate.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
-import android.os.Parcel;
import android.service.controls.Control;
import android.service.controls.actions.FloatAction;
@@ -85,7 +84,7 @@
}
/**
- * Construct a new {@link RangeTemplate} from a {@link Parcel}.
+ * Construct a new {@link RangeTemplate} from a {@link Bundle}.
*
* @throws IllegalArgumentException if the parameters passed do not make a valid range
* @see RangeTemplate#RangeTemplate(String, float, float, float, float, CharSequence)
diff --git a/core/java/android/service/controls/templates/StatelessTemplate.java b/core/java/android/service/controls/templates/StatelessTemplate.java
index 3f98bea..c052412 100644
--- a/core/java/android/service/controls/templates/StatelessTemplate.java
+++ b/core/java/android/service/controls/templates/StatelessTemplate.java
@@ -18,22 +18,36 @@
import android.annotation.NonNull;
import android.os.Bundle;
+import android.service.controls.Control;
+import android.service.controls.actions.CommandAction;
+/**
+ * A template for a {@link Control} which has no state.
+ *
+ * @see CommandAction
+ */
public final class StatelessTemplate extends ControlTemplate {
+ /**
+ * @return {@link ControlTemplate#TYPE_STATELESS}
+ */
@Override
public int getTemplateType() {
return TYPE_STATELESS;
}
/**
- * @param b
+ * Construct a new {@link StatelessTemplate} from a {@link Bundle}
* @hide
*/
StatelessTemplate(@NonNull Bundle b) {
super(b);
}
+ /**
+ * Construct a new {@link StatelessTemplate}
+ * @param templateId the identifier for this template
+ */
public StatelessTemplate(@NonNull String templateId) {
super(templateId);
}
diff --git a/core/java/android/service/controls/templates/TemperatureControlTemplate.java b/core/java/android/service/controls/templates/TemperatureControlTemplate.java
index 9d8dca62..0818c7e 100644
--- a/core/java/android/service/controls/templates/TemperatureControlTemplate.java
+++ b/core/java/android/service/controls/templates/TemperatureControlTemplate.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Bundle;
+import android.service.controls.Control;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -26,6 +27,13 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+/**
+ * A template for a temperature related {@link Control} that supports multiple modes.
+ *
+ * Both the current mode and the active mode for the control can be specified. The combination of
+ * the {@link Control#getDeviceType} and the current and active mode will determine colors and
+ * transitions for the UI element.
+ */
public final class TemperatureControlTemplate extends ControlTemplate {
private static final String TAG = "ThermostatTemplate";
@@ -51,6 +59,7 @@
public @interface Mode {}
private static final int NUM_MODES = 6;
+
public static final @Mode int MODE_UNKNOWN = 0;
public static final @Mode int MODE_OFF = 1;
@@ -102,6 +111,18 @@
private final @Mode int mCurrentActiveMode;
private final @ModeFlag int mModes;
+ /**
+ * Construct a new {@link TemperatureControlTemplate}.
+ *
+ * The current and active mode have to be among the ones supported by the flags.
+ *
+ * @param templateId the identifier for this template object
+ * @param controlTemplate a template to use for interaction with the user
+ * @param currentMode the current mode for the {@link Control}
+ * @param currentActiveMode the current active mode for the {@link Control}
+ * @param modesFlag a flag representing the available modes for the {@link Control}
+ * @throws IllegalArgumentException if the parameters passed do not make a valid template.
+ */
public TemperatureControlTemplate(@NonNull String templateId,
@NonNull ControlTemplate controlTemplate,
@Mode int currentMode,
@@ -179,6 +200,9 @@
return mModes;
}
+ /**
+ * @return {@link ControlTemplate#TYPE_TEMPERATURE}
+ */
@Override
public int getTemplateType() {
return TYPE;
diff --git a/core/java/android/service/controls/templates/ToggleRangeTemplate.java b/core/java/android/service/controls/templates/ToggleRangeTemplate.java
index af43b94..cd6a2fc 100644
--- a/core/java/android/service/controls/templates/ToggleRangeTemplate.java
+++ b/core/java/android/service/controls/templates/ToggleRangeTemplate.java
@@ -18,9 +18,16 @@
import android.annotation.NonNull;
import android.os.Bundle;
+import android.service.controls.Control;
import com.android.internal.util.Preconditions;
+/**
+ * A template for a {@link Control} supporting toggling and a range.
+ *
+ * @see ToggleTemplate
+ * @see RangeTemplate
+ */
public final class ToggleRangeTemplate extends ControlTemplate {
private static final @TemplateType int TYPE = TYPE_TOGGLE_RANGE;
@@ -40,6 +47,12 @@
mRangeTemplate = new RangeTemplate(b.getBundle(KEY_RANGE));
}
+ /**
+ * Constructs a new {@link ToggleRangeTemplate}.
+ * @param templateId the identifier for this template.
+ * @param button a {@link ControlButton} to use for the toggle interface
+ * @param range a {@link RangeTemplate} to use for the range interface
+ */
public ToggleRangeTemplate(@NonNull String templateId,
@NonNull ControlButton button,
@NonNull RangeTemplate range) {
@@ -50,6 +63,14 @@
mRangeTemplate = range;
}
+ /**
+ * Constructs a new {@link ToggleRangeTemplate}.
+ * @param templateId the identifier for this template.
+ * @param checked true if the toggle should be rendered as active.
+ * @param actionDescription action description for the button.
+ * @param range {@link RangeTemplate} to use for the range interface
+ * @see ControlButton
+ */
public ToggleRangeTemplate(@NonNull String templateId,
boolean checked,
@NonNull CharSequence actionDescription,
@@ -86,6 +107,9 @@
return mControlButton.getActionDescription();
}
+ /**
+ * @return {@link ControlTemplate#TYPE_TOGGLE_RANGE}
+ */
@Override
public int getTemplateType() {
return TYPE;
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
new file mode 100644
index 0000000..8cdd24e
--- /dev/null
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -0,0 +1,59 @@
+/*
+ * 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.service.dreams;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * The Activity used by the {@link DreamService} to draw screensaver content
+ * on the screen. This activity runs in dream application's process, but is started by a
+ * specialized method: {@link com.android.server.wm.ActivityTaskManagerService#startDreamActivity}.
+ * Hence, it does not have to be declared in the dream application's manifest.
+ *
+ * We use an activity as the dream canvas, because it interacts easier with other activities on
+ * the screen (compared to a hover window). However, the DreamService is in charge of the dream and
+ * it receives all Window.Callbacks from its main window. Since a window can have only one callback
+ * receiver, the activity will not receive any window callbacks.
+ *
+ * Prior to the DreamActivity, the DreamService used to work with a hovering window and give the
+ * screensaver application control over that window. The DreamActivity is a replacement to that
+ * hover window. Using an activity allows for better-defined interactions with the rest of the
+ * activities on screen. The switch to DreamActivity should be transparent to the screensaver
+ * application, i.e. the application will still use DreamService APIs and not notice that the
+ * system is using an activity behind the scenes.
+ *
+ * @hide
+ */
+public class DreamActivity extends Activity {
+ static final String EXTRA_CALLBACK = "binder";
+
+ public DreamActivity() {}
+
+ @Override
+ public void onCreate(@Nullable Bundle bundle) {
+ super.onCreate(bundle);
+
+ DreamService.DreamServiceWrapper callback =
+ (DreamService.DreamServiceWrapper) getIntent().getIBinderExtra(EXTRA_CALLBACK);
+
+ if (callback != null) {
+ callback.onActivityCreated(this);
+ }
+ }
+}
diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
index ff7cef9..41fdd0b 100644
--- a/core/java/android/service/dreams/DreamManagerInternal.java
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -16,6 +16,8 @@
package android.service.dreams;
+import android.content.ComponentName;
+
/**
* Dream manager local system service interface.
*
@@ -42,4 +44,13 @@
* Called by the power manager to determine whether a dream is running.
*/
public abstract boolean isDreaming();
+
+ /**
+ * Called by the ActivityTaskManagerService to verify that the startDreamActivity
+ * request comes from the current active dream component.
+ *
+ * @param doze If true returns the current active doze component. Otherwise, returns the
+ * active dream component.
+ */
+ public abstract ComponentName getActiveDreamComponent(boolean doze);
}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index de4a551..002d4b8 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -15,25 +15,29 @@
*/
package android.service.dreams;
+import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
+
import android.annotation.IdRes;
import android.annotation.LayoutRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Activity;
+import android.app.ActivityTaskManager;
import android.app.AlarmManager;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
import android.view.ActionMode;
@@ -48,10 +52,8 @@
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
-import com.android.internal.policy.PhoneWindow;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.DumpUtils.Dump;
@@ -176,10 +178,11 @@
*/
public static final String DREAM_META_DATA = "android.service.dream";
- private final IDreamManager mSandman;
- private final Handler mHandler = new Handler();
- private IBinder mWindowToken;
+ private final IDreamManager mDreamManager;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private IBinder mDreamToken;
private Window mWindow;
+ private Activity mActivity;
private boolean mInteractive;
private boolean mLowProfile = true;
private boolean mFullscreen;
@@ -195,8 +198,11 @@
private boolean mDebug = false;
+ private DreamServiceWrapper mDreamServiceWrapper;
+ private Runnable mDispatchAfterOnAttachedToWindow;
+
public DreamService() {
- mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
+ mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
}
/**
@@ -602,6 +608,8 @@
* Marks this dream as windowless. Only available to doze dreams.
*
* @hide
+ *
+ * TODO: Remove @UnsupportedAppUsage.
*/
@UnsupportedAppUsage
public void setWindowless(boolean windowless) {
@@ -670,14 +678,14 @@
}
private void updateDoze() {
- if (mWindowToken == null) {
- Slog.w(TAG, "Updating doze without a window token.");
+ if (mDreamToken == null) {
+ Slog.w(TAG, "Updating doze without a dream token.");
return;
}
if (mDozing) {
try {
- mSandman.startDozing(mWindowToken, mDozeScreenState, mDozeScreenBrightness);
+ mDreamManager.startDozing(mDreamToken, mDozeScreenState, mDozeScreenBrightness);
} catch (RemoteException ex) {
// system server died
}
@@ -700,7 +708,7 @@
if (mDozing) {
mDozing = false;
try {
- mSandman.stopDozing(mWindowToken);
+ mDreamManager.stopDozing(mDreamToken);
} catch (RemoteException ex) {
// system server died
}
@@ -882,7 +890,8 @@
@Override
public final IBinder onBind(Intent intent) {
if (mDebug) Slog.v(TAG, "onBind() intent = " + intent);
- return new DreamServiceWrapper();
+ mDreamServiceWrapper = new DreamServiceWrapper();
+ return mDreamServiceWrapper;
}
/**
@@ -895,20 +904,26 @@
public final void finish() {
if (mDebug) Slog.v(TAG, "finish(): mFinished=" + mFinished);
+ if (mActivity != null) {
+ if (!mActivity.isFinishing()) {
+ // In case the activity is not finished yet, do it now.
+ mActivity.finishAndRemoveTask();
+ return;
+ }
+ } else if (!mWindowless) {
+ Slog.w(TAG, "Finish was called before the dream was attached.");
+ }
+
if (!mFinished) {
mFinished = true;
- if (mWindowToken == null) {
- Slog.w(TAG, "Finish was called before the dream was attached.");
- } else {
- try {
- mSandman.finishSelf(mWindowToken, true /*immediate*/);
- } catch (RemoteException ex) {
- // system server died
- }
+ try {
+ // finishSelf will unbind the dream controller from the dream service. This will
+ // trigger DreamService.this.onDestroy and DreamService.this will die.
+ mDreamManager.finishSelf(mDreamToken, true /*immediate*/);
+ } catch (RemoteException ex) {
+ // system server died
}
-
- stopSelf(); // if launched via any other means
}
}
@@ -938,11 +953,11 @@
// Now tell the system we are waking gently, unless we already told
// it we were finishing immediately.
if (!fromSystem && !mFinished) {
- if (mWindowToken == null) {
+ if (mActivity == null) {
Slog.w(TAG, "WakeUp was called before the dream was attached.");
} else {
try {
- mSandman.finishSelf(mWindowToken, false /*immediate*/);
+ mDreamManager.finishSelf(mDreamToken, false /*immediate*/);
} catch (RemoteException ex) {
// system server died
}
@@ -977,20 +992,14 @@
onDreamingStopped();
}
- if (mWindow != null) {
- // force our window to be removed synchronously
- if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager");
- mWindow.getWindowManager().removeViewImmediate(mWindow.getDecorView());
- mWindow = null;
+ if (mActivity != null && !mActivity.isFinishing()) {
+ mActivity.finishAndRemoveTask();
+ } else {
+ finish();
}
- if (mWindowToken != null) {
- // the following will print a log message if it finds any other leaked windows
- WindowManagerGlobal.getInstance().closeAll(mWindowToken,
- this.getClass().getName(), "Dream");
- mWindowToken = null;
- mCanDoze = false;
- }
+ mDreamToken = null;
+ mCanDoze = false;
}
/**
@@ -998,95 +1007,107 @@
*
* Must run on mHandler.
*
- * @param windowToken A window token that will allow a window to be created in the correct layer.
+ * @param dreamToken Token for this dream service.
* @param started A callback that will be invoked once onDreamingStarted has completed.
*/
- private final void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started) {
- if (mWindowToken != null) {
- Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken);
+ private void attach(IBinder dreamToken, boolean canDoze, IRemoteCallback started) {
+ if (mDreamToken != null) {
+ Slog.e(TAG, "attach() called when dream with token=" + mDreamToken
+ + " already attached");
return;
}
if (mFinished || mWaking) {
Slog.w(TAG, "attach() called after dream already finished");
try {
- mSandman.finishSelf(windowToken, true /*immediate*/);
+ mDreamManager.finishSelf(dreamToken, true /*immediate*/);
} catch (RemoteException ex) {
// system server died
}
return;
}
- mWindowToken = windowToken;
+ mDreamToken = dreamToken;
mCanDoze = canDoze;
if (mWindowless && !mCanDoze) {
throw new IllegalStateException("Only doze dreams can be windowless");
}
- if (!mWindowless) {
- mWindow = new PhoneWindow(this);
- mWindow.setCallback(this);
- mWindow.requestFeature(Window.FEATURE_NO_TITLE);
- mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000));
- mWindow.setFormat(PixelFormat.OPAQUE);
- if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s",
- windowToken, WindowManager.LayoutParams.TYPE_DREAM));
-
- WindowManager.LayoutParams lp = mWindow.getAttributes();
- lp.type = WindowManager.LayoutParams.TYPE_DREAM;
- lp.token = windowToken;
- lp.windowAnimations = com.android.internal.R.style.Animation_Dream;
- lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
- | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
- | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
- | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
- );
- mWindow.setAttributes(lp);
- // Workaround: Currently low-profile and in-window system bar backgrounds don't go
- // along well. Dreams usually don't need such bars anyways, so disable them by default.
- mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- mWindow.setWindowManager(null, windowToken, "dream", true);
-
- applySystemUiVisibilityFlags(
- (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
- View.SYSTEM_UI_FLAG_LOW_PROFILE);
-
- try {
- getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes());
- } catch (WindowManager.BadTokenException ex) {
- // This can happen because the dream manager service will remove the token
- // immediately without necessarily waiting for the dream to start.
- // We should receive a finish message soon.
- Slog.i(TAG, "attach() called after window token already removed, dream will "
- + "finish soon");
- mWindow = null;
- return;
- }
- }
- // We need to defer calling onDreamingStarted until after onWindowAttached,
- // which is posted to the handler by addView, so we post onDreamingStarted
- // to the handler also. Need to watch out here in case detach occurs before
- // this callback is invoked.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mWindow != null || mWindowless) {
- if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()");
- mStarted = true;
+ mDispatchAfterOnAttachedToWindow = () -> {
+ if (mWindow != null || mWindowless) {
+ mStarted = true;
+ try {
+ onDreamingStarted();
+ } finally {
try {
- onDreamingStarted();
- } finally {
- try {
- started.sendResult(null);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ started.sendResult(null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
}
- });
+ };
+
+ // We need to defer calling onDreamingStarted until after the activity is created.
+ // If the dream is windowless, we can call it immediately. Otherwise, we wait
+ // for the DreamActivity to report onActivityCreated via
+ // DreamServiceWrapper.onActivityCreated.
+ if (!mWindowless) {
+ Intent i = new Intent(this, DreamActivity.class);
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ i.putExtra(DreamActivity.EXTRA_CALLBACK, mDreamServiceWrapper);
+
+ try {
+ if (!ActivityTaskManager.getService().startDreamActivity(i)) {
+ detach();
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not connect to activity task manager to start dream activity");
+ e.rethrowFromSystemServer();
+ }
+ } else {
+ mDispatchAfterOnAttachedToWindow.run();
+ }
+ }
+
+ private void onWindowCreated(Window w) {
+ mWindow = w;
+ mWindow.setCallback(this);
+ mWindow.setType(TYPE_DREAM);
+ mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+
+ WindowManager.LayoutParams lp = mWindow.getAttributes();
+ lp.windowAnimations = com.android.internal.R.style.Animation_Dream;
+ lp.flags |= (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+ | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
+ | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
+ );
+ mWindow.setAttributes(lp);
+ // Workaround: Currently low-profile and in-window system bar backgrounds don't go
+ // along well. Dreams usually don't need such bars anyways, so disable them by default.
+ mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+
+ applySystemUiVisibilityFlags(
+ (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
+ View.SYSTEM_UI_FLAG_LOW_PROFILE);
+
+ mWindow.getDecorView().addOnAttachStateChangeListener(
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ mDispatchAfterOnAttachedToWindow.run();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ finish();
+ }
+ });
}
private boolean getWindowFlagValue(int flag, boolean defaultValue) {
@@ -1131,10 +1152,10 @@
/** @hide */
protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print(TAG + ": ");
- if (mWindowToken == null) {
+ if (mFinished) {
pw.println("stopped");
} else {
- pw.println("running (token=" + mWindowToken + ")");
+ pw.println("running (dreamToken=" + mDreamToken + ")");
}
pw.println(" window: " + mWindow);
pw.print(" flags:");
@@ -1156,36 +1177,32 @@
return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
}
- private final class DreamServiceWrapper extends IDreamService.Stub {
+ /**
+ * The DreamServiceWrapper is used as a gateway to the system_server, where DreamController
+ * uses it to control the DreamService. It is also used to receive callbacks from the
+ * DreamActivity.
+ */
+ final class DreamServiceWrapper extends IDreamService.Stub {
@Override
- public void attach(final IBinder windowToken, final boolean canDoze,
+ public void attach(final IBinder dreamToken, final boolean canDoze,
IRemoteCallback started) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- DreamService.this.attach(windowToken, canDoze, started);
- }
- });
+ mHandler.post(() -> DreamService.this.attach(dreamToken, canDoze, started));
}
@Override
public void detach() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- DreamService.this.detach();
- }
- });
+ mHandler.post(DreamService.this::detach);
}
@Override
public void wakeUp() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- DreamService.this.wakeUp(true /*fromSystem*/);
- }
- });
+ mHandler.post(() -> DreamService.this.wakeUp(true /*fromSystem*/));
+ }
+
+ /** @hide */
+ void onActivityCreated(DreamActivity a) {
+ mActivity = a;
+ onWindowCreated(a.getWindow());
}
}
}
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index d254ffd..6496de3 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -31,12 +31,14 @@
void setDreamComponents(in ComponentName[] componentNames);
@UnsupportedAppUsage
ComponentName[] getDreamComponents();
- ComponentName getDefaultDreamComponent();
- void testDream(in ComponentName componentName);
+ ComponentName getDefaultDreamComponentForUser(int userId);
+ void testDream(int userId, in ComponentName componentName);
@UnsupportedAppUsage
boolean isDreaming();
void finishSelf(in IBinder token, boolean immediate);
void startDozing(in IBinder token, int screenState, int screenBrightness);
void stopDozing(in IBinder token);
void forceAmbientDisplayEnabled(boolean enabled);
+ ComponentName[] getDreamComponentsForUser(int userId);
+ void setDreamComponentsForUser(int userId, in ComponentName[] componentNames);
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index e976e18..975e75c 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -184,7 +184,7 @@
/**
* Implement this to know when the notification panel is revealed
*
- * @param items Number of items on the panel at time of opening
+ * @param items Number of notifications on the panel at time of opening
*/
public void onPanelRevealed(int items) {
diff --git a/core/java/android/service/notification/OWNERS b/core/java/android/service/notification/OWNERS
new file mode 100644
index 0000000..2e94be5
--- /dev/null
+++ b/core/java/android/service/notification/OWNERS
@@ -0,0 +1,4 @@
+juliacr@google.com
+beverlyt@google.com
+dsandler@android.com
+pixel@google.com
\ No newline at end of file
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index a88d389..7d070b1 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -227,7 +227,6 @@
@Nullable
private KeyphraseMetadata mKeyphraseMetadata;
private final KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
- private final IVoiceInteractionService mVoiceInteractionService;
private final IVoiceInteractionManagerService mModelManagementService;
private final SoundTriggerListener mInternalCallback;
private final Callback mExternalCallback;
@@ -413,14 +412,11 @@
* @param text The keyphrase text to get the detector for.
* @param locale The java locale for the detector.
* @param callback A non-null Callback for receiving the recognition events.
- * @param voiceInteractionService The current voice interaction service.
* @param modelManagementService A service that allows management of sound models.
- *
* @hide
*/
public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback,
KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
- IVoiceInteractionService voiceInteractionService,
IVoiceInteractionManagerService modelManagementService) {
mText = text;
mLocale = locale;
@@ -428,7 +424,6 @@
mExternalCallback = callback;
mHandler = new MyHandler();
mInternalCallback = new SoundTriggerListener(mHandler);
- mVoiceInteractionService = voiceInteractionService;
mModelManagementService = modelManagementService;
new RefreshAvailabiltyTask().execute();
}
@@ -471,6 +466,8 @@
/**
* Get the audio capabilities supported by the platform which can be enabled when
* starting a recognition.
+ * Caller must be the active voice interaction service via
+ * Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @see #AUDIO_CAPABILITY_ECHO_CANCELLATION
* @see #AUDIO_CAPABILITY_NOISE_SUPPRESSION
@@ -488,7 +485,7 @@
private int getSupportedAudioCapabilitiesLocked() {
try {
ModuleProperties properties =
- mModelManagementService.getDspModuleProperties(mVoiceInteractionService);
+ mModelManagementService.getDspModuleProperties();
if (properties != null) {
return properties.audioCapabilities;
}
@@ -501,6 +498,8 @@
/**
* Starts recognition for the associated keyphrase.
+ * Caller must be the active voice interaction service via
+ * Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @see #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
* @see #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
@@ -533,6 +532,8 @@
/**
* Stops recognition for the associated keyphrase.
+ * Caller must be the active voice interaction service via
+ * Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @return Indicates whether the call succeeded or not.
* @throws UnsupportedOperationException if the recognition isn't supported.
@@ -565,6 +566,8 @@
* stopping recognition. Once the model is unloaded, the value will be lost.
* {@link AlwaysOnHotwordDetector#queryParameter} should be checked first before calling this
* method.
+ * Caller must be the active voice interaction service via
+ * Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @param modelParam {@link ModelParams}
* @param value Value to set
@@ -595,6 +598,8 @@
* value is returned. See {@link ModelParams} for parameter default values.
* {@link AlwaysOnHotwordDetector#queryParameter} should be checked first before
* calling this method.
+ * Caller must be the active voice interaction service via
+ * Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @param modelParam {@link ModelParams}
* @return value of parameter
@@ -617,6 +622,8 @@
* Determine if parameter control is supported for the given model handle.
* This method should be checked prior to calling {@link AlwaysOnHotwordDetector#setParameter}
* or {@link AlwaysOnHotwordDetector#getParameter}.
+ * Caller must be the active voice interaction service via
+ * Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @param modelParam {@link ModelParams}
* @return supported range of parameter, null if not supported
@@ -775,7 +782,7 @@
int code = STATUS_ERROR;
try {
- code = mModelManagementService.startRecognition(mVoiceInteractionService,
+ code = mModelManagementService.startRecognition(
mKeyphraseMetadata.id, mLocale.toLanguageTag(), mInternalCallback,
new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
recognitionExtra, null /* additional data */, audioCapabilities));
@@ -791,8 +798,8 @@
private int stopRecognitionLocked() {
int code = STATUS_ERROR;
try {
- code = mModelManagementService.stopRecognition(
- mVoiceInteractionService, mKeyphraseMetadata.id, mInternalCallback);
+ code = mModelManagementService.stopRecognition(mKeyphraseMetadata.id,
+ mInternalCallback);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in stopRecognition!", e);
}
@@ -805,8 +812,8 @@
private int setParameterLocked(@ModelParams int modelParam, int value) {
try {
- int code = mModelManagementService.setParameter(mVoiceInteractionService,
- mKeyphraseMetadata.id, modelParam, value);
+ int code = mModelManagementService.setParameter(mKeyphraseMetadata.id, modelParam,
+ value);
if (code != STATUS_OK) {
Slog.w(TAG, "setParameter failed with error code " + code);
@@ -820,8 +827,7 @@
private int getParameterLocked(@ModelParams int modelParam) {
try {
- return mModelManagementService.getParameter(mVoiceInteractionService,
- mKeyphraseMetadata.id, modelParam);
+ return mModelManagementService.getParameter(mKeyphraseMetadata.id, modelParam);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -831,8 +837,7 @@
private ModelParamRange queryParameterLocked(@ModelParams int modelParam) {
try {
SoundTrigger.ModelParamRange modelParamRange =
- mModelManagementService.queryParameter(mVoiceInteractionService,
- mKeyphraseMetadata.id, modelParam);
+ mModelManagementService.queryParameter(mKeyphraseMetadata.id, modelParam);
if (modelParamRange == null) {
return null;
@@ -966,7 +971,7 @@
ModuleProperties dspModuleProperties = null;
try {
dspModuleProperties =
- mModelManagementService.getDspModuleProperties(mVoiceInteractionService);
+ mModelManagementService.getDspModuleProperties();
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in getDspProperties!", e);
}
@@ -982,7 +987,7 @@
private void internalUpdateEnrolledKeyphraseMetadata() {
try {
mKeyphraseMetadata = mModelManagementService.getEnrolledKeyphraseMetadata(
- mVoiceInteractionService, mText, mLocale.toLanguageTag());
+ mText, mLocale.toLanguageTag());
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in internalUpdateEnrolledKeyphraseMetadata", e);
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index fc99836..b54e4d9 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -199,7 +199,7 @@
throw new IllegalStateException("Not available until onReady() is called");
}
try {
- mSystemService.showSession(mInterface, args, flags);
+ mSystemService.showSession(args, flags);
} catch (RemoteException e) {
}
}
@@ -302,7 +302,7 @@
// Allow only one concurrent recognition via the APIs.
safelyShutdownHotwordDetector();
mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback,
- mKeyphraseEnrollmentInfo, mInterface, mSystemService);
+ mKeyphraseEnrollmentInfo, mSystemService);
}
return mHotwordDetector;
}
@@ -373,7 +373,7 @@
}
try {
- mSystemService.setUiHints(mInterface, hints);
+ mSystemService.setUiHints(hints);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 2c077bb..7238b12 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
@@ -62,7 +61,6 @@
*
* @hide
*/
-@SystemApi
@TestApi
public class TelephonyRegistryManager {
@@ -286,7 +284,6 @@
* @param incomingNumber incoming phone number.
* @hide
*/
- @SystemApi
@TestApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void notifyCallStateChangedForAllSubscriptions(@CallState int state,
@@ -302,7 +299,6 @@
* Notify {@link SubscriptionInfo} change.
* @hide
*/
- @SystemApi
public void notifySubscriptionInfoChanged() {
try {
sRegistry.notifySubscriptionInfoChanged();
@@ -315,7 +311,6 @@
* Notify opportunistic {@link SubscriptionInfo} change.
* @hide
*/
- @SystemApi
public void notifyOpportunisticSubscriptionInfoChanged() {
try {
sRegistry.notifyOpportunisticSubscriptionInfoChanged();
diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index 7b24ba9..9a555c16 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -32,7 +32,7 @@
* <p>
* This API is not as convenient or type safe as the standard protobuf
* classes. If possible, the best recommended library is to use protobuf lite.
- * However, in environements (such as the Android platform itself), a
+ * However, in environments (such as the Android platform itself), a
* more memory efficient version is necessary.
*
* <p>Each write method takes an ID code from the protoc generated classes
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 31fc161..0ab856e 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -554,26 +554,12 @@
*/
public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
if (insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0
- || isBoundsEmpty()) {
+ || (isBoundsEmpty() && mWaterfallInsets.equals(Insets.NONE))) {
return this;
}
- Rect safeInsets = new Rect(mSafeInsets);
-
- // Note: it's not really well defined what happens when the inset is negative, because we
- // don't know if the safe inset needs to expand in general.
- if (insetTop > 0 || safeInsets.top > 0) {
- safeInsets.top = atLeastZero(safeInsets.top - insetTop);
- }
- if (insetBottom > 0 || safeInsets.bottom > 0) {
- safeInsets.bottom = atLeastZero(safeInsets.bottom - insetBottom);
- }
- if (insetLeft > 0 || safeInsets.left > 0) {
- safeInsets.left = atLeastZero(safeInsets.left - insetLeft);
- }
- if (insetRight > 0 || safeInsets.right > 0) {
- safeInsets.right = atLeastZero(safeInsets.right - insetRight);
- }
+ Rect safeInsets = insetInsets(insetLeft, insetTop, insetRight, insetBottom,
+ new Rect(mSafeInsets));
// If we are not cutting off part of the cutout by insetting it on bottom/right, and we also
// don't move it around, we can avoid the allocation and copy of the instance.
@@ -581,6 +567,9 @@
return this;
}
+ Rect waterfallInsets = insetInsets(insetLeft, insetTop, insetRight, insetBottom,
+ mWaterfallInsets.toRect());
+
Rect[] bounds = mBounds.getRects();
for (int i = 0; i < bounds.length; ++i) {
if (!bounds[i].equals(ZERO_RECT)) {
@@ -588,7 +577,27 @@
}
}
- return new DisplayCutout(safeInsets, mWaterfallInsets, bounds, false /* copyArguments */);
+ return new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds,
+ false /* copyArguments */);
+ }
+
+ private Rect insetInsets(int insetLeft, int insetTop, int insetRight, int insetBottom,
+ Rect insets) {
+ // Note: it's not really well defined what happens when the inset is negative, because we
+ // don't know if the safe inset needs to expand in general.
+ if (insetTop > 0 || insets.top > 0) {
+ insets.top = atLeastZero(insets.top - insetTop);
+ }
+ if (insetBottom > 0 || insets.bottom > 0) {
+ insets.bottom = atLeastZero(insets.bottom - insetBottom);
+ }
+ if (insetLeft > 0 || insets.left > 0) {
+ insets.left = atLeastZero(insets.left - insetLeft);
+ }
+ if (insetRight > 0 || insets.right > 0) {
+ insets.right = atLeastZero(insets.right - insetRight);
+ }
+ return insets;
}
/**
diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
index cb82f16..1cf83a3 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -25,32 +25,8 @@
* @hide
*/
interface IPinnedStackController {
-
- /**
- * Notifies the controller that the PiP is currently minimized.
- */
- oneway void setIsMinimized(boolean isMinimized);
-
/**
* @return what WM considers to be the current device rotation.
*/
int getDisplayRotation();
-
- /**
- * Notifies the controller to actually start the PiP animation.
- * The bounds would be calculated based on the last save reentry fraction internally.
- * {@param destinationBounds} is the stack bounds of the final PiP window
- * and {@param sourceRectHint} is the source bounds hint used when entering picture-in-picture,
- * expect the same bound passed via IPinnedStackListener#onPrepareAnimation.
- * {@param animationDuration} suggests the animation duration transitioning to PiP window.
- */
- void startAnimation(in Rect destinationBounds, in Rect sourceRectHint, int animationDuration);
-
- /**
- * Notifies the controller to reset on bounds animation, if there is any.
- * This could happen when screen rotation is happening and we need to notify the WM to reset
- * any running bounds animation on the pinned stack.
- * {@param bounds} here is the final destination bounds.
- */
- void resetBoundsAnimation(in Rect bounds);
}
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index d01c933..596d55a 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -43,8 +43,7 @@
* pinned stack (the final bounds if animating, the current bounds if not),
* which may be helpful in calculating dependent animation bounds.
*/
- void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment,
- boolean fromShelfAdjustment);
+ void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment);
/**
* Called when window manager decides to adjust the pinned stack bounds because of the IME, or
@@ -55,12 +54,6 @@
void onImeVisibilityChanged(boolean imeVisible, int imeHeight);
/**
- * Called when window manager decides to adjust the minimized state, or when the listener
- * is first registered to allow the listener to synchronized its state with the controller.
- */
- void onMinimizedStateChanged(boolean isMinimized);
-
- /**
* Called when the set of actions for the current PiP activity changes, or when the listener
* is first registered to allow the listener to synchronized its state with the controller.
*/
@@ -99,13 +92,4 @@
* Called by the window manager when the aspect ratio is reset.
*/
void onAspectRatioChanged(float aspectRatio);
-
- /**
- * Called by the window manager to notify the listener to prepare for PiP animation.
- * Internally, the target bounds would be calculated from the given {@param aspectRatio}
- * and {@param bounds}, the saved reentry snap fraction also contributes.
- * Caller would wait for a IPinnedStackController#startAnimation callback to actually
- * start the animation, see details in IPinnedStackController.
- */
- void onPrepareAnimation(in Rect sourceRectHint, float aspectRatio, in Rect bounds);
}
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 762366e..a60a5cc 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -67,23 +67,11 @@
void setAnimationTargetsBehindSystemBars(boolean behindSystemBars);
/**
- * Informs the system that the primary split-screen stack should be minimized.
- */
- void setSplitScreenMinimized(boolean minimized);
-
- /**
* Hides the current input method if one is showing.
*/
void hideCurrentInputMethod();
/**
- * This call is deprecated, use #setDeferCancelUntilNextTransition() instead
- * TODO(138144750): Remove this method once there are no callers
- * @deprecated
- */
- void setCancelWithDeferredScreenshot(boolean screenshot);
-
- /**
* Clean up the screenshot of previous task which was created during recents animation that
* was cancelled by a stack order change.
*
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 1730347..73601d9 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -444,8 +444,7 @@
WindowContentFrameStats getWindowContentFrameStats(IBinder token);
/**
- * @return the dock side the current docked stack is at; must be one of the
- * WindowManagerGlobal.DOCKED_* values
+ * This is a no-op.
*/
@UnsupportedAppUsage
int getDockedStackSide();
@@ -457,27 +456,11 @@
void setDockedStackDividerTouchRegion(in Rect touchableRegion);
/**
- * Registers a listener that will be called when the dock divider changes its visibility or when
- * the docked stack gets added/removed.
- */
- @UnsupportedAppUsage
- void registerDockedStackListener(IDockedStackListener listener);
-
- /**
* Registers a listener that will be called when the pinned stack state changes.
*/
void registerPinnedStackListener(int displayId, IPinnedStackListener listener);
/**
- * Updates the dim layer used while resizing.
- *
- * @param visible Whether the dim layer should be visible.
- * @param targetWindowingMode The windowing mode of the stack the dim layer should be placed on.
- * @param alpha The translucency of the dim layer, between 0 and 1.
- */
- void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha);
-
- /**
* Requests Keyboard Shortcuts from the displayed window.
*
* @param receiver The receiver to deliver the results to.
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index f226369..43afc15 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -17,7 +17,6 @@
package android.view;
import static android.view.InsetsState.ITYPE_IME;
-import static android.view.InsetsState.toPublicType;
import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
@@ -99,6 +98,15 @@
}
}
+ @Override
+ void hide(boolean animationFinished) {
+ super.hide();
+ if (animationFinished) {
+ // remove IME surface as IME has finished hide animation.
+ removeSurface();
+ }
+ }
+
/**
* Request {@link InputMethodManager} to show the IME.
* @return @see {@link android.view.InsetsSourceConsumer.ShowResult}.
@@ -128,6 +136,11 @@
}
@Override
+ public void removeSurface() {
+ getImm().removeImeSurface();
+ }
+
+ @Override
public void setControl(@Nullable InsetsSourceControl control, int[] showTypes,
int[] hideTypes) {
super.setControl(control, showTypes, hideTypes);
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 5f9c480..cb9e746 100644
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -233,6 +233,21 @@
return mSeq;
}
+ /**
+ * Gets the ID of this event. This is generated when an event is created and preserved until its
+ * last stage. It won't change just because the event crosses process boundary, but should
+ * change when making a copy with modifications.
+ * <p>
+ * To avoid exposing app usage to other processes this ID is generated from a CSPRNG. Therefore
+ * there isn't 100% guarantee on the uniqueness of this ID, though the chance of ID collisions
+ * is considerably low. The rule of thumb is not to rely on the uniqueness for production logic,
+ * but a good source for tracking an event (e.g. logging and profiling).
+ *
+ * @return The ID of this event.
+ * @hide
+ */
+ public abstract int getId();
+
public int describeContents() {
return 0;
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 4a6a5a0..65ea6bb 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -694,7 +694,7 @@
if (shown) {
showDirectly(controller.getTypes());
} else {
- hideDirectly(controller.getTypes());
+ hideDirectly(controller.getTypes(), true /* animationFinished */);
}
}
@@ -852,10 +852,10 @@
: LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
}
- private void hideDirectly(@InsetsType int types) {
+ private void hideDirectly(@InsetsType int types, boolean animationFinished) {
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
- getSourceConsumer(internalTypes.valueAt(i)).hide();
+ getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished);
}
}
@@ -887,7 +887,7 @@
if (layoutDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
showDirectly(types);
} else {
- hideDirectly(types);
+ hideDirectly(types, false /* animationFinished */);
}
if (mViewRoot.mView == null) {
return;
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index e6497c0..e3a7de1 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -16,12 +16,10 @@
package android.view;
-import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsState.toPublicType;
import android.annotation.IntDef;
import android.annotation.Nullable;
-import android.util.MutableShort;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type.InsetsType;
@@ -137,6 +135,10 @@
setRequestedVisible(false);
}
+ void hide(boolean animationFinished) {
+ hide();
+ }
+
/**
* Called when current window gains focus
*/
@@ -201,6 +203,13 @@
}
/**
+ * Remove surface on which this consumer type is drawn.
+ */
+ public void removeSurface() {
+ // no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
+ }
+
+ /**
* Sets requested visibility from the client, regardless of whether we are able to control it at
* the moment.
*/
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index c91096e..e249c77 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1265,6 +1265,7 @@
private KeyEvent mNext;
+ private int mId;
@UnsupportedAppUsage
private int mDeviceId;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -1350,9 +1351,9 @@
private static native String nativeKeyCodeToString(int keyCode);
private static native int nativeKeyCodeFromString(String keyCode);
+ private static native int nativeNextId();
- private KeyEvent() {
- }
+ private KeyEvent() {}
/**
* Create a new key event.
@@ -1362,6 +1363,7 @@
* @param code The key code.
*/
public KeyEvent(int action, int code) {
+ mId = nativeNextId();
mAction = action;
mKeyCode = code;
mRepeatCount = 0;
@@ -1383,6 +1385,7 @@
*/
public KeyEvent(long downTime, long eventTime, int action,
int code, int repeat) {
+ mId = nativeNextId();
mDownTime = downTime;
mEventTime = eventTime;
mAction = action;
@@ -1407,6 +1410,7 @@
*/
public KeyEvent(long downTime, long eventTime, int action,
int code, int repeat, int metaState) {
+ mId = nativeNextId();
mDownTime = downTime;
mEventTime = eventTime;
mAction = action;
@@ -1435,6 +1439,7 @@
public KeyEvent(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int deviceId, int scancode) {
+ mId = nativeNextId();
mDownTime = downTime;
mEventTime = eventTime;
mAction = action;
@@ -1465,6 +1470,7 @@
public KeyEvent(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int deviceId, int scancode, int flags) {
+ mId = nativeNextId();
mDownTime = downTime;
mEventTime = eventTime;
mAction = action;
@@ -1497,6 +1503,7 @@
public KeyEvent(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int deviceId, int scancode, int flags, int source) {
+ mId = nativeNextId();
mDownTime = downTime;
mEventTime = eventTime;
mAction = action;
@@ -1523,6 +1530,7 @@
* @param flags The flags for this key event
*/
public KeyEvent(long time, String characters, int deviceId, int flags) {
+ mId = nativeNextId();
mDownTime = time;
mEventTime = time;
mCharacters = characters;
@@ -1539,6 +1547,7 @@
* Make an exact copy of an existing key event.
*/
public KeyEvent(KeyEvent origEvent) {
+ mId = origEvent.mId;
mDownTime = origEvent.mDownTime;
mEventTime = origEvent.mEventTime;
mAction = origEvent.mAction;
@@ -1567,6 +1576,7 @@
*/
@Deprecated
public KeyEvent(KeyEvent origEvent, long eventTime, int newRepeat) {
+ mId = nativeNextId(); // Not an exact copy so assign a new ID.
mDownTime = origEvent.mDownTime;
mEventTime = eventTime;
mAction = origEvent.mAction;
@@ -1598,15 +1608,16 @@
}
/**
- * Obtains a (potentially recycled) key event.
+ * Obtains a (potentially recycled) key event. Used by native code to create a Java object.
*
* @hide
*/
- public static KeyEvent obtain(long downTime, long eventTime, int action,
+ public static KeyEvent obtain(int id, long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int deviceId, int scancode, int flags, int source, int displayId, @Nullable byte[] hmac,
String characters) {
KeyEvent ev = obtain();
+ ev.mId = id;
ev.mDownTime = downTime;
ev.mEventTime = eventTime;
ev.mAction = action;
@@ -1628,12 +1639,24 @@
*
* @hide
*/
+ public static KeyEvent obtain(long downTime, long eventTime, int action,
+ int code, int repeat, int metaState,
+ int deviceId, int scanCode, int flags, int source, int displayId, String characters) {
+ return obtain(nativeNextId(), downTime, eventTime, action, code, repeat, metaState,
+ deviceId, scanCode, flags, source, displayId, null /* hmac */, characters);
+ }
+
+ /**
+ * Obtains a (potentially recycled) key event.
+ *
+ * @hide
+ */
@UnsupportedAppUsage
public static KeyEvent obtain(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int deviceId, int scancode, int flags, int source, String characters) {
return obtain(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode,
- flags, source, INVALID_DISPLAY, null /* hmac */, characters);
+ flags, source, INVALID_DISPLAY, characters);
}
/**
@@ -1645,6 +1668,7 @@
*/
public static KeyEvent obtain(KeyEvent other) {
KeyEvent ev = obtain();
+ ev.mId = other.mId;
ev.mDownTime = other.mDownTime;
ev.mEventTime = other.mEventTime;
ev.mAction = other.mAction;
@@ -1695,6 +1719,12 @@
// Do nothing.
}
+ /** @hide */
+ @Override
+ public int getId() {
+ return mId;
+ }
+
/**
* Create a new key event that is the same as the given one, but whose
* event time and repeat count are replaced with the given value.
@@ -1723,6 +1753,7 @@
public static KeyEvent changeTimeRepeat(KeyEvent event, long eventTime,
int newRepeat, int newFlags) {
KeyEvent ret = new KeyEvent(event);
+ ret.mId = nativeNextId(); // Not an exact copy so assign a new ID.
ret.mEventTime = eventTime;
ret.mRepeatCount = newRepeat;
ret.mFlags = newFlags;
@@ -1736,6 +1767,7 @@
* @param action The new action code of the event.
*/
private KeyEvent(KeyEvent origEvent, int action) {
+ mId = nativeNextId(); // Not an exact copy so assign a new ID.
mDownTime = origEvent.mDownTime;
mEventTime = origEvent.mEventTime;
mAction = action;
@@ -1772,6 +1804,7 @@
*/
public static KeyEvent changeFlags(KeyEvent event, int flags) {
event = new KeyEvent(event);
+ event.mId = nativeNextId(); // Not an exact copy so assign a new ID.
event.mFlags = flags;
return event;
}
@@ -1941,7 +1974,6 @@
/** @hide */
public static final boolean isWakeKey(int keyCode) {
switch (keyCode) {
- case KeyEvent.KEYCODE_BACK:
case KeyEvent.KEYCODE_CAMERA:
case KeyEvent.KEYCODE_MENU:
case KeyEvent.KEYCODE_PAIRING:
@@ -3096,6 +3128,7 @@
}
private KeyEvent(Parcel in) {
+ mId = in.readInt();
mDeviceId = in.readInt();
mSource = in.readInt();
mDisplayId = in.readInt();
@@ -3115,6 +3148,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_KEY_EVENT);
+ out.writeInt(mId);
out.writeInt(mDeviceId);
out.writeInt(mSource);
out.writeInt(mDisplayId);
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 0068476..19eff72 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1550,6 +1550,8 @@
private static native long nativeCopy(long destNativePtr, long sourceNativePtr,
boolean keepHistory);
@CriticalNative
+ private static native int nativeGetId(long nativePtr);
+ @CriticalNative
private static native int nativeGetDeviceId(long nativePtr);
@CriticalNative
private static native int nativeGetSource(long nativePtr);
@@ -2024,6 +2026,12 @@
}
}
+ /** @hide */
+ @Override
+ public int getId() {
+ return nativeGetId(mNativePtr);
+ }
+
/** {@inheritDoc} */
@Override
public final int getDeviceId() {
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 680a878..4badede 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -111,6 +111,7 @@
@NonNull WindowlessWindowManager wwm) {
mWm = wwm;
mViewRoot = new ViewRootImpl(c, d, mWm);
+ mViewRoot.forceDisableBLAST();
mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
@@ -135,6 +136,7 @@
mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
mSurfaceControl, hostToken);
mViewRoot = new ViewRootImpl(context, display, mWm);
+ mViewRoot.forceDisableBLAST();
mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 341cf39..fb7c04a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -392,7 +392,7 @@
* This gets called on a RenderThread worker thread, so members accessed here must
* be protected by a lock.
*/
- final boolean useBLAST = WindowManagerGlobal.useBLAST();
+ final boolean useBLAST = viewRoot.useBLAST();
viewRoot.registerRtFrameCallback(frame -> {
try {
final SurfaceControl.Transaction t = useBLAST ?
@@ -930,7 +930,7 @@
mSurfaceHeight);
}
} else if ((layoutSizeChanged || positionChanged) &&
- WindowManagerGlobal.useBLAST()) {
+ viewRoot.useBLAST()) {
viewRoot.setUseBLASTSyncTransaction();
}
mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
@@ -1132,9 +1132,8 @@
private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
Rect position, long frameNumber) {
- if (frameNumber > 0 && !WindowManagerGlobal.useBLAST()) {
- final ViewRootImpl viewRoot = getViewRootImpl();
-
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ if (frameNumber > 0 && viewRoot != null && !viewRoot.useBLAST()) {
t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(),
frameNumber);
}
@@ -1150,8 +1149,8 @@
}
private void setParentSpaceRectangle(Rect position, long frameNumber) {
- final boolean useBLAST = WindowManagerGlobal.useBLAST();
final ViewRootImpl viewRoot = getViewRootImpl();
+ final boolean useBLAST = viewRoot.useBLAST();
final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() :
mRtTransaction;
@@ -1211,7 +1210,8 @@
@Override
public void positionLost(long frameNumber) {
- boolean useBLAST = WindowManagerGlobal.useBLAST();
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ boolean useBLAST = viewRoot != null && viewRoot.useBLAST();
if (DEBUG) {
Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
System.identityHashCode(this), frameNumber));
@@ -1222,8 +1222,6 @@
return;
}
- final ViewRootImpl viewRoot = getViewRootImpl();
-
final SurfaceControl.Transaction t = useBLAST ?
(viewRoot != null ? viewRoot.getBLASTSyncTransaction() : mRtTransaction) :
mRtTransaction;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 11ab572..15d18d1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -29794,7 +29794,7 @@
// accessibility
CharSequence contentDescription = getContentDescription();
- stream.addProperty("accessibility:contentDescription",
+ stream.addUserProperty("accessibility:contentDescription",
contentDescription == null ? "" : contentDescription.toString());
stream.addProperty("accessibility:labelFor", getLabelFor());
stream.addProperty("accessibility:importantForAccessibility", getImportantForAccessibility());
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 2f44fe0..8a5be75 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1209,6 +1209,7 @@
ByteArrayOutputStream baOut = new ByteArrayOutputStream();
final ViewHierarchyEncoder encoder = new ViewHierarchyEncoder(baOut);
+ encoder.setUserPropertiesEnabled(false);
encoder.addProperty("window:left", view.mAttachInfo.mWindowLeft);
encoder.addProperty("window:top", view.mAttachInfo.mWindowTop);
view.encode(encoder);
diff --git a/core/java/android/view/ViewHierarchyEncoder.java b/core/java/android/view/ViewHierarchyEncoder.java
index b0e0524..ace05a6 100644
--- a/core/java/android/view/ViewHierarchyEncoder.java
+++ b/core/java/android/view/ViewHierarchyEncoder.java
@@ -66,10 +66,16 @@
private short mPropertyId = 1;
private Charset mCharset = Charset.forName("utf-8");
+ private boolean mUserPropertiesEnabled = true;
+
public ViewHierarchyEncoder(@NonNull ByteArrayOutputStream stream) {
mStream = new DataOutputStream(stream);
}
+ public void setUserPropertiesEnabled(boolean enabled) {
+ mUserPropertiesEnabled = enabled;
+ }
+
public void beginObject(@NonNull Object o) {
startPropertyMap();
addProperty("meta:__name__", o.getClass().getName());
@@ -121,6 +127,17 @@
}
/**
+ * Encodes a user defined property if they are allowed to be encoded
+ *
+ * @see #setUserPropertiesEnabled(boolean)
+ */
+ public void addUserProperty(@NonNull String name, @Nullable String s) {
+ if (mUserPropertiesEnabled) {
+ addProperty(name, s);
+ }
+ }
+
+ /**
* Writes the given name as the property name, and leaves it to the callee
* to fill in value for this property.
*/
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 148b327..918b5a3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -322,7 +322,7 @@
*/
private boolean mForceNextConfigUpdate;
- private final boolean mUseBLASTAdapter;
+ private boolean mUseBLASTAdapter;
/**
* Signals that compatibility booleans have been initialized according to
@@ -5379,7 +5379,8 @@
if (mTracePrefix == null) {
mTracePrefix = getClass().getSimpleName();
}
- Trace.traceBegin(traceTag, mTracePrefix + " seq#=" + q.mEvent.getSequenceNumber());
+ Trace.traceBegin(traceTag, mTracePrefix + " id=0x"
+ + Integer.toHexString(q.mEvent.getId()));
}
}
@@ -7986,12 +7987,13 @@
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
- q.mEvent.getSequenceNumber());
+ q.mEvent.getId());
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent src=0x"
+ Integer.toHexString(q.mEvent.getSource()) + " eventTimeNano="
- + q.mEvent.getEventTimeNano() + " seq#=" + q.mEvent.getSequenceNumber());
+ + q.mEvent.getEventTimeNano() + " id=0x"
+ + Integer.toHexString(q.mEvent.getId()));
}
try {
if (mInputEventConsistencyVerifier != null) {
@@ -8032,7 +8034,7 @@
private void finishInputEvent(QueuedInputEvent q) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
- q.mEvent.getSequenceNumber());
+ q.mEvent.getId());
if (q.mReceiver != null) {
boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
@@ -9639,4 +9641,16 @@
public void onDescendantUnbufferedRequested() {
mUnbufferedInputSource = mView.mUnbufferedInputSource;
}
+
+ /**
+ * Force disabling use of the BLAST adapter regardless of the system
+ * flag. Needs to be called before addView.
+ */
+ void forceDisableBLAST() {
+ mUseBLASTAdapter = false;
+ }
+
+ boolean useBLAST() {
+ return mUseBLASTAdapter;
+ }
}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 024de4d..cb0320e 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -69,6 +69,9 @@
/** The type of the UI. */
private final @NonNull @Type String mType;
+ /** Whether the suggestion should be pinned or not. */
+ private final boolean mPinned;
+
/**
* Creates a new {@link InlineSuggestionInfo}, for testing purpose.
*
@@ -79,8 +82,8 @@
public static InlineSuggestionInfo newInlineSuggestionInfo(
@NonNull InlinePresentationSpec presentationSpec,
@NonNull @Source String source,
- @Nullable String[] autofillHints) {
- return new InlineSuggestionInfo(presentationSpec, source, autofillHints, TYPE_SUGGESTION);
+ @Nullable String[] autofillHints, @NonNull @Type String type, boolean isPinned) {
+ return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned);
}
@@ -127,6 +130,8 @@
* Hints for the type of data being suggested.
* @param type
* The type of the UI.
+ * @param pinned
+ * Whether the suggestion should be pinned or not.
* @hide
*/
@DataClass.Generated.Member
@@ -134,7 +139,8 @@
@NonNull InlinePresentationSpec presentationSpec,
@NonNull @Source String source,
@Nullable String[] autofillHints,
- @NonNull @Type String type) {
+ @NonNull @Type String type,
+ boolean pinned) {
this.mPresentationSpec = presentationSpec;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPresentationSpec);
@@ -163,6 +169,7 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mType);
+ this.mPinned = pinned;
// onConstructed(); // You can define this method to get a callback
}
@@ -199,6 +206,14 @@
return mType;
}
+ /**
+ * Whether the suggestion should be pinned or not.
+ */
+ @DataClass.Generated.Member
+ public boolean isPinned() {
+ return mPinned;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -209,7 +224,8 @@
"presentationSpec = " + mPresentationSpec + ", " +
"source = " + mSource + ", " +
"autofillHints = " + java.util.Arrays.toString(mAutofillHints) + ", " +
- "type = " + mType +
+ "type = " + mType + ", " +
+ "pinned = " + mPinned +
" }";
}
@@ -229,7 +245,8 @@
&& java.util.Objects.equals(mPresentationSpec, that.mPresentationSpec)
&& java.util.Objects.equals(mSource, that.mSource)
&& java.util.Arrays.equals(mAutofillHints, that.mAutofillHints)
- && java.util.Objects.equals(mType, that.mType);
+ && java.util.Objects.equals(mType, that.mType)
+ && mPinned == that.mPinned;
}
@Override
@@ -243,6 +260,7 @@
_hash = 31 * _hash + java.util.Objects.hashCode(mSource);
_hash = 31 * _hash + java.util.Arrays.hashCode(mAutofillHints);
_hash = 31 * _hash + java.util.Objects.hashCode(mType);
+ _hash = 31 * _hash + Boolean.hashCode(mPinned);
return _hash;
}
@@ -253,6 +271,7 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
+ if (mPinned) flg |= 0x10;
if (mAutofillHints != null) flg |= 0x4;
dest.writeByte(flg);
dest.writeTypedObject(mPresentationSpec, flags);
@@ -273,6 +292,7 @@
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
+ boolean pinned = (flg & 0x10) != 0;
InlinePresentationSpec presentationSpec = (InlinePresentationSpec) in.readTypedObject(InlinePresentationSpec.CREATOR);
String source = in.readString();
String[] autofillHints = (flg & 0x4) == 0 ? null : in.createStringArray();
@@ -306,6 +326,7 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mType);
+ this.mPinned = pinned;
// onConstructed(); // You can define this method to get a callback
}
@@ -325,10 +346,10 @@
};
@DataClass.Generated(
- time = 1579806757327L,
+ time = 1582753084046L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java",
- inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.view.inline.InlinePresentationSpec,java.lang.String,java.lang.String[])\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+ inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.view.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 5700dda..e50da40 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -22,7 +22,9 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.LocaleList;
+import android.os.Parcel;
import android.os.Parcelable;
+import android.view.Display;
import android.view.inline.InlinePresentationSpec;
import com.android.internal.util.DataClass;
@@ -67,7 +69,11 @@
*/
private @NonNull LocaleList mSupportedLocales;
- // TODO(b/149609075): the generated code needs to be manually fixed due to the bug.
+ /**
+ * The extras state propagated from the IME to pass extra data.
+ */
+ private @Nullable Bundle mExtras;
+
/**
* The host input token of the IME that made the request. This will be set by the system for
* safety reasons.
@@ -77,9 +83,12 @@
private @Nullable IBinder mHostInputToken;
/**
- * The extras state propagated from the IME to pass extra data.
+ * The host display id of the IME that made the request. This will be set by the system for
+ * safety reasons.
+ *
+ * @hide
*/
- private @Nullable Bundle mExtras;
+ private int mHostDisplayId;
/**
* @hide
@@ -89,6 +98,24 @@
mHostInputToken = hostInputToken;
}
+ // TODO(b/149609075): remove once IBinder parcelling is natively supported
+ private void parcelHostInputToken(@NonNull Parcel parcel, int flags) {
+ parcel.writeStrongBinder(mHostInputToken);
+ }
+
+ // TODO(b/149609075): remove once IBinder parcelling is natively supported
+ private @Nullable IBinder unparcelHostInputToken(Parcel parcel) {
+ return parcel.readStrongBinder();
+ }
+
+ /**
+ * @hide
+ * @see {@link #mHostDisplayId}.
+ */
+ public void setHostDisplayId(int hostDisplayId) {
+ mHostDisplayId = hostDisplayId;
+ }
+
private void onConstructed() {
Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size());
}
@@ -111,10 +138,17 @@
}
@Nullable
+ private static int defaultHostDisplayId() {
+ return Display.INVALID_DISPLAY;
+ }
+
+ @Nullable
private static Bundle defaultExtras() {
return null;
}
+
+
/** @hide */
abstract static class BaseBuilder {
abstract Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value);
@@ -122,6 +156,8 @@
abstract Builder setHostPackageName(@Nullable String value);
abstract Builder setHostInputToken(IBinder hostInputToken);
+
+ abstract Builder setHostDisplayId(int value);
}
@@ -145,8 +181,9 @@
@NonNull List<InlinePresentationSpec> presentationSpecs,
@NonNull String hostPackageName,
@NonNull LocaleList supportedLocales,
+ @Nullable Bundle extras,
@Nullable IBinder hostInputToken,
- @Nullable Bundle extras) {
+ int hostDisplayId) {
this.mMaxSuggestionCount = maxSuggestionCount;
this.mPresentationSpecs = presentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
@@ -157,8 +194,9 @@
this.mSupportedLocales = supportedLocales;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSupportedLocales);
- this.mHostInputToken = hostInputToken;
this.mExtras = extras;
+ this.mHostInputToken = hostInputToken;
+ this.mHostDisplayId = hostDisplayId;
onConstructed();
}
@@ -202,6 +240,14 @@
}
/**
+ * The extras state propagated from the IME to pass extra data.
+ */
+ @DataClass.Generated.Member
+ public @Nullable Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
* The host input token of the IME that made the request. This will be set by the system for
* safety reasons.
*
@@ -213,11 +259,14 @@
}
/**
- * The extras state propagated from the IME to pass extra data.
+ * The host display id of the IME that made the request. This will be set by the system for
+ * safety reasons.
+ *
+ * @hide
*/
@DataClass.Generated.Member
- public @Nullable Bundle getExtras() {
- return mExtras;
+ public int getHostDisplayId() {
+ return mHostDisplayId;
}
@Override
@@ -231,8 +280,9 @@
"presentationSpecs = " + mPresentationSpecs + ", " +
"hostPackageName = " + mHostPackageName + ", " +
"supportedLocales = " + mSupportedLocales + ", " +
+ "extras = " + mExtras + ", " +
"hostInputToken = " + mHostInputToken + ", " +
- "extras = " + mExtras +
+ "hostDisplayId = " + mHostDisplayId +
" }";
}
@@ -253,8 +303,9 @@
&& java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs)
&& java.util.Objects.equals(mHostPackageName, that.mHostPackageName)
&& java.util.Objects.equals(mSupportedLocales, that.mSupportedLocales)
+ && java.util.Objects.equals(mExtras, that.mExtras)
&& java.util.Objects.equals(mHostInputToken, that.mHostInputToken)
- && java.util.Objects.equals(mExtras, that.mExtras);
+ && mHostDisplayId == that.mHostDisplayId;
}
@Override
@@ -268,27 +319,29 @@
_hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpecs);
_hash = 31 * _hash + java.util.Objects.hashCode(mHostPackageName);
_hash = 31 * _hash + java.util.Objects.hashCode(mSupportedLocales);
- _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
_hash = 31 * _hash + java.util.Objects.hashCode(mExtras);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
+ _hash = 31 * _hash + mHostDisplayId;
return _hash;
}
@Override
@DataClass.Generated.Member
- public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
- if (mHostInputToken != null) flg |= 0x10;
- if (mExtras != null) flg |= 0x20;
+ if (mExtras != null) flg |= 0x10;
+ if (mHostInputToken != null) flg |= 0x20;
dest.writeByte(flg);
dest.writeInt(mMaxSuggestionCount);
dest.writeParcelableList(mPresentationSpecs, flags);
dest.writeString(mHostPackageName);
dest.writeTypedObject(mSupportedLocales, flags);
- if (mHostInputToken != null) dest.writeStrongBinder(mHostInputToken);
if (mExtras != null) dest.writeBundle(mExtras);
+ parcelHostInputToken(dest, flags);
+ dest.writeInt(mHostDisplayId);
}
@Override
@@ -298,7 +351,7 @@
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ InlineSuggestionsRequest(@NonNull android.os.Parcel in) {
+ /* package-private */ InlineSuggestionsRequest(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -308,8 +361,9 @@
in.readParcelableList(presentationSpecs, InlinePresentationSpec.class.getClassLoader());
String hostPackageName = in.readString();
LocaleList supportedLocales = (LocaleList) in.readTypedObject(LocaleList.CREATOR);
- IBinder hostInputToken = (flg & 0x10) == 0 ? null : in.readStrongBinder();
- Bundle extras = (flg & 0x20) == 0 ? null : in.readBundle();
+ Bundle extras = (flg & 0x10) == 0 ? null : in.readBundle();
+ IBinder hostInputToken = unparcelHostInputToken(in);
+ int hostDisplayId = in.readInt();
this.mMaxSuggestionCount = maxSuggestionCount;
this.mPresentationSpecs = presentationSpecs;
@@ -321,8 +375,9 @@
this.mSupportedLocales = supportedLocales;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSupportedLocales);
- this.mHostInputToken = hostInputToken;
this.mExtras = extras;
+ this.mHostInputToken = hostInputToken;
+ this.mHostDisplayId = hostDisplayId;
onConstructed();
}
@@ -336,7 +391,7 @@
}
@Override
- public InlineSuggestionsRequest createFromParcel(@NonNull android.os.Parcel in) {
+ public InlineSuggestionsRequest createFromParcel(@NonNull Parcel in) {
return new InlineSuggestionsRequest(in);
}
};
@@ -352,8 +407,9 @@
private @NonNull List<InlinePresentationSpec> mPresentationSpecs;
private @NonNull String mHostPackageName;
private @NonNull LocaleList mSupportedLocales;
- private @Nullable IBinder mHostInputToken;
private @Nullable Bundle mExtras;
+ private @Nullable IBinder mHostInputToken;
+ private int mHostDisplayId;
private long mBuilderFieldsSet = 0L;
@@ -436,6 +492,17 @@
}
/**
+ * The extras state propagated from the IME to pass extra data.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setExtras(@Nullable Bundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mExtras = value;
+ return this;
+ }
+
+ /**
* The host input token of the IME that made the request. This will be set by the system for
* safety reasons.
*
@@ -445,26 +512,30 @@
@Override
@NonNull Builder setHostInputToken(@Nullable IBinder value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x10;
+ mBuilderFieldsSet |= 0x20;
mHostInputToken = value;
return this;
}
/**
- * The extras state propagated from the IME to pass extra data.
+ * The host display id of the IME that made the request. This will be set by the system for
+ * safety reasons.
+ *
+ * @hide
*/
@DataClass.Generated.Member
- public @NonNull Builder setExtras(@Nullable Bundle value) {
+ @Override
+ @NonNull Builder setHostDisplayId(int value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x20;
- mExtras = value;
+ mBuilderFieldsSet |= 0x40;
+ mHostDisplayId = value;
return this;
}
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull InlineSuggestionsRequest build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x40; // Mark builder used
+ mBuilderFieldsSet |= 0x80; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -476,23 +547,27 @@
mSupportedLocales = defaultSupportedLocales();
}
if ((mBuilderFieldsSet & 0x10) == 0) {
- mHostInputToken = defaultHostInputToken();
+ mExtras = defaultExtras();
}
if ((mBuilderFieldsSet & 0x20) == 0) {
- mExtras = defaultExtras();
+ mHostInputToken = defaultHostInputToken();
+ }
+ if ((mBuilderFieldsSet & 0x40) == 0) {
+ mHostDisplayId = defaultHostDisplayId();
}
InlineSuggestionsRequest o = new InlineSuggestionsRequest(
mMaxSuggestionCount,
mPresentationSpecs,
mHostPackageName,
mSupportedLocales,
+ mExtras,
mHostInputToken,
- mExtras);
+ mHostDisplayId);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x40) != 0) {
+ if ((mBuilderFieldsSet & 0x80) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -500,10 +575,10 @@
}
@DataClass.Generated(
- time = 1581747892762L,
+ time = 1582339908980L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate @android.annotation.Nullable android.os.Bundle mExtras\npublic void setHostInputToken(android.os.IBinder)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 71c9e33..869a929 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -348,6 +348,27 @@
* {@link InputMethodManager#RESULT_UNCHANGED_HIDDEN InputMethodManager.RESULT_UNCHANGED_HIDDEN},
* {@link InputMethodManager#RESULT_SHOWN InputMethodManager.RESULT_SHOWN}, or
* {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}.
+ * @param hideInputToken an opaque {@link android.os.Binder} token to identify which API call
+ * of {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}} is associated
+ * with this callback.
+ * @hide
+ */
+ @MainThread
+ public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
+ IBinder hideInputToken);
+
+ /**
+ * Request that any soft input part of the input method be hidden from the user.
+ * @param flags Provides additional information about the show request.
+ * Currently always 0.
+ * @param resultReceiver The client requesting the show may wish to
+ * be told the impact of their request, which should be supplied here.
+ * The result code should be
+ * {@link InputMethodManager#RESULT_UNCHANGED_SHOWN InputMethodManager.RESULT_UNCHANGED_SHOWN},
+ * {@link InputMethodManager#RESULT_UNCHANGED_HIDDEN
+ * InputMethodManager.RESULT_UNCHANGED_HIDDEN},
+ * {@link InputMethodManager#RESULT_SHOWN InputMethodManager.RESULT_SHOWN}, or
+ * {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}.
*/
@MainThread
public void hideSoftInput(int flags, ResultReceiver resultReceiver);
@@ -366,4 +387,13 @@
* @hide
*/
public void setCurrentShowInputToken(IBinder showInputToken);
+
+ /**
+ * Update token of the client window requesting {@link #hideSoftInput(int, ResultReceiver)}
+ * @param hideInputToken dummy app window token for window requesting
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}
+ * @hide
+ */
+ public void setCurrentHideInputToken(IBinder hideInputToken);
+
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 39d5f5c..f3aa314 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1709,7 +1709,7 @@
}
try {
- return mService.hideSoftInput(mClient, flags, resultReceiver);
+ return mService.hideSoftInput(mClient, windowToken, flags, resultReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1986,7 +1986,8 @@
@UnsupportedAppUsage
void closeCurrentInput() {
try {
- mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null);
+ mService.hideSoftInput(
+ mClient, mCurRootView.getView().getWindowToken(), HIDE_NOT_ALWAYS, null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2056,6 +2057,21 @@
}
/**
+ * Notify IME directly to remove surface as it is no longer visible.
+ * @hide
+ */
+ public void removeImeSurface() {
+ synchronized (mH) {
+ try {
+ if (mCurMethod != null) {
+ mCurMethod.removeImeSurface();
+ }
+ } catch (RemoteException re) {
+ }
+ }
+ }
+
+ /**
* Report the current selection range.
*
* <p><strong>Editor authors</strong>, you need to call this method whenever
diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java
index eb81628..0d688ff 100644
--- a/core/java/android/view/inputmethod/InputMethodSession.java
+++ b/core/java/android/view/inputmethod/InputMethodSession.java
@@ -191,4 +191,10 @@
* @hide
*/
public void notifyImeHidden();
+
+ /**
+ * Notify IME directly to remove surface as it is no longer visible.
+ * @hide
+ */
+ public void removeImeSurface();
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 2d27a78..53541f7 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -369,10 +369,22 @@
public abstract boolean getDisplayZoomControls();
/**
- * Enables or disables file access within WebView. File access is enabled by
- * default. Note that this enables or disables file system access only.
- * Assets and resources are still accessible using file:///android_asset and
- * file:///android_res.
+ * Enables or disables file access within WebView.
+ * Note that this enables or disables file system access only. Assets and resources
+ * are still accessible using file:///android_asset and file:///android_res.
+ * <p class="note">
+ * <b>Note:</b> Apps should not open {@code file://} URLs from any external source in
+ * WebView, don't enable this if your app accepts arbitrary URLs from external sources.
+ * It's recommended to always use
+ * <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader">
+ * androidx.webkit.WebViewAssetLoader</a> to access files including assets and resources over
+ * {@code http(s)://} schemes, instead of {@code file://} URLs. To prevent possible security
+ * issues targeting {@link android.os.Build.VERSION_CODES#Q} and earlier, you should explicitly
+ * set this value to {@code false}.
+ * <p>
+ * The default value is {@code true} for apps targeting
+ * {@link android.os.Build.VERSION_CODES#Q} and below, and {@code false} when targeting
+ * {@link android.os.Build.VERSION_CODES#R} and above.
*/
public abstract void setAllowFileAccess(boolean allow);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index a299b01..8ea824d 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -353,8 +353,9 @@
// Gets the startX for new style, which should be bounded by the horizontal bounds.
// Also calculates the left/right cut width for pixel copy.
- leftBound += mViewCoordinatesInSurface[0];
- rightBound += mViewCoordinatesInSurface[0];
+ leftBound = Math.max(leftBound + mViewCoordinatesInSurface[0], 0);
+ rightBound = Math.min(
+ rightBound + mViewCoordinatesInSurface[0], mContentCopySurface.mWidth);
mLeftCutWidth = Math.max(0, leftBound - startX);
mRightCutWidth = Math.max(0, startX + mSourceWidth - rightBound);
startX = Math.max(startX, leftBound);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 815cc5c..f3243aa 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -13174,7 +13174,7 @@
stream.addProperty("text:selectionStart", getSelectionStart());
stream.addProperty("text:selectionEnd", getSelectionEnd());
stream.addProperty("text:curTextColor", mCurTextColor);
- stream.addProperty("text:text", mText == null ? null : mText.toString());
+ stream.addUserProperty("text:text", mText == null ? null : mText.toString());
stream.addProperty("text:gravity", mGravity);
}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index bb40465..5cdcab0 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -412,8 +412,13 @@
* Class to wrap TextToSpeech for shortcut dialog spoken feedback.
*/
private class TtsPrompt implements TextToSpeech.OnInitListener {
+ private static final int RETRY_MILLIS = 1000;
+
private final CharSequence mText;
+
+ private int mRetryCount = 3;
private boolean mDismiss;
+ private boolean mLanguageReady = false;
private TextToSpeech mTts;
TtsPrompt(String serviceName) {
@@ -437,17 +442,15 @@
playNotificationTone();
return;
}
- mHandler.sendMessage(PooledLambda.obtainMessage(TtsPrompt::play, this));
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ TtsPrompt::waitForTtsReady, this));
}
private void play() {
if (mDismiss) {
return;
}
- int status = TextToSpeech.ERROR;
- if (setLanguage(Locale.getDefault())) {
- status = mTts.speak(mText, TextToSpeech.QUEUE_FLUSH, null, null);
- }
+ final int status = mTts.speak(mText, TextToSpeech.QUEUE_FLUSH, null, null);
if (status != TextToSpeech.SUCCESS) {
Slog.d(TAG, "Tts play fail");
playNotificationTone();
@@ -455,21 +458,42 @@
}
/**
- * @return false if tts language is not available
+ * Waiting for tts is ready to speak. Trying again if tts language pack is not available
+ * or tts voice data is not installed yet.
*/
- private boolean setLanguage(final Locale locale) {
- int status = mTts.isLanguageAvailable(locale);
- if (status == TextToSpeech.LANG_MISSING_DATA
- || status == TextToSpeech.LANG_NOT_SUPPORTED) {
- return false;
+ private void waitForTtsReady() {
+ if (mDismiss) {
+ return;
}
- mTts.setLanguage(locale);
- Voice voice = mTts.getVoice();
- if (voice == null || (voice.getFeatures() != null && voice.getFeatures()
- .contains(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED))) {
- return false;
+ if (!mLanguageReady) {
+ final int status = mTts.setLanguage(Locale.getDefault());
+ // True if language is available and TTS#loadVoice has called once
+ // that trigger TTS service to start initialization.
+ mLanguageReady = status != TextToSpeech.LANG_MISSING_DATA
+ && status != TextToSpeech.LANG_NOT_SUPPORTED;
}
- return true;
+ if (mLanguageReady) {
+ final Voice voice = mTts.getVoice();
+ final boolean voiceDataInstalled = voice != null
+ && voice.getFeatures() != null
+ && !voice.getFeatures().contains(
+ TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED);
+ if (voiceDataInstalled) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ TtsPrompt::play, this));
+ return;
+ }
+ }
+
+ if (mRetryCount == 0) {
+ Slog.d(TAG, "Tts not ready to speak.");
+ playNotificationTone();
+ return;
+ }
+ // Retry if TTS service not ready yet.
+ mRetryCount -= 1;
+ mHandler.sendMessageDelayed(PooledLambda.obtainMessage(
+ TtsPrompt::waitForTtsReady, this), RETRY_MILLIS);
}
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index dc6942c..6a0b443 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -71,8 +71,8 @@
void noteSyncStart(String name, int uid);
void noteSyncFinish(String name, int uid);
- void noteJobStart(String name, int uid, int standbyBucket, int jobid);
- void noteJobFinish(String name, int uid, int stopReason, int standbyBucket, int jobid);
+ void noteJobStart(String name, int uid);
+ void noteJobFinish(String name, int uid, int stopReason);
void noteStartWakelock(int uid, int pid, String name, String historyName,
int type, boolean unimportantForLogging);
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 417e23f..71ee8af 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -33,7 +33,7 @@
import android.service.voice.IVoiceInteractionSession;
interface IVoiceInteractionManagerService {
- void showSession(IVoiceInteractionService service, in Bundle sessionArgs, int flags);
+ void showSession(in Bundle sessionArgs, int flags);
boolean deliverNewSession(IBinder token, IVoiceInteractionSession session,
IVoiceInteractor interactor);
boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags);
@@ -52,68 +52,91 @@
/**
* Gets the registered Sound model for keyphrase detection for the current user.
* May be null if no matching sound model exists.
+ * Caller must either be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
+ * enrollment application detected by
+ * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
*
* @param keyphraseId The unique identifier for the keyphrase.
* @param bcp47Locale The BCP47 language tag for the keyphrase's locale.
+ * @RequiresPermission Manifest.permission.MANAGE_VOICE_KEYPHRASES
*/
@UnsupportedAppUsage
SoundTrigger.KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, in String bcp47Locale);
/**
- * Add/Update the given keyphrase sound model.
+ * Add/Update the given keyphrase sound model for the current user.
+ * Caller must either be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
+ * enrollment application detected by
+ * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
+ *
+ * @param model The keyphrase sound model to store peristantly.
+ * @RequiresPermission Manifest.permission.MANAGE_VOICE_KEYPHRASES
*/
int updateKeyphraseSoundModel(in SoundTrigger.KeyphraseSoundModel model);
/**
* Deletes the given keyphrase sound model for the current user.
+ * Caller must either be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
+ * enrollment application detected by
+ * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
*
* @param keyphraseId The unique identifier for the keyphrase.
* @param bcp47Locale The BCP47 language tag for the keyphrase's locale.
+ * @RequiresPermission Manifest.permission.MANAGE_VOICE_KEYPHRASES
*/
int deleteKeyphraseSoundModel(int keyphraseId, in String bcp47Locale);
/**
* Gets the properties of the DSP hardware on this device, null if not present.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*/
- SoundTrigger.ModuleProperties getDspModuleProperties(in IVoiceInteractionService service);
+ SoundTrigger.ModuleProperties getDspModuleProperties();
/**
* Indicates if there's a keyphrase sound model available for the given keyphrase ID and the
* user ID of the caller.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*
- * @param service The current VoiceInteractionService.
* @param keyphraseId The unique identifier for the keyphrase.
* @param bcp47Locale The BCP47 language tag for the keyphrase's locale.
*/
- boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId,
- String bcp47Locale);
+ boolean isEnrolledForKeyphrase(int keyphraseId, String bcp47Locale);
/**
* Generates KeyphraseMetadata for an enrolled sound model based on keyphrase string, locale,
* and the user ID of the caller.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*
- * @param service The current VoiceInteractionService
* @param keyphrase Keyphrase text associated with the enrolled model
* @param bcp47Locale The BCP47 language tag for the keyphrase's locale.
* @return The metadata for the enrolled voice model bassed on the passed in parameters. Null if
* no matching voice model exists.
*/
- KeyphraseMetadata getEnrolledKeyphraseMetadata(IVoiceInteractionService service,
- String keyphrase, String bcp47Locale);
+ KeyphraseMetadata getEnrolledKeyphraseMetadata(String keyphrase, String bcp47Locale);
/**
* Starts a recognition for the given keyphrase.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*/
- int startRecognition(in IVoiceInteractionService service, int keyphraseId,
- in String bcp47Locale, in IRecognitionStatusCallback callback,
+ int startRecognition(int keyphraseId, in String bcp47Locale,
+ in IRecognitionStatusCallback callback,
in SoundTrigger.RecognitionConfig recognitionConfig);
/**
* Stops a recognition for the given keyphrase.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*/
- int stopRecognition(in IVoiceInteractionService service, int keyphraseId,
- in IRecognitionStatusCallback callback);
+ int stopRecognition(int keyphraseId, in IRecognitionStatusCallback callback);
/**
* Set a model specific ModelParams with the given value. This
* parameter will keep its value for the duration the model is loaded regardless of starting and
* stopping recognition. Once the model is unloaded, the value will be lost.
* queryParameter should be checked first before calling this method.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*
- * @param service The current VoiceInteractionService.
* @param keyphraseId The unique identifier for the keyphrase.
* @param modelParam ModelParams
* @param value Value to set
@@ -123,36 +146,37 @@
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
* if API is not supported by HAL
*/
- int setParameter(in IVoiceInteractionService service, int keyphraseId,
- in ModelParams modelParam, int value);
+ int setParameter(int keyphraseId, in ModelParams modelParam, int value);
/**
* Get a model specific ModelParams. This parameter will keep its value
* for the duration the model is loaded regardless of starting and stopping recognition.
* Once the model is unloaded, the value will be lost. If the value is not set, a default
* value is returned. See ModelParams for parameter default values.
* queryParameter should be checked first before calling this method.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*
- * @param service The current VoiceInteractionService.
* @param keyphraseId The unique identifier for the keyphrase.
* @param modelParam ModelParams
* @return value of parameter
*/
- int getParameter(in IVoiceInteractionService service, int keyphraseId,
- in ModelParams modelParam);
+ int getParameter(int keyphraseId, in ModelParams modelParam);
/**
* Determine if parameter control is supported for the given model handle.
* This method should be checked prior to calling setParameter or getParameter.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*
- * @param service The current VoiceInteractionService.
* @param keyphraseId The unique identifier for the keyphrase.
* @param modelParam ModelParams
* @return supported range of parameter, null if not supported
*/
- @nullable SoundTrigger.ModelParamRange queryParameter(in IVoiceInteractionService service,
- int keyphraseId, in ModelParams modelParam);
+ @nullable SoundTrigger.ModelParamRange queryParameter(int keyphraseId,
+ in ModelParams modelParam);
/**
* @return the component name for the currently active voice interaction service
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
ComponentName getActiveServiceComponentName();
@@ -164,59 +188,70 @@
* @param sourceFlags flags indicating the source of this show
* @param showCallback optional callback to be notified when the session was shown
* @param activityToken optional token of activity that needs to be on top
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
boolean showSessionForActiveService(in Bundle args, int sourceFlags,
IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken);
/**
* Hides the session from the active service, if it is showing.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
void hideCurrentSession();
/**
* Notifies the active service that a launch was requested from the Keyguard. This will only
* be called if {@link #activeServiceSupportsLaunchFromKeyguard()} returns true.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
void launchVoiceAssistFromKeyguard();
/**
* Indicates whether there is a voice session running (but not necessarily showing).
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
boolean isSessionRunning();
/**
* Indicates whether the currently active voice interaction service is capable of handling the
* assist gesture.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
boolean activeServiceSupportsAssist();
/**
* Indicates whether the currently active voice interaction service is capable of being launched
* from the lockscreen.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
boolean activeServiceSupportsLaunchFromKeyguard();
/**
* Called when the lockscreen got shown.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
void onLockscreenShown();
/**
* Register a voice interaction listener.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener);
/**
* Checks the availability of a set of voice actions for the current active voice service.
* Returns all supported voice actions.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
void getActiveServiceSupportedActions(in List<String> voiceActions,
in IVoiceActionCheckCallback callback);
/**
* Provide hints for showing UI.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*/
- void setUiHints(in IVoiceInteractionService service, in Bundle hints);
+ void setUiHints(in Bundle hints);
/**
* Requests a list of supported actions from a specific activity.
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index ef9b3d10..73ef8c6 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
+import android.database.DatabaseUtils;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.graphics.Point;
@@ -38,6 +39,7 @@
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsProvider;
import android.provider.MediaStore;
+import android.provider.MediaStore.Files.FileColumns;
import android.provider.MetadataReader;
import android.system.Int64Ref;
import android.text.TextUtils;
@@ -333,15 +335,17 @@
if (isDirectory) {
FileUtils.deleteContents(file);
}
- if (!file.delete()) {
+ // We could be deleting pending media which doesn't have any content yet, so only throw
+ // if the file exists and we fail to delete it.
+ if (file.exists() && !file.delete()) {
throw new IllegalStateException("Failed to delete " + file);
}
onDocIdChanged(docId);
- removeFromMediaStore(visibleFile, isDirectory);
+ removeFromMediaStore(visibleFile);
}
- private void removeFromMediaStore(@Nullable File visibleFile, boolean isFolder)
+ private void removeFromMediaStore(@Nullable File visibleFile)
throws FileNotFoundException {
// visibleFolder is null if we're removing a document from external thumb drive or SD card.
if (visibleFile != null) {
@@ -350,21 +354,19 @@
try {
final ContentResolver resolver = getContext().getContentResolver();
final Uri externalUri = MediaStore.Files.getContentUri("external");
+ final Bundle queryArgs = new Bundle();
+ queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_INCLUDE);
- // Remove media store entries for any files inside this directory, using
- // path prefix match. Logic borrowed from MtpDatabase.
- if (isFolder) {
- final String path = visibleFile.getAbsolutePath() + "/";
- resolver.delete(externalUri,
- "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
- new String[]{path + "%", Integer.toString(path.length()), path});
- }
-
- // Remove media store entry for this exact file.
- final String path = visibleFile.getAbsolutePath();
- resolver.delete(externalUri,
- "_data LIKE ?1 AND lower(_data)=lower(?2)",
- new String[]{path, path});
+ // Remove the media store entry corresponding to visibleFile and if it is a
+ // directory, also remove media store entries for any files inside this directory.
+ // Logic borrowed from com.android.providers.media.scan.ModernMediaScanner.
+ final String pathEscapedForLike = DatabaseUtils.escapeForLike(
+ visibleFile.getAbsolutePath());
+ ContentResolver.includeSqlSelectionArgs(queryArgs,
+ FileColumns.DATA + " LIKE ? ESCAPE '\\' OR "
+ + FileColumns.DATA + " LIKE ? ESCAPE '\\'",
+ new String[] {pathEscapedForLike + "/%", pathEscapedForLike});
+ resolver.delete(externalUri, queryArgs);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index 20cd7c2..9a22686 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -41,5 +41,5 @@
boolean shouldOfferSwitchingToNextInputMethod();
void notifyUserAction();
void reportPreRendered(in EditorInfo info);
- void applyImeVisibility(IBinder showInputToken, boolean setVisible);
+ void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 382a254..3876976 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -16,6 +16,8 @@
package com.android.internal.inputmethod;
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
@@ -25,6 +27,7 @@
* Provides useful methods for debugging.
*/
public final class InputMethodDebug {
+
/**
* Not intended to be instantiated.
*/
@@ -174,4 +177,71 @@
return joiner.setEmptyValue("(none)").toString();
}
+
+
+ /**
+ * Converts {@link SoftInputShowHideReason} to {@link String} for history dump.
+ */
+ public static String softInputDisplayReasonToString(@SoftInputShowHideReason int reason) {
+ switch (reason) {
+ case SoftInputShowHideReason.SHOW_SOFT_INPUT:
+ return "SHOW_SOFT_INPUT";
+ case SoftInputShowHideReason.ATTACH_NEW_INPUT:
+ return "ATTACH_NEW_INPUT";
+ case SoftInputShowHideReason.SHOW_MY_SOFT_INPUT:
+ return "SHOW_MY_SOFT_INPUT";
+ case SoftInputShowHideReason.HIDE_SOFT_INPUT:
+ return "HIDE_SOFT_INPUT";
+ case SoftInputShowHideReason.HIDE_MY_SOFT_INPUT:
+ return "HIDE_MY_SOFT_INPUT";
+ case SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV:
+ return "SHOW_AUTO_EDITOR_FORWARD_NAV";
+ case SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV:
+ return "SHOW_STATE_VISIBLE_FORWARD_NAV";
+ case SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE:
+ return "SHOW_STATE_ALWAYS_VISIBLE";
+ case SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE:
+ return "SHOW_SETTINGS_ON_CHANGE";
+ case SoftInputShowHideReason.HIDE_SWITCH_USER:
+ return "HIDE_SWITCH_USER";
+ case SoftInputShowHideReason.HIDE_INVALID_USER:
+ return "HIDE_INVALID_USER";
+ case SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW:
+ return "HIDE_UNSPECIFIED_WINDOW";
+ case SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV:
+ return "HIDE_STATE_HIDDEN_FORWARD_NAV";
+ case SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE:
+ return "HIDE_ALWAYS_HIDDEN_STATE";
+ case SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND:
+ return "HIDE_RESET_SHELL_COMMAND";
+ case SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE:
+ return "HIDE_SETTINGS_ON_CHANGE";
+ case SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME:
+ return "HIDE_POWER_BUTTON_GO_HOME";
+ case SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED:
+ return "HIDE_DOCKED_STACK_ATTACHED";
+ case SoftInputShowHideReason.HIDE_RECENTS_ANIMATION:
+ return "HIDE_RECENTS_ANIMATION";
+ default:
+ return "Unknown=" + reason;
+ }
+ }
+
+ /**
+ * Return a fixed size string of the object.
+ * TODO(b/141738570): Take & return with StringBuilder to make more memory efficient.
+ */
+ @NonNull
+ @AnyThread
+ public static String objToString(Object obj) {
+ if (obj == null) {
+ return "null";
+ }
+ StringBuilder sb = new StringBuilder(64);
+ sb.setLength(0);
+ sb.append(obj.getClass().getName());
+ sb.append("@");
+ sb.append(Integer.toHexString(obj.hashCode()));
+ return sb.toString();
+ }
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 9eeef96..e5475f8 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -371,18 +371,20 @@
/**
* Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean)}.
*
- * @param showInputToken dummy token that maps to window requesting
- * {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)}
+ * @param showOrHideInputToken dummy token that maps to window requesting
+ * {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or
+ * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow
+ * (IBinder, int)}
* @param setVisible {@code true} to set IME visible, else hidden.
*/
@AnyThread
- public void applyImeVisibility(IBinder showInputToken, boolean setVisible) {
+ public void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
}
try {
- ops.applyImeVisibility(showInputToken, setVisible);
+ ops.applyImeVisibility(showOrHideInputToken, setVisible);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
new file mode 100644
index 0000000..79397b8
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.view.WindowManager.LayoutParams;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Describes the reason why Soft input window visible / hidden.
+ */
+@Retention(SOURCE)
+@IntDef(value = {
+ SoftInputShowHideReason.SHOW_SOFT_INPUT,
+ SoftInputShowHideReason.ATTACH_NEW_INPUT,
+ SoftInputShowHideReason.SHOW_MY_SOFT_INPUT,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT,
+ SoftInputShowHideReason.HIDE_MY_SOFT_INPUT,
+ SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV,
+ SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV,
+ SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE,
+ SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE,
+ SoftInputShowHideReason.HIDE_SWITCH_USER,
+ SoftInputShowHideReason.HIDE_INVALID_USER,
+ SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW,
+ SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV,
+ SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE,
+ SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND,
+ SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE,
+ SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME,
+ SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED,
+ SoftInputShowHideReason.HIDE_RECENTS_ANIMATION})
+public @interface SoftInputShowHideReason {
+ /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
+ int SHOW_SOFT_INPUT = 0;
+
+ /** Show soft input when {@code InputMethodManagerService#attachNewInputLocked} called. */
+ int ATTACH_NEW_INPUT = 1;
+
+ /** Show soft input by {@code InputMethodManagerService#showMySoftInput}. */
+ int SHOW_MY_SOFT_INPUT = 2;
+
+ /**
+ * Hide soft input by
+ * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow}.
+ */
+ int HIDE_SOFT_INPUT = 3;
+
+ /** Hide soft input by {@code InputMethodManagerService#hideMySoftInput}. */
+ int HIDE_MY_SOFT_INPUT = 4;
+
+ /**
+ * Show soft input when navigated forward to the window (with
+ * {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION}} which the focused view is text
+ * editor and system will auto-show the IME when the window can resize or running on a large
+ * screen.
+ */
+ int SHOW_AUTO_EDITOR_FORWARD_NAV = 5;
+
+ /**
+ * Show soft input when navigated forward to the window with
+ * {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION} and
+ * {@link LayoutParams#SOFT_INPUT_STATE_VISIBLE}.
+ */
+ int SHOW_STATE_VISIBLE_FORWARD_NAV = 6;
+
+ /**
+ * Show soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE}.
+ */
+ int SHOW_STATE_ALWAYS_VISIBLE = 7;
+
+ /**
+ * Show soft input during {@code InputMethodManagerService} receive changes from
+ * {@code SettingsProvider}.
+ */
+ int SHOW_SETTINGS_ON_CHANGE = 8;
+
+ /** Hide soft input during switching user. */
+ int HIDE_SWITCH_USER = 9;
+
+ /** Hide soft input when the user is invalid. */
+ int HIDE_INVALID_USER = 10;
+
+ /**
+ * Hide soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_UNSPECIFIED} which
+ * the focused view is not text editor.
+ */
+ int HIDE_UNSPECIFIED_WINDOW = 11;
+
+ /**
+ * Hide soft input when navigated forward to the window with
+ * {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION} and
+ * {@link LayoutParams#SOFT_INPUT_STATE_HIDDEN}.
+ */
+ int HIDE_STATE_HIDDEN_FORWARD_NAV = 12;
+
+ /**
+ * Hide soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN}.
+ */
+ int HIDE_ALWAYS_HIDDEN_STATE = 13;
+
+ /** Hide soft input when "adb shell ime <command>" called. */
+ int HIDE_RESET_SHELL_COMMAND = 14;
+
+ /**
+ * Hide soft input during {@code InputMethodManagerService} receive changes from
+ * {@code SettingsProvider}.
+ */
+ int HIDE_SETTINGS_ON_CHANGE = 15;
+
+ /**
+ * Hide soft input from {@link com.android.server.policy.PhoneWindowManager} when setting
+ * {@link com.android.internal.R.integer#config_shortPressOnPowerBehavior} in config.xml as
+ * dismiss IME.
+ */
+ int HIDE_POWER_BUTTON_GO_HOME = 16;
+
+ /** Hide soft input when attaching docked stack. */
+ int HIDE_DOCKED_STACK_ATTACHED = 17;
+
+ /**
+ * Hide soft input when {@link com.android.server.wm.RecentsAnimationController} starts
+ * intercept touch from app window.
+ */
+ int HIDE_RECENTS_ANIMATION = 18;
+}
diff --git a/core/java/com/android/internal/logging/InstanceIdSequence.java b/core/java/com/android/internal/logging/InstanceIdSequence.java
index aa507e5..3464310 100644
--- a/core/java/com/android/internal/logging/InstanceIdSequence.java
+++ b/core/java/com/android/internal/logging/InstanceIdSequence.java
@@ -25,7 +25,7 @@
import java.util.Random;
/**
- * Generates random InstanceIds in range [0, instanceIdMax) for passing to
+ * Generates random InstanceIds in range [1, instanceIdMax] for passing to
* UiEventLogger.logWithInstanceId(). Holds a SecureRandom, which self-seeds on
* first use; try to give it a long lifetime. Safe for concurrent use.
*/
@@ -34,12 +34,12 @@
private final Random mRandom = new SecureRandom();
/**
- * Constructs a sequence with identifiers [0, instanceIdMax). Capped at INSTANCE_ID_MAX.
+ * Constructs a sequence with identifiers [1, instanceIdMax]. Capped at INSTANCE_ID_MAX.
* @param instanceIdMax Limiting value of identifiers. Normally positive: otherwise you get
- * an all-zero sequence.
+ * an all-1 sequence.
*/
public InstanceIdSequence(int instanceIdMax) {
- mInstanceIdMax = min(max(0, instanceIdMax), InstanceId.INSTANCE_ID_MAX);
+ mInstanceIdMax = min(max(1, instanceIdMax), InstanceId.INSTANCE_ID_MAX);
}
/**
@@ -47,7 +47,7 @@
* @return new InstanceId
*/
public InstanceId newInstanceId() {
- return newInstanceIdInternal(mRandom.nextInt(mInstanceIdMax));
+ return newInstanceIdInternal(1 + mRandom.nextInt(mInstanceIdMax));
}
/**
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 75eb4aa..140c410 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -55,11 +55,8 @@
protected void saveLog(LogMaker log) {
// TODO(b/116684537): Flag guard logging to event log and statsd socket.
EventLogTags.writeSysuiMultiAction(log.serialize());
- if (log.getCategory() != MetricsEvent.RESERVED_FOR_LOGBUILDER_COUNTER
- && log.getCategory() != MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM) {
- FrameworkStatsLog.write(FrameworkStatsLog.KEY_VALUE_PAIRS_ATOM,
- /* UID is retrieved from statsd side */ 0, log.getEntries());
- }
+ FrameworkStatsLog.write(FrameworkStatsLog.KEY_VALUE_PAIRS_ATOM,
+ /* UID is retrieved from statsd side */ 0, log.getEntries());
}
public static final int VIEW_UNKNOWN = MetricsEvent.VIEW_UNKNOWN;
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index fd4b5ab..40e4f4d 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -55,7 +55,7 @@
void showSoftInput(in IBinder showInputToken, int flags, in ResultReceiver resultReceiver);
- void hideSoftInput(int flags, in ResultReceiver resultReceiver);
+ void hideSoftInput(in IBinder hideInputToken, int flags, in ResultReceiver resultReceiver);
void changeInputMethodSubtype(in InputMethodSubtype subtype);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 0337ddd..3f03f2a 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -43,7 +43,7 @@
boolean showSoftInput(in IInputMethodClient client, IBinder windowToken, int flags,
in ResultReceiver resultReceiver);
- boolean hideSoftInput(in IInputMethodClient client, int flags,
+ boolean hideSoftInput(in IInputMethodClient client, IBinder windowToken, int flags,
in ResultReceiver resultReceiver);
// If windowToken is null, this just does startInput(). Otherwise this reports that a window
// has gained focus, and if 'attribute' is non-null then also does startInput.
diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl
index 664643c..0319e36 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -50,4 +50,6 @@
void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo);
void notifyImeHidden();
+
+ void removeImeSurface();
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index d27be27..ade2c7d 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -208,7 +208,6 @@
"libseccomp_policy",
"libgrallocusage",
"libscrypt_static",
- "libstatssocket",
],
shared_libs: [
@@ -266,6 +265,7 @@
"libdl",
"libdl_android",
"libstatslog",
+ "libstatssocket",
"libtimeinstate",
"server_configurable_flags",
"libstatspull",
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 84acf9a..0a2b1d4 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -114,9 +114,9 @@
uint32_t publishedSeq = mNextPublishedSeq++;
status_t status =
- mInputPublisher.publishKeyEvent(publishedSeq, event->getDeviceId(), event->getSource(),
- event->getDisplayId(), event->getHmac(),
- event->getAction(), event->getFlags(),
+ mInputPublisher.publishKeyEvent(publishedSeq, event->getId(), event->getDeviceId(),
+ event->getSource(), event->getDisplayId(),
+ event->getHmac(), event->getAction(), event->getFlags(),
event->getKeyCode(), event->getScanCode(),
event->getMetaState(), event->getRepeatCount(),
event->getDownTime(), event->getEventTime());
@@ -138,12 +138,12 @@
for (size_t i = 0; i <= event->getHistorySize(); i++) {
publishedSeq = mNextPublishedSeq++;
status_t status =
- mInputPublisher.publishMotionEvent(publishedSeq, event->getDeviceId(),
- event->getSource(), event->getDisplayId(),
- event->getHmac(), event->getAction(),
- event->getActionButton(), event->getFlags(),
- event->getEdgeFlags(), event->getMetaState(),
- event->getButtonState(),
+ mInputPublisher.publishMotionEvent(publishedSeq, event->getId(),
+ event->getDeviceId(), event->getSource(),
+ event->getDisplayId(), event->getHmac(),
+ event->getAction(), event->getActionButton(),
+ event->getFlags(), event->getEdgeFlags(),
+ event->getMetaState(), event->getButtonState(),
event->getClassification(), event->getXScale(),
event->getYScale(), event->getXOffset(),
event->getYOffset(), event->getXPrecision(),
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index bbe563e..54567e5 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -75,6 +75,7 @@
jmethodID obtain;
jmethodID recycle;
+ jfieldID mId;
jfieldID mDeviceId;
jfieldID mSource;
jfieldID mDisplayId;
@@ -96,6 +97,7 @@
ScopedLocalRef<jbyteArray> hmac = toJbyteArray(env, event->getHmac());
jobject eventObj =
env->CallStaticObjectMethod(gKeyEventClassInfo.clazz, gKeyEventClassInfo.obtain,
+ event->getId(),
nanoseconds_to_milliseconds(event->getDownTime()),
nanoseconds_to_milliseconds(event->getEventTime()),
event->getAction(), event->getKeyCode(),
@@ -114,6 +116,7 @@
status_t android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj,
KeyEvent* event) {
+ jint id = env->GetIntField(eventObj, gKeyEventClassInfo.mId);
jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId);
jint source = env->GetIntField(eventObj, gKeyEventClassInfo.mSource);
jint displayId = env->GetIntField(eventObj, gKeyEventClassInfo.mDisplayId);
@@ -131,7 +134,7 @@
jlong downTime = env->GetLongField(eventObj, gKeyEventClassInfo.mDownTime);
jlong eventTime = env->GetLongField(eventObj, gKeyEventClassInfo.mEventTime);
- event->initialize(deviceId, source, displayId, *hmac, action, flags, keyCode, scanCode,
+ event->initialize(id, deviceId, source, displayId, *hmac, action, flags, keyCode, scanCode,
metaState, repeatCount, milliseconds_to_nanoseconds(downTime),
milliseconds_to_nanoseconds(eventTime));
return OK;
@@ -159,14 +162,18 @@
return KeyEvent::getKeyCodeFromLabel(keyLabel.c_str());
}
+static jint android_view_KeyEvent_nativeNextId() {
+ return static_cast<jint>(InputEvent::nextId());
+}
// ----------------------------------------------------------------------------
static const JNINativeMethod g_methods[] = {
- { "nativeKeyCodeToString", "(I)Ljava/lang/String;",
- (void*)android_view_KeyEvent_nativeKeyCodeToString},
- { "nativeKeyCodeFromString", "(Ljava/lang/String;)I",
- (void*)android_view_KeyEvent_nativeKeyCodeFromString},
+ {"nativeKeyCodeToString", "(I)Ljava/lang/String;",
+ (void*)android_view_KeyEvent_nativeKeyCodeToString},
+ {"nativeKeyCodeFromString", "(Ljava/lang/String;)I",
+ (void*)android_view_KeyEvent_nativeKeyCodeFromString},
+ {"nativeNextId", "()I", (void*)android_view_KeyEvent_nativeNextId},
};
int register_android_view_KeyEvent(JNIEnv* env) {
@@ -175,10 +182,11 @@
gKeyEventClassInfo.obtain =
GetStaticMethodIDOrDie(env, gKeyEventClassInfo.clazz, "obtain",
- "(JJIIIIIIIII[BLjava/lang/String;)Landroid/view/KeyEvent;");
+ "(IJJIIIIIIIII[BLjava/lang/String;)Landroid/view/KeyEvent;");
gKeyEventClassInfo.recycle = GetMethodIDOrDie(env, gKeyEventClassInfo.clazz,
"recycle", "()V");
+ gKeyEventClassInfo.mId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mId", "I");
gKeyEventClassInfo.mDeviceId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDeviceId", "I");
gKeyEventClassInfo.mSource = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mSource", "I");
gKeyEventClassInfo.mDisplayId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDisplayId",
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 3335fb2..9816d71 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -369,9 +369,10 @@
env->DeleteLocalRef(pointerCoordsObj);
}
- event->initialize(deviceId, source, displayId, INVALID_HMAC, action, 0, flags, edgeFlags,
- metaState, buttonState, static_cast<MotionClassification>(classification),
- 1 /*xScale*/, 1 /*yScale*/, xOffset, yOffset, xPrecision, yPrecision,
+ event->initialize(InputEvent::nextId(), deviceId, source, displayId, INVALID_HMAC, action, 0,
+ flags, edgeFlags, metaState, buttonState,
+ static_cast<MotionClassification>(classification), 1 /*xScale*/, 1 /*yScale*/,
+ xOffset, yOffset, xPrecision, yPrecision,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
downTimeNanos, eventTimeNanos, pointerCount, pointerProperties,
rawPointerCoords);
@@ -592,6 +593,11 @@
return reinterpret_cast<jlong>(destEvent);
}
+static jint android_view_MotionEvent_nativeGetId(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getId();
+}
+
static jint android_view_MotionEvent_nativeGetDeviceId(jlong nativePtr) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
return event->getDeviceId();
@@ -790,6 +796,7 @@
// --------------- @CriticalNative ------------------
{"nativeCopy", "(JJZ)J", (void*)android_view_MotionEvent_nativeCopy},
+ {"nativeGetId", "(J)I", (void*)android_view_MotionEvent_nativeGetId},
{"nativeGetDeviceId", "(J)I", (void*)android_view_MotionEvent_nativeGetDeviceId},
{"nativeGetSource", "(J)I", (void*)android_view_MotionEvent_nativeGetSource},
{"nativeSetSource", "(JI)V", (void*)android_view_MotionEvent_nativeSetSource},
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index e4141e0..359fd48 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -626,12 +626,6 @@
// Set the jemalloc decay time to 1.
mallopt(M_DECAY_TIME, 1);
-
- // Maybe initialize GWP-ASan here. Must be called after
- // mallopt(M_SET_ZYGOTE_CHILD).
- bool ForceEnableGwpAsan = false;
- android_mallopt(M_INITIALIZE_GWP_ASAN, &ForceEnableGwpAsan,
- sizeof(ForceEnableGwpAsan));
}
static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto
index d2bf205..41863bb 100644
--- a/core/proto/android/app/job/enums.proto
+++ b/core/proto/android/app/job/enums.proto
@@ -34,5 +34,5 @@
STOP_REASON_TIMEOUT = 3;
STOP_REASON_DEVICE_IDLE = 4;
STOP_REASON_DEVICE_THERMAL = 5;
- STOP_REASON_RESTRAINED = 6;
+ STOP_REASON_RESTRICTED_BUCKET = 6;
}
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index 6850d01..0455d58 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -225,9 +225,10 @@
message ScreenBrightnessSettingLimitsProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional int32 setting_minimum = 1;
- optional int32 setting_maximum = 2;
- optional int32 setting_default = 3;
+ reserved 1, 2, 3; // setting_minimum, setting_maximum, setting_default
+ optional float setting_minimum_float = 4;
+ optional float setting_maximum_float = 5;
+ optional float setting_default_float = 6;
}
// True to decouple auto-suspend mode from the display state.
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 08db454..e8a0b46 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -167,7 +167,7 @@
optional WindowContainerProto window_container = 1;
optional int32 id = 2;
reserved 3; // stacks
- optional DockedStackDividerControllerProto docked_stack_divider_controller = 4;
+ optional DockedStackDividerControllerProto docked_stack_divider_controller = 4 [deprecated=true];
// Will be removed soon.
optional PinnedStackControllerProto pinned_stack_controller = 5 [deprecated=true];
/* non app windows */
@@ -229,7 +229,7 @@
message DockedStackDividerControllerProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional bool minimized_dock = 1;
+ optional bool minimized_dock = 1 [deprecated=true];
}
/* represents PinnedStackController */
diff --git a/core/proto/android/service/appwidget.proto b/core/proto/android/service/appwidget.proto
index cd7173a..97350ef 100644
--- a/core/proto/android/service/appwidget.proto
+++ b/core/proto/android/service/appwidget.proto
@@ -36,4 +36,5 @@
optional int32 minHeight = 7;
optional int32 maxWidth = 8;
optional int32 maxHeight = 9;
+ optional bool restoreCompleted = 10;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 61d2298..6062102 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1817,6 +1817,9 @@
android:protectionLevel="normal" />
<!-- @SystemApi Allows an internal user to use privileged SecureElement APIs.
+ Applications holding this permission can access OMAPI reset system API
+ and bypass OMAPI AccessControlEnforcer.
+ <p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED"
android:protectionLevel="signature|privileged" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4ac51c6..8023990 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2752,7 +2752,9 @@
<!-- The amount to scale reduced scale snapshots for Overview and snapshot starting windows.
Reduced scale snapshots are loaded before full screen snapshots to improve load times and
- minimize the chance the user will see an empty task card. -->
+ minimize the chance the user will see an empty task card. If set to 0, reduced scale
+ snapshots are disabled, and snapshots will only be stored at config_highResTaskSnapshotScale
+ -->
<item name="config_lowResTaskSnapshotScale" format="float" type="dimen">0.5</item>
<!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. -->
@@ -3957,14 +3959,14 @@
<!-- Component name for the default module metadata provider on this device -->
<string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string>
- <!-- This is the default launcher component to use on secondary displays that support system
- decorations.
- This launcher activity must support multiple instances and have corresponding launch mode
- set in AndroidManifest.
+ <!-- This is the default launcher package with an activity to use on secondary displays that
+ support system decorations.
+ This launcher package must have an activity that supports multiple instances and has
+ corresponding launch mode set in AndroidManifest.
{@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} -->
- <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string>
+ <string name="config_secondaryHomePackage" translatable="false">com.android.launcher3</string>
- <!-- Force secondary home launcher specified in config_secondaryHomeComponent always. If this is
+ <!-- Force secondary home launcher specified in config_secondaryHomePackage always. If this is
not set, secondary home launcher can be replaced by user. -->
<bool name ="config_useSystemProvidedLauncherForSecondary">false</bool>
@@ -4404,6 +4406,7 @@
Determines whether the specified key groups can be used to wake up the device. -->
<bool name="config_wakeOnDpadKeyPress">true</bool>
<bool name="config_wakeOnAssistKeyPress">true</bool>
+ <bool name="config_wakeOnBackKeyPress">true</bool>
<!-- Whether to default to an expanded list of users on the lock screen user switcher. -->
<bool name="config_expandLockScreenUserSwitcher">false</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4f221d0..e1d94f50f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -685,7 +685,7 @@
<!-- The size of the right icon image when on low ram -->
<dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen>
- <dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen>
+ <dimen name="messaging_avatar_size">52dp</dimen>
<dimen name="messaging_group_sending_progress_size">24dp</dimen>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 64768cf..966f495 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -146,7 +146,7 @@
<item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item>
</style>
<style name="Widget.DeviceDefault.Notification.MessagingName" parent="Widget.Material.Notification.MessagingName">
- <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.Title</item>
+ <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.MessagingName</item>
</style>
<style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Material.PreferenceFrameLayout"/>
<style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/>
@@ -290,6 +290,9 @@
<style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title">
<item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
</style>
+ <style name="TextAppearance.DeviceDefault.Notification.MessagingName" parent="TextAppearance.DeviceDefault.Notification.Title">
+ <item name="textSize">16sp</item>
+ </style>
<style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply">
<item name="fontFamily">@string/config_bodyFontFamily</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d595459..3ed3a64e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1612,6 +1612,7 @@
<java-symbol type="style" name="TextAppearance.SlidingTabNormal" />
<java-symbol type="style" name="Theme.DeviceDefault.Dialog.NoFrame" />
<java-symbol type="style" name="Theme.IconMenu" />
+ <java-symbol type="style" name="Theme.Dream" />
<java-symbol type="style" name="Theme.DeviceDefault.VoiceInteractionSession" />
<java-symbol type="style" name="Pointer" />
<java-symbol type="style" name="LargePointer" />
@@ -3051,6 +3052,7 @@
<!-- Override Wake Key Behavior When Screen is Off -->
<java-symbol type="bool" name="config_wakeOnDpadKeyPress" />
<java-symbol type="bool" name="config_wakeOnAssistKeyPress" />
+ <java-symbol type="bool" name="config_wakeOnBackKeyPress" />
<!-- Pinner Service -->
<java-symbol type="array" name="config_defaultPinnerServiceFiles" />
@@ -3676,7 +3678,7 @@
<java-symbol type="string" name="config_defaultModuleMetadataProvider" />
<!-- For Secondary Launcher -->
- <java-symbol type="string" name="config_secondaryHomeComponent" />
+ <java-symbol type="string" name="config_secondaryHomePackage" />
<java-symbol type="bool" name="config_useSystemProvidedLauncherForSecondary" />
<java-symbol type="string" name="battery_saver_notification_channel_name" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 5d9cb48..2ef0c92 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -701,6 +701,11 @@
<item name="windowNoDisplay">true</item>
</style>
+ <style name="Theme.Dream">
+ <item name="windowBackground">@null</item>
+ <item name="windowDisablePreview">true</item>
+ </style>
+
<!-- Default theme for dialog windows and activities (on API level 10 and lower),
which is used by the
{@link android.app.Dialog} class. This changes the window to be
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 372b8c2..f4fbefe 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -37,6 +37,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
+import android.content.pm.ProviderInfoList;
import android.content.pm.ServiceInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -397,7 +398,7 @@
@Override
public void bindApplication(String s, ApplicationInfo applicationInfo,
- List<ProviderInfo> list, ComponentName componentName, ProfilerInfo profilerInfo,
+ ProviderInfoList list, ComponentName componentName, ProfilerInfo profilerInfo,
Bundle bundle, IInstrumentationWatcher iInstrumentationWatcher,
IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1,
boolean b2, boolean b3, Configuration configuration,
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index 7c2b98f..d02c6d5 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -230,6 +230,16 @@
}
@Test
+ public void inset_insets_withWaterfallCutout() throws Exception {
+ DisplayCutout cutout = createCutoutWaterfallOnly(Insets.of(0, 10, 0, 10)).inset(1, 2, 3, 4);
+
+ assertEquals(cutout.getSafeInsetLeft(), 0);
+ assertEquals(cutout.getSafeInsetTop(), 8);
+ assertEquals(cutout.getSafeInsetRight(), 0);
+ assertEquals(cutout.getSafeInsetBottom(), 6);
+ }
+
+ @Test
public void inset_insets_consumeInset() throws Exception {
DisplayCutout cutout = mCutoutTop.inset(0, 1000, 0, 0);
@@ -457,7 +467,8 @@
private static DisplayCutout createCutoutWaterfallOnly(Insets waterfallInsets) {
return new DisplayCutout(
- Insets.of(20, 0, 20, 0),
+ Insets.of(waterfallInsets.left, waterfallInsets.top, waterfallInsets.right,
+ waterfallInsets.bottom),
ZERO_RECT,
ZERO_RECT,
ZERO_RECT,
diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java
index 14999c7..623008e 100644
--- a/core/tests/coretests/src/android/view/KeyEventTest.java
+++ b/core/tests/coretests/src/android/view/KeyEventTest.java
@@ -19,6 +19,7 @@
import static android.view.Display.INVALID_DISPLAY;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import android.os.Parcel;
@@ -28,10 +29,14 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashSet;
+import java.util.Set;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KeyEventTest {
+ private static final int ID = 0xabcdef;
private static final int DOWN_TIME = 50;
private static final long EVENT_TIME = 100;
private static final int ACTION = KeyEvent.ACTION_DOWN;
@@ -45,6 +50,8 @@
private static final byte[] HMAC = null;
private static final String CHARACTERS = null;
+ private static final int ID_SOURCE_MASK = 0x3 << 30;
+
@Test
public void testObtain() {
KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
@@ -75,8 +82,7 @@
public void testObtainWithDisplayId() {
final int displayId = 5;
KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
- METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, displayId, null /* hmac*/,
- CHARACTERS);
+ METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, displayId, CHARACTERS);
assertEquals(DOWN_TIME, keyEvent.getDownTime());
assertEquals(EVENT_TIME, keyEvent.getEventTime());
assertEquals(ACTION, keyEvent.getAction());
@@ -91,6 +97,52 @@
assertEquals(CHARACTERS, keyEvent.getCharacters());
}
+ /**
+ * Tests that it can generate 500 consecutive distinct numbers. This is a non-deterministic test
+ * but with 30 bits randomness the failure rate is roughly 4.52e-5, which is negligible enough.
+ * Probability formula: N * (N - 1) * ... * (N - n + 1) / N^n, where N = 2^30 and n = 500 for
+ * this test.
+ */
+ @Test
+ public void testObtainGeneratesUniqueId() {
+ Set<Integer> set = new HashSet<>();
+ for (int i = 0; i < 500; ++i) {
+ KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+ METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS);
+ assertFalse("Found duplicate ID in round " + i,
+ set.contains(keyEvent.getId()));
+ set.add(keyEvent.getSequenceNumber());
+ }
+ }
+
+ @Test
+ public void testConstructorGeneratesUniqueId() {
+ Set<Integer> set = new HashSet<>();
+ for (int i = 0; i < 500; ++i) {
+ KeyEvent keyEvent = new KeyEvent(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT);
+ assertFalse("Found duplicate sequence number in round " + i,
+ set.contains(keyEvent.getId()));
+ set.add(keyEvent.getSequenceNumber());
+ }
+ }
+
+ @Test
+ public void testObtainGeneratesIdWithRightSource() {
+ for (int i = 0; i < 500; ++i) {
+ KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+ METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS);
+ assertEquals(0x3 << 30, ID_SOURCE_MASK & keyEvent.getId());
+ }
+ }
+
+ @Test
+ public void testConstructorGeneratesIdWithRightSource() {
+ for (int i = 0; i < 500; ++i) {
+ KeyEvent keyEvent = new KeyEvent(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT);
+ assertEquals(0x3 << 30, ID_SOURCE_MASK & keyEvent.getId());
+ }
+ }
+
@Test
public void testParcelUnparcel() {
KeyEvent key1 = createKey();
@@ -112,11 +164,12 @@
}
private static KeyEvent createKey() {
- return KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
- METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, HMAC, CHARACTERS);
+ return KeyEvent.obtain(ID, DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, METASTATE,
+ DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, HMAC, CHARACTERS);
}
private static void compareKeys(KeyEvent key1, KeyEvent key2) {
+ assertEquals(key1.getId(), key2.getId());
assertEquals(key1.getDownTime(), key2.getDownTime());
assertEquals(key1.getEventTime(), key2.getEventTime());
assertEquals(key1.getAction(), key2.getAction());
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 9d09830..786ae89 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -23,6 +23,7 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import android.view.MotionEvent.PointerCoords;
@@ -34,9 +35,13 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashSet;
+import java.util.Set;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MotionEventTest {
+ private static final int ID_SOURCE_MASK = 0x3 << 30;
@Test
public void testObtainWithDisplayId() {
@@ -138,4 +143,30 @@
assertEquals(30, event.getXCursorPosition(), 0.1);
assertEquals(50, event.getYCursorPosition(), 0.1);
}
+
+ /**
+ * Tests that it can generate 500 consecutive distinct numbers. This is a non-deterministic test
+ * but with 30 bits randomness the failure rate is roughly 4.52e-5, which is negligible enough.
+ * Probability formula: N * (N - 1) * ... * (N - n + 1) / N^n, where N = 2^30 and n = 500 for
+ * this test.
+ */
+ @Test
+ public void testObtainGeneratesUniqueId() {
+ Set<Integer> set = new HashSet<>();
+ for (int i = 0; i < 500; ++i) {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */);
+ assertFalse("Found duplicate ID in round " + i, set.contains(event.getId()));
+ set.add(event.getSequenceNumber());
+ }
+ }
+
+ @Test
+ public void testObtainGeneratesIdWithRightSource() {
+ for (int i = 0; i < 500; ++i) {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */);
+ assertEquals(0x3 << 30, ID_SOURCE_MASK & event.getId());
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index bbf3b12..9af0ed0 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -36,6 +36,7 @@
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -85,7 +86,9 @@
import java.lang.reflect.Field;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
@RunWith(AndroidJUnit4.class)
@@ -534,6 +537,36 @@
verify(mRingtone).play();
}
+ @Test
+ public void testOnAccessibilityShortcut_showsWarningDialog_ttsLongTimeInit_retrySpoken()
+ throws Exception {
+ configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
+ configureValidShortcutService();
+ configureTtsSpokenPromptEnabled();
+ configureHandlerCallbackInvocation();
+ AccessibilityShortcutController accessibilityShortcutController = getController();
+ Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+ Set<String> features = new HashSet<>();
+ features.add(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED);
+ doReturn(features, Collections.emptySet()).when(mVoice).getFeatures();
+ doReturn(TextToSpeech.LANG_NOT_SUPPORTED, TextToSpeech.LANG_AVAILABLE)
+ .when(mTextToSpeech).setLanguage(any());
+ accessibilityShortcutController.performAccessibilityShortcut();
+
+ verify(mAlertDialog).show();
+ ArgumentCaptor<TextToSpeech.OnInitListener> onInitCap = ArgumentCaptor.forClass(
+ TextToSpeech.OnInitListener.class);
+ verify(mFrameworkObjectProvider).getTextToSpeech(any(), onInitCap.capture());
+ onInitCap.getValue().onInit(TextToSpeech.SUCCESS);
+ verify(mTextToSpeech).speak(any(), eq(TextToSpeech.QUEUE_FLUSH), any(), any());
+ ArgumentCaptor<DialogInterface.OnDismissListener> onDismissCap = ArgumentCaptor.forClass(
+ DialogInterface.OnDismissListener.class);
+ verify(mAlertDialog).setOnDismissListener(onDismissCap.capture());
+ onDismissCap.getValue().onDismiss(mAlertDialog);
+ verify(mTextToSpeech).shutdown();
+ verify(mRingtone, times(0)).play();
+ }
+
private void configureNoShortcutService() throws Exception {
when(mAccessibilityManagerService
.getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY))
diff --git a/data/etc/com.android.documentsui.xml b/data/etc/com.android.documentsui.xml
index 4d98603..b6671db 100644
--- a/data/etc/com.android.documentsui.xml
+++ b/data/etc/com.android.documentsui.xml
@@ -18,5 +18,8 @@
<privapp-permissions package="com.android.documentsui">
<permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <!-- Permissions required for reading and logging compat changes -->
+ <permission name="android.permission.LOG_COMPAT_CHANGE"/>
+ <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 145fd8b..99605ad 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -697,6 +697,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "-668956537": {
+ "message": " THUMBNAIL %s: CREATE",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/SurfaceFreezer.java"
+ },
"-666510420": {
"message": "With display frozen, orientationChangeComplete=%b",
"level": "VERBOSE",
diff --git a/data/keyboards/Vendor_0079_Product_18d4.kl b/data/keyboards/Vendor_0079_Product_18d4.kl
new file mode 100644
index 0000000..b9a2b67
--- /dev/null
+++ b/data/keyboards/Vendor_0079_Product_18d4.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# GPD Win 2 X-Box Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_044f_Product_b326.kl b/data/keyboards/Vendor_044f_Product_b326.kl
new file mode 100644
index 0000000..d248d71
--- /dev/null
+++ b/data/keyboards/Vendor_044f_Product_b326.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Thrustmaster Gamepad GP XID
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_045e_Product_028f.kl b/data/keyboards/Vendor_045e_Product_028f.kl
new file mode 100644
index 0000000..cc5b33b
--- /dev/null
+++ b/data/keyboards/Vendor_045e_Product_028f.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Microsoft X-Box 360 pad v2
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_046d_Product_c21e.kl b/data/keyboards/Vendor_046d_Product_c21e.kl
new file mode 100644
index 0000000..9980743
--- /dev/null
+++ b/data/keyboards/Vendor_046d_Product_c21e.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Logitech Gamepad F510
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_046d_Product_c242.kl b/data/keyboards/Vendor_046d_Product_c242.kl
new file mode 100644
index 0000000..51eb44a
--- /dev/null
+++ b/data/keyboards/Vendor_046d_Product_c242.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Logitech Chillstream Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_056e_Product_2004.kl b/data/keyboards/Vendor_056e_Product_2004.kl
new file mode 100644
index 0000000..9eaa36d
--- /dev/null
+++ b/data/keyboards/Vendor_056e_Product_2004.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Elecom JC-U3613M
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_06a3_Product_f51a.kl b/data/keyboards/Vendor_06a3_Product_f51a.kl
new file mode 100644
index 0000000..e52f257
--- /dev/null
+++ b/data/keyboards/Vendor_06a3_Product_f51a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Saitek P3600
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_4716.kl b/data/keyboards/Vendor_0738_Product_4716.kl
new file mode 100644
index 0000000..5f3d4aa
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_4716.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Wired Xbox 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_4718.kl b/data/keyboards/Vendor_0738_Product_4718.kl
new file mode 100644
index 0000000..756e1e7
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_4718.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Street Fighter IV FightStick SE
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_4726.kl b/data/keyboards/Vendor_0738_Product_4726.kl
new file mode 100644
index 0000000..9d8deb3
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_4726.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Xbox 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_4736.kl b/data/keyboards/Vendor_0738_Product_4736.kl
new file mode 100644
index 0000000..c556e25
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_4736.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz MicroCon Gamepad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_4740.kl b/data/keyboards/Vendor_0738_Product_4740.kl
new file mode 100644
index 0000000..cdb7268
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_4740.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Beat Pad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_9871.kl b/data/keyboards/Vendor_0738_Product_9871.kl
new file mode 100644
index 0000000..f404065
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_9871.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Portable Drum
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_b726.kl b/data/keyboards/Vendor_0738_Product_b726.kl
new file mode 100644
index 0000000..05b737f
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_b726.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Xbox controller - MW2
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_beef.kl b/data/keyboards/Vendor_0738_Product_beef.kl
new file mode 100644
index 0000000..f969e73
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_beef.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz JOYTECH NEO SE Advanced GamePad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_cb02.kl b/data/keyboards/Vendor_0738_Product_cb02.kl
new file mode 100644
index 0000000..bc2fc35
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_cb02.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Saitek Cyborg Rumble Pad - PC/Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_cb03.kl b/data/keyboards/Vendor_0738_Product_cb03.kl
new file mode 100644
index 0000000..dcbf6b7
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_cb03.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Saitek P3200 Rumble Pad - PC/Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_cb29.kl b/data/keyboards/Vendor_0738_Product_cb29.kl
new file mode 100644
index 0000000..fe81d1c
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_cb29.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Saitek Aviator Stick AV8R02
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_f738.kl b/data/keyboards/Vendor_0738_Product_f738.kl
new file mode 100644
index 0000000..2c99380
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_f738.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Super SFIV FightStick TE S
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_07ff_Product_ffff.kl b/data/keyboards/Vendor_07ff_Product_ffff.kl
new file mode 100644
index 0000000..637c01b
--- /dev/null
+++ b/data/keyboards/Vendor_07ff_Product_ffff.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz GamePad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0113.kl b/data/keyboards/Vendor_0e6f_Product_0113.kl
new file mode 100644
index 0000000..90e1f75
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0113.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Afterglow AX.1 Gamepad for Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_011f.kl b/data/keyboards/Vendor_0e6f_Product_011f.kl
new file mode 100644
index 0000000..8c63c6b
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_011f.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Rock Candy Gamepad Wired Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0131.kl b/data/keyboards/Vendor_0e6f_Product_0131.kl
new file mode 100644
index 0000000..368c3760
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0131.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP EA Sports Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0133.kl b/data/keyboards/Vendor_0e6f_Product_0133.kl
new file mode 100644
index 0000000..815902e
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0133.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Xbox 360 Wired Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0139.kl b/data/keyboards/Vendor_0e6f_Product_0139.kl
new file mode 100644
index 0000000..8e2ae13
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0139.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Afterglow Prismatic Wired Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_013a.kl b/data/keyboards/Vendor_0e6f_Product_013a.kl
new file mode 100644
index 0000000..3f81983
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_013a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Xbox One Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0146.kl b/data/keyboards/Vendor_0e6f_Product_0146.kl
new file mode 100644
index 0000000..6ddd056
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0146.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Rock Candy Wired Controller for Xbox One
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0147.kl b/data/keyboards/Vendor_0e6f_Product_0147.kl
new file mode 100644
index 0000000..6745b7c
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0147.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Marvel Xbox One Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0161.kl b/data/keyboards/Vendor_0e6f_Product_0161.kl
new file mode 100644
index 0000000..3f81983
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0161.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Xbox One Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0162.kl b/data/keyboards/Vendor_0e6f_Product_0162.kl
new file mode 100644
index 0000000..3f81983
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0162.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Xbox One Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0163.kl b/data/keyboards/Vendor_0e6f_Product_0163.kl
new file mode 100644
index 0000000..3f81983
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0163.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Xbox One Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0164.kl b/data/keyboards/Vendor_0e6f_Product_0164.kl
new file mode 100644
index 0000000..0fdfd32
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0164.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Battlefield One
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0165.kl b/data/keyboards/Vendor_0e6f_Product_0165.kl
new file mode 100644
index 0000000..f9731e0
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0165.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Titanfall 2
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0201.kl b/data/keyboards/Vendor_0e6f_Product_0201.kl
new file mode 100644
index 0000000..5b4c167
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0201.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Pelican PL-3601 'TSZ' Wired Xbox 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0213.kl b/data/keyboards/Vendor_0e6f_Product_0213.kl
new file mode 100644
index 0000000..9317346
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0213.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Afterglow Gamepad for Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_021f.kl b/data/keyboards/Vendor_0e6f_Product_021f.kl
new file mode 100644
index 0000000..f8d3f0c
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_021f.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Rock Candy Gamepad for Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0246.kl b/data/keyboards/Vendor_0e6f_Product_0246.kl
new file mode 100644
index 0000000..daf8e45
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0246.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Rock Candy Gamepad for Xbox One 2015
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_02a6.kl b/data/keyboards/Vendor_0e6f_Product_02a6.kl
new file mode 100644
index 0000000..99a5931
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_02a6.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Wired Controller for Xbox One - Camo Series
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_02ab.kl b/data/keyboards/Vendor_0e6f_Product_02ab.kl
new file mode 100644
index 0000000..071a56c
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_02ab.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Controller for Xbox One
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0301.kl b/data/keyboards/Vendor_0e6f_Product_0301.kl
new file mode 100644
index 0000000..a3b982d
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0301.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Logic3 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0346.kl b/data/keyboards/Vendor_0e6f_Product_0346.kl
new file mode 100644
index 0000000..6fefbf7
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0346.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Rock Candy Gamepad for Xbox One 2016
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0401.kl b/data/keyboards/Vendor_0e6f_Product_0401.kl
new file mode 100644
index 0000000..a3b982d
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0401.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Logic3 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0413.kl b/data/keyboards/Vendor_0e6f_Product_0413.kl
new file mode 100644
index 0000000..90e1f75
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0413.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Afterglow AX.1 Gamepad for Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0501.kl b/data/keyboards/Vendor_0e6f_Product_0501.kl
new file mode 100644
index 0000000..35831d1
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0501.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Xbox 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_f900.kl b/data/keyboards/Vendor_0e6f_Product_f900.kl
new file mode 100644
index 0000000..44848ba
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_f900.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Afterglow AX.1
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0f0d_Product_000a.kl b/data/keyboards/Vendor_0f0d_Product_000a.kl
new file mode 100644
index 0000000..b3aea04
--- /dev/null
+++ b/data/keyboards/Vendor_0f0d_Product_000a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori Co. DOA4 FightStick
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0f0d_Product_000c.kl b/data/keyboards/Vendor_0f0d_Product_000c.kl
new file mode 100644
index 0000000..49c3add
--- /dev/null
+++ b/data/keyboards/Vendor_0f0d_Product_000c.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori PadEX Turbo
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0f0d_Product_0067.kl b/data/keyboards/Vendor_0f0d_Product_0067.kl
new file mode 100644
index 0000000..0dfcceb
--- /dev/null
+++ b/data/keyboards/Vendor_0f0d_Product_0067.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# HORIPAD ONE
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1038_Product_1430.kl b/data/keyboards/Vendor_1038_Product_1430.kl
new file mode 100644
index 0000000..e635c1d
--- /dev/null
+++ b/data/keyboards/Vendor_1038_Product_1430.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# SteelSeries Stratus Duo
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1038_Product_1431.kl b/data/keyboards/Vendor_1038_Product_1431.kl
new file mode 100644
index 0000000..e635c1d
--- /dev/null
+++ b/data/keyboards/Vendor_1038_Product_1431.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# SteelSeries Stratus Duo
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_11c9_Product_55f0.kl b/data/keyboards/Vendor_11c9_Product_55f0.kl
new file mode 100644
index 0000000..dbb4a7e
--- /dev/null
+++ b/data/keyboards/Vendor_11c9_Product_55f0.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Nacon GC-100XF
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_12ab_Product_0301.kl b/data/keyboards/Vendor_12ab_Product_0301.kl
new file mode 100644
index 0000000..36956c1
--- /dev/null
+++ b/data/keyboards/Vendor_12ab_Product_0301.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP AFTERGLOW AX.1
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1430_Product_4748.kl b/data/keyboards/Vendor_1430_Product_4748.kl
new file mode 100644
index 0000000..dbe8308
--- /dev/null
+++ b/data/keyboards/Vendor_1430_Product_4748.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# RedOctane Guitar Hero X-plorer
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1430_Product_f801.kl b/data/keyboards/Vendor_1430_Product_f801.kl
new file mode 100644
index 0000000..a8f9146
--- /dev/null
+++ b/data/keyboards/Vendor_1430_Product_f801.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# RedOctane Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_146b_Product_0601.kl b/data/keyboards/Vendor_146b_Product_0601.kl
new file mode 100644
index 0000000..ea2f221
--- /dev/null
+++ b/data/keyboards/Vendor_146b_Product_0601.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# BigBen Interactive XBOX 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1532_Product_0037.kl b/data/keyboards/Vendor_1532_Product_0037.kl
new file mode 100644
index 0000000..39d8b2e
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_0037.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Razer Sabertooth
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1532_Product_0a03.kl b/data/keyboards/Vendor_1532_Product_0a03.kl
new file mode 100644
index 0000000..75775e9
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_0a03.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Razer Wildcat
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_15e4_Product_3f00.kl b/data/keyboards/Vendor_15e4_Product_3f00.kl
new file mode 100644
index 0000000..0d641cf
--- /dev/null
+++ b/data/keyboards/Vendor_15e4_Product_3f00.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Power A Mini Pro Elite
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_15e4_Product_3f0a.kl b/data/keyboards/Vendor_15e4_Product_3f0a.kl
new file mode 100644
index 0000000..9e98aee
--- /dev/null
+++ b/data/keyboards/Vendor_15e4_Product_3f0a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Xbox Airflo wired controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_15e4_Product_3f10.kl b/data/keyboards/Vendor_15e4_Product_3f10.kl
new file mode 100644
index 0000000..7fb0fea
--- /dev/null
+++ b/data/keyboards/Vendor_15e4_Product_3f10.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Batarang Xbox 360 controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_162e_Product_beef.kl b/data/keyboards/Vendor_162e_Product_beef.kl
new file mode 100644
index 0000000..e7fab5d
--- /dev/null
+++ b/data/keyboards/Vendor_162e_Product_beef.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Joytech Neo-Se Take2
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_0002.kl b/data/keyboards/Vendor_1bad_Product_0002.kl
new file mode 100644
index 0000000..d8eaaba
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_0002.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Harmonix Rock Band Guitar
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f021.kl b/data/keyboards/Vendor_1bad_Product_f021.kl
new file mode 100644
index 0000000..9fd688b
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f021.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Cats Ghost Recon FS GamePad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f025.kl b/data/keyboards/Vendor_1bad_Product_f025.kl
new file mode 100644
index 0000000..03aab44
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f025.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Call Of Duty
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f028.kl b/data/keyboards/Vendor_1bad_Product_f028.kl
new file mode 100644
index 0000000..5173331
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f028.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Street Fighter IV FightPad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f038.kl b/data/keyboards/Vendor_1bad_Product_f038.kl
new file mode 100644
index 0000000..79e147d
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f038.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Street Fighter IV FightStick TE
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f501.kl b/data/keyboards/Vendor_1bad_Product_f501.kl
new file mode 100644
index 0000000..1282532
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f501.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# HoriPad EX2 Turbo
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f506.kl b/data/keyboards/Vendor_1bad_Product_f506.kl
new file mode 100644
index 0000000..3a9d462
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f506.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori Real Arcade Pro.EX Premium VLX
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f900.kl b/data/keyboards/Vendor_1bad_Product_f900.kl
new file mode 100644
index 0000000..9cfceb4
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f900.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Harmonix Xbox 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f901.kl b/data/keyboards/Vendor_1bad_Product_f901.kl
new file mode 100644
index 0000000..86d45e5
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f901.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Gamestop Xbox 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f903.kl b/data/keyboards/Vendor_1bad_Product_f903.kl
new file mode 100644
index 0000000..f61c050
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f903.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Tron Xbox 360 controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f904.kl b/data/keyboards/Vendor_1bad_Product_f904.kl
new file mode 100644
index 0000000..3e02a24
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f904.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Versus Fighting Pad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_fa01.kl b/data/keyboards/Vendor_1bad_Product_fa01.kl
new file mode 100644
index 0000000..517413d
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_fa01.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# MadCatz GamePad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_fd00.kl b/data/keyboards/Vendor_1bad_Product_fd00.kl
new file mode 100644
index 0000000..fc6a4f8
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_fd00.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Razer Onza TE
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_fd01.kl b/data/keyboards/Vendor_1bad_Product_fd01.kl
new file mode 100644
index 0000000..8882abf
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_fd01.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Razer Onza
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5300.kl b/data/keyboards/Vendor_24c6_Product_5300.kl
new file mode 100644
index 0000000..303e906
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5300.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PowerA MINI PROEX Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5303.kl b/data/keyboards/Vendor_24c6_Product_5303.kl
new file mode 100644
index 0000000..9e98aee
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5303.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Xbox Airflo wired controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_530a.kl b/data/keyboards/Vendor_24c6_Product_530a.kl
new file mode 100644
index 0000000..aa88515
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_530a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Xbox 360 Pro EX Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_531a.kl b/data/keyboards/Vendor_24c6_Product_531a.kl
new file mode 100644
index 0000000..09a5c6a
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_531a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PowerA Pro Ex
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5397.kl b/data/keyboards/Vendor_24c6_Product_5397.kl
new file mode 100644
index 0000000..66b896a3
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5397.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# FUS1ON Tournament Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_541a.kl b/data/keyboards/Vendor_24c6_Product_541a.kl
new file mode 100644
index 0000000..24271fb
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_541a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PowerA Xbox One Mini Wired Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_542a.kl b/data/keyboards/Vendor_24c6_Product_542a.kl
new file mode 100644
index 0000000..623bd13
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_542a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Xbox ONE spectra
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_543a.kl b/data/keyboards/Vendor_24c6_Product_543a.kl
new file mode 100644
index 0000000..59769c4
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_543a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PowerA Xbox One wired controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5500.kl b/data/keyboards/Vendor_24c6_Product_5500.kl
new file mode 100644
index 0000000..d76d7d0
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5500.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori XBOX 360 EX 2 with Turbo
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5501.kl b/data/keyboards/Vendor_24c6_Product_5501.kl
new file mode 100644
index 0000000..64d901a
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5501.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori Real Arcade Pro VX-SA
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5506.kl b/data/keyboards/Vendor_24c6_Product_5506.kl
new file mode 100644
index 0000000..bfb23c3
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5506.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori SOULCALIBUR V Stick
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_550d.kl b/data/keyboards/Vendor_24c6_Product_550d.kl
new file mode 100644
index 0000000..24852b0
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_550d.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori GEM Xbox controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_551a.kl b/data/keyboards/Vendor_24c6_Product_551a.kl
new file mode 100644
index 0000000..5e338a5
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_551a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PowerA FUSION Pro Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_561a.kl b/data/keyboards/Vendor_24c6_Product_561a.kl
new file mode 100644
index 0000000..57b7ddc
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_561a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PowerA FUSION Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5b02.kl b/data/keyboards/Vendor_24c6_Product_5b02.kl
new file mode 100644
index 0000000..bcf354d
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5b02.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Thrustmaster, Inc. GPX Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5d04.kl b/data/keyboards/Vendor_24c6_Product_5d04.kl
new file mode 100644
index 0000000..39d8b2e
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5d04.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Razer Sabertooth
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_fafe.kl b/data/keyboards/Vendor_24c6_Product_fafe.kl
new file mode 100644
index 0000000..f8d3f0c
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_fafe.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Rock Candy Gamepad for Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 3f2f349..f87f98a 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -191,4 +191,3 @@
shared_libs: common_test_libs,
data: ["tests/data/**/*.apk"],
}
-
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 35cebd4..2bfc7fc 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -22,6 +22,7 @@
#include <androidfw/Asset.h>
#include <androidfw/LocaleData.h>
+#include <androidfw/StringPiece.h>
#include <utils/Errors.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@@ -34,6 +35,7 @@
#include <android/configuration.h>
+#include <array>
#include <memory>
namespace android {
@@ -1676,42 +1678,64 @@
*/
struct ResTable_overlayable_policy_header
{
- struct ResChunk_header header;
-
+ /**
+ * Flags for a bitmask for all possible overlayable policy options.
+ *
+ * Any changes to this set should also update aidl/android/os/OverlayablePolicy.aidl
+ */
enum PolicyFlags : uint32_t {
+ // Base
+ NONE = 0x00000000,
+
// Any overlay can overlay these resources.
- POLICY_PUBLIC = 0x00000001,
+ PUBLIC = 0x00000001,
// The overlay must reside of the system partition or must have existed on the system partition
// before an upgrade to overlay these resources.
- POLICY_SYSTEM_PARTITION = 0x00000002,
+ SYSTEM_PARTITION = 0x00000002,
// The overlay must reside of the vendor partition or must have existed on the vendor partition
// before an upgrade to overlay these resources.
- POLICY_VENDOR_PARTITION = 0x00000004,
+ VENDOR_PARTITION = 0x00000004,
// The overlay must reside of the product partition or must have existed on the product
// partition before an upgrade to overlay these resources.
- POLICY_PRODUCT_PARTITION = 0x00000008,
+ PRODUCT_PARTITION = 0x00000008,
- // The overlay must be signed with the same signature as the actor of the target resource,
- // which can be separate or the same as the target package with the resource.
- POLICY_SIGNATURE = 0x00000010,
+ // The overlay must be signed with the same signature as the package containing the target
+ // resource
+ SIGNATURE = 0x00000010,
// The overlay must reside of the odm partition or must have existed on the odm
// partition before an upgrade to overlay these resources.
- POLICY_ODM_PARTITION = 0x00000020,
+ ODM_PARTITION = 0x00000020,
// The overlay must reside of the oem partition or must have existed on the oem
// partition before an upgrade to overlay these resources.
- POLICY_OEM_PARTITION = 0x00000040,
+ OEM_PARTITION = 0x00000040,
+
+ // The overlay must be signed with the same signature as the actor declared for the target
+ // resource
+ ACTOR_SIGNATURE = 0x00000080,
};
- uint32_t policy_flags;
+
+ using PolicyBitmask = uint32_t;
+
+ struct ResChunk_header header;
+
+ PolicyFlags policy_flags;
// The number of ResTable_ref that follow this header.
uint32_t entry_count;
};
+inline ResTable_overlayable_policy_header::PolicyFlags& operator |=(
+ ResTable_overlayable_policy_header::PolicyFlags& first,
+ ResTable_overlayable_policy_header::PolicyFlags second) {
+ first = static_cast<ResTable_overlayable_policy_header::PolicyFlags>(first | second);
+ return first;
+}
+
#pragma pack(push, 1)
struct Idmap_header {
// Always 0x504D4449 ('IDMP')
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 8615069..2d69dfe 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -41,6 +41,8 @@
using ::testing::SizeIs;
using ::testing::StrEq;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android {
TEST(LoadedArscTest, LoadSinglePackageArsc) {
@@ -240,29 +242,29 @@
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->name, Eq("OverlayableResources1"));
EXPECT_THAT(info->actor, Eq("overlay://theme"));
- EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+ EXPECT_THAT(info->policy_flags, Eq(PolicyFlags::PUBLIC));
info = package->GetOverlayableInfo(overlayable::R::string::overlayable2);
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->name, Eq("OverlayableResources1"));
EXPECT_THAT(info->actor, Eq("overlay://theme"));
EXPECT_THAT(info->policy_flags,
- Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION
- | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+ Eq(PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION));
info = package->GetOverlayableInfo(overlayable::R::string::overlayable3);
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->name, Eq("OverlayableResources2"));
EXPECT_THAT(info->actor, Eq("overlay://com.android.overlayable"));
EXPECT_THAT(info->policy_flags,
- Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION
- | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+ Eq(PolicyFlags::VENDOR_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION));
info = package->GetOverlayableInfo(overlayable::R::string::overlayable4);
EXPECT_THAT(info->name, Eq("OverlayableResources1"));
EXPECT_THAT(info->actor, Eq("overlay://theme"));
ASSERT_THAT(info, NotNull());
- EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+ EXPECT_THAT(info->policy_flags, Eq(PolicyFlags::PUBLIC));
}
TEST(LoadedArscTest, ResourceIdentifierIterator) {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index f2b4db1..f800f9e 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2531,7 +2531,7 @@
int offset, int size, long presentationTimeUs, int flags)
throws CryptoException {
synchronized(mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
invalidateByteBuffer(mCachedInputBuffers, index);
@@ -2783,7 +2783,7 @@
long presentationTimeUs,
int flags) throws CryptoException {
synchronized(mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
invalidateByteBuffer(mCachedInputBuffers, index);
@@ -2818,7 +2818,7 @@
*/
public final int dequeueInputBuffer(long timeoutUs) {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
}
@@ -3534,7 +3534,7 @@
public final int dequeueOutputBuffer(
@NonNull BufferInfo info, long timeoutUs) {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
}
@@ -3916,7 +3916,7 @@
@NonNull
public ByteBuffer[] getInputBuffers() {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
if (mCachedInputBuffers == null) {
@@ -3952,7 +3952,7 @@
@NonNull
public ByteBuffer[] getOutputBuffers() {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
if (mCachedOutputBuffers == null) {
@@ -3984,7 +3984,7 @@
@Nullable
public ByteBuffer getInputBuffer(int index) {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
}
@@ -4018,7 +4018,7 @@
@Nullable
public Image getInputImage(int index) {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
}
@@ -4052,7 +4052,7 @@
@Nullable
public ByteBuffer getOutputBuffer(int index) {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
}
@@ -4085,7 +4085,7 @@
@Nullable
public Image getOutputImage(int index) {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index b579144..95199cc 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -240,7 +240,7 @@
* @param inputId The ID of the TV input associated with the session.
*/
@Nullable
- public abstract Session onCreateSession(String inputId);
+ public abstract Session onCreateSession(@NonNull String inputId);
/**
* Returns a concrete implementation of {@link RecordingSession}.
@@ -251,7 +251,7 @@
* @param inputId The ID of the TV input associated with the recording session.
*/
@Nullable
- public RecordingSession onCreateRecordingSession(String inputId) {
+ public RecordingSession onCreateRecordingSession(@NonNull String inputId) {
return null;
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 73769be..7a684b3 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -18,11 +18,13 @@
import android.annotation.BytesLong;
import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
+import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.TvInputService;
import android.media.tv.tuner.TunerConstants.Result;
import android.media.tv.tuner.dvr.DvrPlayback;
@@ -45,6 +47,8 @@
import android.os.Looper;
import android.os.Message;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -67,6 +71,22 @@
private static final int MSG_ON_FILTER_STATUS = 3;
private static final int MSG_ON_LNB_EVENT = 4;
+ /** @hide */
+ @IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DvrType {}
+
+ /**
+ * DVR for recording.
+ * @hide
+ */
+ public static final int DVR_TYPE_RECORD = Constants.DvrType.RECORD;
+ /**
+ * DVR for playback of recorded programs.
+ * @hide
+ */
+ public static final int DVR_TYPE_PLAYBACK = Constants.DvrType.PLAYBACK;
+
static {
System.loadLibrary("media_tv_tuner");
nativeInit();
diff --git a/media/java/android/media/tv/tuner/dvr/Dvr.java b/media/java/android/media/tv/tuner/dvr/Dvr.java
deleted file mode 100644
index 4183e3b..0000000
--- a/media/java/android/media/tv/tuner/dvr/Dvr.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 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 android.media.tv.tuner.dvr;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.Result;
-import android.media.tv.tuner.filter.Filter;
-import android.os.ParcelFileDescriptor;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Digital Video Record (DVR) interface provides record control on Demux's output buffer and
- * playback control on Demux's input buffer.
- *
- * @hide
- */
-@SystemApi
-public class Dvr implements AutoCloseable {
-
- /** @hide */
- @IntDef(prefix = "TYPE_", value = {TYPE_RECORD, TYPE_PLAYBACK})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Type {}
-
- /**
- * DVR for recording.
- */
- public static final int TYPE_RECORD = Constants.DvrType.RECORD;
- /**
- * DVR for playback of recorded programs.
- */
- public static final int TYPE_PLAYBACK = Constants.DvrType.PLAYBACK;
-
-
- final int mType;
- long mNativeContext;
-
- private native int nativeAttachFilter(Filter filter);
- private native int nativeDetachFilter(Filter filter);
- private native int nativeConfigureDvr(DvrSettings settings);
- private native int nativeStartDvr();
- private native int nativeStopDvr();
- private native int nativeFlushDvr();
- private native int nativeClose();
- private native void nativeSetFileDescriptor(int fd);
-
- protected Dvr(int type) {
- mType = type;
- }
-
- /**
- * Attaches a filter to DVR interface for recording.
- *
- * @param filter the filter to be attached.
- * @return result status of the operation.
- */
- @Result
- public int attachFilter(@NonNull Filter filter) {
- return nativeAttachFilter(filter);
- }
-
- /**
- * Detaches a filter from DVR interface.
- *
- * @param filter the filter to be detached.
- * @return result status of the operation.
- */
- @Result
- public int detachFilter(@NonNull Filter filter) {
- return nativeDetachFilter(filter);
- }
-
- /**
- * Configures the DVR.
- *
- * @param settings the settings of the DVR interface.
- * @return result status of the operation.
- */
- @Result
- public int configure(@NonNull DvrSettings settings) {
- return nativeConfigureDvr(settings);
- }
-
- /**
- * Starts DVR.
- *
- * <p>Starts consuming playback data or producing data for recording.
- *
- * @return result status of the operation.
- */
- @Result
- public int start() {
- return nativeStartDvr();
- }
-
- /**
- * Stops DVR.
- *
- * <p>Stops consuming playback data or producing data for recording.
- *
- * @return result status of the operation.
- */
- @Result
- public int stop() {
- return nativeStopDvr();
- }
-
- /**
- * Flushed DVR data.
- *
- * <p>The data in DVR buffer is cleared.
- *
- * @return result status of the operation.
- */
- @Result
- public int flush() {
- return nativeFlushDvr();
- }
-
- /**
- * Closes the DVR instance to release resources.
- */
- public void close() {
- nativeClose();
- }
-
- /**
- * Sets file descriptor to read/write data.
- *
- * @param fd the file descriptor to read/write data.
- */
- public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
- nativeSetFileDescriptor(fd.getFd());
- }
-
- @Type
- int getType() {
- return mType;
- }
-}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index eb31574..7c15bb7 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -21,6 +21,9 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.filter.Filter;
+import android.os.ParcelFileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -33,7 +36,7 @@
* @hide
*/
@SystemApi
-public class DvrPlayback extends Dvr {
+public class DvrPlayback implements AutoCloseable {
/** @hide */
@@ -66,17 +69,110 @@
*/
public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL;
+ long mNativeContext;
-
+ private native int nativeAttachFilter(Filter filter);
+ private native int nativeDetachFilter(Filter filter);
+ private native int nativeConfigureDvr(DvrSettings settings);
+ private native int nativeStartDvr();
+ private native int nativeStopDvr();
+ private native int nativeFlushDvr();
+ private native int nativeClose();
+ private native void nativeSetFileDescriptor(int fd);
private native long nativeRead(long size);
private native long nativeRead(byte[] bytes, long offset, long size);
private DvrPlayback() {
- super(Dvr.TYPE_PLAYBACK);
}
/**
+ * Attaches a filter to DVR interface for recording.
+ *
+ * @param filter the filter to be attached.
+ * @return result status of the operation.
+ */
+ @Result
+ public int attachFilter(@NonNull Filter filter) {
+ return nativeAttachFilter(filter);
+ }
+
+ /**
+ * Detaches a filter from DVR interface.
+ *
+ * @param filter the filter to be detached.
+ * @return result status of the operation.
+ */
+ @Result
+ public int detachFilter(@NonNull Filter filter) {
+ return nativeDetachFilter(filter);
+ }
+
+ /**
+ * Configures the DVR.
+ *
+ * @param settings the settings of the DVR interface.
+ * @return result status of the operation.
+ */
+ @Result
+ public int configure(@NonNull DvrSettings settings) {
+ return nativeConfigureDvr(settings);
+ }
+
+ /**
+ * Starts DVR.
+ *
+ * <p>Starts consuming playback data or producing data for recording.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int start() {
+ return nativeStartDvr();
+ }
+
+ /**
+ * Stops DVR.
+ *
+ * <p>Stops consuming playback data or producing data for recording.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int stop() {
+ return nativeStopDvr();
+ }
+
+ /**
+ * Flushed DVR data.
+ *
+ * <p>The data in DVR buffer is cleared.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int flush() {
+ return nativeFlushDvr();
+ }
+
+ /**
+ * Closes the DVR instance to release resources.
+ */
+ @Override
+ public void close() {
+ nativeClose();
+ }
+
+ /**
+ * Sets file descriptor to read/write data.
+ *
+ * @param fd the file descriptor to read/write data.
+ */
+ public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
+ nativeSetFileDescriptor(fd.getFd());
+ }
+
+ /**
* Reads data from the file for DVR playback.
*
* @param size the maximum number of bytes to read.
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index 3128ca5..52ef5e6 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -19,6 +19,9 @@
import android.annotation.BytesLong;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.filter.Filter;
+import android.os.ParcelFileDescriptor;
/**
* Digital Video Record (DVR) recorder class which provides record control on Demux's output buffer.
@@ -26,12 +29,108 @@
* @hide
*/
@SystemApi
-public class DvrRecorder extends Dvr {
+public class DvrRecorder implements AutoCloseable {
+ long mNativeContext;
+
+ private native int nativeAttachFilter(Filter filter);
+ private native int nativeDetachFilter(Filter filter);
+ private native int nativeConfigureDvr(DvrSettings settings);
+ private native int nativeStartDvr();
+ private native int nativeStopDvr();
+ private native int nativeFlushDvr();
+ private native int nativeClose();
+ private native void nativeSetFileDescriptor(int fd);
private native long nativeWrite(long size);
private native long nativeWrite(byte[] bytes, long offset, long size);
private DvrRecorder() {
- super(Dvr.TYPE_RECORD);
+ }
+
+
+ /**
+ * Attaches a filter to DVR interface for recording.
+ *
+ * @param filter the filter to be attached.
+ * @return result status of the operation.
+ */
+ @Result
+ public int attachFilter(@NonNull Filter filter) {
+ return nativeAttachFilter(filter);
+ }
+
+ /**
+ * Detaches a filter from DVR interface.
+ *
+ * @param filter the filter to be detached.
+ * @return result status of the operation.
+ */
+ @Result
+ public int detachFilter(@NonNull Filter filter) {
+ return nativeDetachFilter(filter);
+ }
+
+ /**
+ * Configures the DVR.
+ *
+ * @param settings the settings of the DVR interface.
+ * @return result status of the operation.
+ */
+ @Result
+ public int configure(@NonNull DvrSettings settings) {
+ return nativeConfigureDvr(settings);
+ }
+
+ /**
+ * Starts DVR.
+ *
+ * <p>Starts consuming playback data or producing data for recording.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int start() {
+ return nativeStartDvr();
+ }
+
+ /**
+ * Stops DVR.
+ *
+ * <p>Stops consuming playback data or producing data for recording.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int stop() {
+ return nativeStopDvr();
+ }
+
+ /**
+ * Flushed DVR data.
+ *
+ * <p>The data in DVR buffer is cleared.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int flush() {
+ return nativeFlushDvr();
+ }
+
+ /**
+ * Closes the DVR instance to release resources.
+ */
+ @Override
+ public void close() {
+ nativeClose();
+ }
+
+ /**
+ * Sets file descriptor to read/write data.
+ *
+ * @param fd the file descriptor to read/write data.
+ */
+ public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
+ nativeSetFileDescriptor(fd.getFd());
}
/**
diff --git a/media/java/android/media/tv/tuner/dvr/DvrSettings.java b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
index f9dc682..362b108 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrSettings.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
@@ -30,7 +30,7 @@
import java.lang.annotation.RetentionPolicy;
/**
- * DVR settings used to configure {@link Dvr}.
+ * DVR settings used to configure {@link DvrPlayback} and {@link DvrRecorder}.
*
* @hide
*/
diff --git a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
index 6837244..598ff8f 100644
--- a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
+++ b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
@@ -78,7 +78,8 @@
* {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE}
* {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD}.
* New [use case : priority value] pair can be defined in the manifest by the
- * OEM. Any undefined use case would cause IllegalArgumentException.
+ * OEM. The id of the useCaseVendor should be passed through this parameter. Any
+ * undefined use case would cause IllegalArgumentException.
*/
public ResourceClientProfile(@NonNull String tvInputSessionId,
int useCase) {
diff --git a/media/java/android/media/voice/KeyphraseModelManager.java b/media/java/android/media/voice/KeyphraseModelManager.java
index 3fa38e0..8ec8967 100644
--- a/media/java/android/media/voice/KeyphraseModelManager.java
+++ b/media/java/android/media/voice/KeyphraseModelManager.java
@@ -37,7 +37,8 @@
* manage voice based sound trigger models.
* Callers of this class are expected to have whitelist manifest permission MANAGE_VOICE_KEYPHRASES.
* Callers of this class are expected to be the designated voice interaction service via
- * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE} or a bundled voice model enrollment application
+ * detected by {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
* @hide
*/
@SystemApi
@@ -65,6 +66,10 @@
* {@link #updateKeyphraseSoundModel}.
* If the active voice interaction service changes from the current user, all requests will be
* rejected, and any registered models will be unregistered.
+ * Caller must either be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
+ * enrollment application detected by
+ * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
*
* @param keyphraseId The unique identifier for the keyphrase.
* @param locale The locale language tag supported by the desired model.
@@ -93,6 +98,10 @@
* will be overwritten with the new model.
* If the active voice interaction service changes from the current user, all requests will be
* rejected, and any registered models will be unregistered.
+ * Caller must either be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
+ * enrollment application detected by
+ * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
*
* @param model Keyphrase sound model to be updated.
* @throws ServiceSpecificException Thrown with error code if failed to update the keyphrase
@@ -120,6 +129,10 @@
* {@link #updateKeyphraseSoundModel}.
* If the active voice interaction service changes from the current user, all requests will be
* rejected, and any registered models will be unregistered.
+ * Caller must either be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
+ * enrollment application detected by
+ * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
*
* @param keyphraseId The unique identifier for the keyphrase.
* @param locale The locale language tag supported by the desired model.
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index a4eada4..f39f912 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -122,6 +122,7 @@
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -324,6 +325,7 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ PhoneStatusBarPolicy phoneStatusBarPolicy,
DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
/* Car Settings injected components. */
@@ -407,6 +409,7 @@
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
+ phoneStatusBarPolicy,
dismissCallbackRegistry,
statusBarTouchableRegionManager);
mUserSwitcherController = userSwitcherController;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 7294965..843e7c5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -82,6 +82,7 @@
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -197,6 +198,7 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ PhoneStatusBarPolicy phoneStatusBarPolicy,
DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
CarServiceProvider carServiceProvider,
@@ -278,6 +280,7 @@
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
+ phoneStatusBarPolicy,
dismissCallbackRegistry,
statusBarTouchableRegionManager,
carServiceProvider,
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 37a77be..f36f97d 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -82,6 +82,9 @@
static final String DEFAULT_DSU_SLOT = "dsu";
static final String KEY_PUBKEY = "KEY_PUBKEY";
+ // Default userdata partition size is 2GiB.
+ private static final long DEFAULT_USERDATA_SIZE = 2L << 30;
+
/*
* Intent actions
*/
@@ -270,6 +273,10 @@
String dsuSlot = intent.getStringExtra(KEY_DSU_SLOT);
String publicKey = intent.getStringExtra(KEY_PUBKEY);
+ if (userdataSize == 0) {
+ userdataSize = DEFAULT_USERDATA_SIZE;
+ }
+
if (TextUtils.isEmpty(dsuSlot)) {
dsuSlot = DEFAULT_DSU_SLOT;
}
diff --git a/packages/SettingsLib/SchedulesProvider/res/values/config.xml b/packages/SettingsLib/SchedulesProvider/res/values/config.xml
new file mode 100644
index 0000000..48f3e3e
--- /dev/null
+++ b/packages/SettingsLib/SchedulesProvider/res/values/config.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+ <!-- Package name for the caller of the Schedules provider. -->
+ <string name="config_schedules_provider_caller_package" translatable="false">com.android.settings</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java
index 7d2b8e2..26bcd54 100644
--- a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java
+++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java
@@ -24,9 +24,9 @@
import androidx.annotation.NonNull;
/**
- * This is a schedule data item. It contains the schedule title text, the summary text which
- * displays on the summary of the Settings preference and an {@link Intent}. Intent is able to
- * launch the editing page of the schedule data when user clicks this item (preference).
+ * Schedule data item containing the schedule title text, the summary text which is displayed on the
+ * summary of the Settings preference and an {@link Intent} which Settings will launch when the
+ * user clicks on the preference.
*/
public class ScheduleInfo implements Parcelable {
private static final String TAG = "ScheduleInfo";
@@ -40,7 +40,7 @@
mIntent = builder.mIntent;
}
- protected ScheduleInfo(Parcel in) {
+ private ScheduleInfo(Parcel in) {
mTitle = in.readString();
mSummary = in.readString();
mIntent = in.readParcelable(Intent.class.getClassLoader());
@@ -48,8 +48,6 @@
/**
* Returns the title text.
- *
- * @return The title.
*/
public String getTitle() {
return mTitle;
@@ -57,15 +55,14 @@
/**
* Returns the summary text.
- *
- * @return The summary.
*/
public String getSummary() {
return mSummary;
}
/**
- * Returns an {@link Intent}.
+ * Returns an {@link Intent} which Settings will launch when the user clicks on a schedule
+ * preference.
*/
public Intent getIntent() {
return mIntent;
@@ -107,19 +104,15 @@
@NonNull
@Override
public String toString() {
- return "title : " + mTitle + " summary : " + mSummary + (mIntent == null
- ? " and intent is null." : ".");
+ return "title: " + mTitle + ", summary: " + mSummary + ", intent: " + mIntent;
}
/**
* A simple builder for {@link ScheduleInfo}.
*/
public static class Builder {
- @NonNull
private String mTitle;
- @NonNull
private String mSummary;
- @NonNull
private Intent mIntent;
/**
diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java
index a423e47..28d5f07 100644
--- a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java
+++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java
@@ -21,19 +21,18 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
-import java.util.List;
import java.util.stream.Collectors;
/**
- * This provider is a bridge for client apps to provide the schedule data.
- * Client provider needs to implement their {@link #getScheduleInfoList()} and returns a list of
- * {@link ScheduleInfo}.
+ * A bridge for client apps to provide the schedule data. Client provider needs to implement
+ * {@link #getScheduleInfoList()} returning a list of {@link ScheduleInfo}.
*/
public abstract class SchedulesProvider extends ContentProvider {
public static final String METHOD_GENERATE_SCHEDULE_INFO_LIST = "generateScheduleInfoList";
@@ -46,9 +45,8 @@
}
@Override
- public final Cursor query(
- Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
+ public final Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
throw new UnsupportedOperationException("Query operation is not supported currently.");
}
@@ -74,18 +72,24 @@
}
/**
- * Return the list of the schedule information.
- *
- * @return a list of the {@link ScheduleInfo}.
+ * Returns the list of the schedule information.
*/
public abstract ArrayList<ScheduleInfo> getScheduleInfoList();
/**
- * Returns a bundle which contains a list of {@link ScheduleInfo} and data types:
- * scheduleInfoList : ArrayList<ScheduleInfo>
+ * Returns a bundle which contains a list of {@link ScheduleInfo}s:
+ *
+ * <ul>
+ * <li>scheduleInfoList: ArrayList<ScheduleInfo>
+ * </ul>
*/
@Override
public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
+ if (!TextUtils.equals(getCallingPackage(),
+ getContext().getText(R.string.config_schedules_provider_caller_package))) {
+ return null;
+ }
+
final Bundle bundle = new Bundle();
if (METHOD_GENERATE_SCHEDULE_INFO_LIST.equals(method)) {
final ArrayList<ScheduleInfo> scheduleInfoList = filterInvalidData(
@@ -98,36 +102,40 @@
}
/**
- * To filter the invalid schedule info.
+ * Filters our invalid schedule infos from {@code schedulesInfoList}.
*
- * @param scheduleInfoList The list of the {@link ScheduleInfo}.
- * @return The valid list of the {@link ScheduleInfo}.
+ * @return valid {@link SchedulesInfo}s if {@code schedulesInfoList} is not null. Otherwise,
+ * null.
*/
- private ArrayList<ScheduleInfo> filterInvalidData(ArrayList<ScheduleInfo> scheduleInfoList) {
+ @Nullable
+ private ArrayList<ScheduleInfo> filterInvalidData(
+ @Nullable ArrayList<ScheduleInfo> scheduleInfoList) {
if (scheduleInfoList == null) {
Log.d(TAG, "package : " + getContext().getPackageName() + " has no scheduling data.");
return null;
}
// Dump invalid data in debug mode.
if (SystemProperties.getInt("ro.debuggable", 0) == 1) {
- new Thread(() -> {
- dumpInvalidData(scheduleInfoList);
- }).start();
+ dumpInvalidData(scheduleInfoList);
}
- final List<ScheduleInfo> filteredList = scheduleInfoList
+ return scheduleInfoList
.stream()
- .filter(scheduleInfo -> scheduleInfo.isValid())
- .collect(Collectors.toList());
-
- return new ArrayList<>(filteredList);
+ .filter(ScheduleInfo::isValid)
+ .collect(Collectors.toCollection(ArrayList::new));
}
private void dumpInvalidData(ArrayList<ScheduleInfo> scheduleInfoList) {
- Log.d(TAG, "package : " + getContext().getPackageName()
- + " provided some scheduling data are invalid.");
- scheduleInfoList
+ final boolean hasInvalidData = scheduleInfoList
.stream()
- .filter(scheduleInfo -> !scheduleInfo.isValid())
- .forEach(scheduleInfo -> Log.d(TAG, scheduleInfo.toString()));
+ .anyMatch(scheduleInfo -> !scheduleInfo.isValid());
+
+ if (hasInvalidData) {
+ Log.w(TAG, "package : " + getContext().getPackageName()
+ + " provided some scheduling data that are invalid.");
+ scheduleInfoList
+ .stream()
+ .filter(scheduleInfo -> !scheduleInfo.isValid())
+ .forEach(scheduleInfo -> Log.w(TAG, scheduleInfo.toString()));
+ }
}
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6d11461..2297ddf 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -892,9 +892,9 @@
<!-- UI debug setting: enable gpu debug layers summary [CHAR LIMIT=50] -->
<string name="enable_gpu_debug_layers_summary">Allow loading GPU debug layers for debug apps</string>
- <!-- UI debug setting: enable verbose vendor logging [CHAR LIMIT=30] -->
+ <!-- UI debug setting: enable verbose vendor logging [CHAR LIMIT=60] -->
<string name="enable_verbose_vendor_logging">Enable verbose vendor logging</string>
- <!-- UI debug setting: enable verbose vendor logging summary [CHAR LIMIT=100] -->
+ <!-- UI debug setting: enable verbose vendor logging summary [CHAR LIMIT=NONE] -->
<string name="enable_verbose_vendor_logging_summary">Allow additional vendor logs to be included in bug reports, may contain private information</string>
<!-- UI debug setting: scaling factor for window animations [CHAR LIMIT=25] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 3c0f6fe..57e6808 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -136,7 +136,7 @@
if (mDreamManager == null)
return null;
try {
- return mDreamManager.getDefaultDreamComponent();
+ return mDreamManager.getDefaultDreamComponentForUser(mContext.getUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed to get default dream", e);
return null;
@@ -269,7 +269,7 @@
if (mDreamManager == null || dreamInfo == null || dreamInfo.componentName == null)
return;
try {
- mDreamManager.testDream(dreamInfo.componentName);
+ mDreamManager.testDream(mContext.getUserId(), dreamInfo.componentName);
} catch (RemoteException e) {
Log.w(TAG, "Failed to preview " + dreamInfo, e);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index f69e4f5..3ae9e1e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -15,9 +15,15 @@
*/
package com.android.settingslib.media;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_BLUETOOTH;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_UNKNOWN;
+import static android.media.MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
+import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_GROUP;
+import static android.media.MediaRoute2Info.TYPE_HEARING_AID;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
+import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
import android.app.Notification;
import android.bluetooth.BluetoothAdapter;
@@ -165,6 +171,26 @@
}
/**
+ * Release session to stop playing media on MediaDevice.
+ */
+ boolean releaseSession() {
+ if (TextUtils.isEmpty(mPackageName)) {
+ Log.w(TAG, "releaseSession() package name is null or empty!");
+ return false;
+ }
+
+ final RoutingSessionInfo info = getRoutingSessionInfo();
+ if (info != null) {
+ mRouterManager.getControllerForSession(info).release();
+ return true;
+ }
+
+ Log.w(TAG, "releaseSession() Ignoring release session : " + mPackageName);
+
+ return false;
+ }
+
+ /**
* Get the MediaDevice list that can be added to current media.
*
* @return list of MediaDevice
@@ -298,7 +324,9 @@
private void buildAllRoutes() {
for (MediaRoute2Info route : mRouterManager.getAllRoutes()) {
- addMediaDevice(route);
+ if (route.isSystemRoute()) {
+ addMediaDevice(route);
+ }
}
}
@@ -309,27 +337,29 @@
}
private void addMediaDevice(MediaRoute2Info route) {
- final int deviceType = route.getDeviceType();
+ final int deviceType = route.getType();
MediaDevice mediaDevice = null;
switch (deviceType) {
- case DEVICE_TYPE_UNKNOWN:
+ case TYPE_UNKNOWN:
+ case TYPE_REMOTE_TV:
+ case TYPE_REMOTE_SPEAKER:
+ case TYPE_GROUP:
//TODO(b/148765806): use correct device type once api is ready.
- final String defaultRoute = "DEFAULT_ROUTE";
- if (TextUtils.equals(defaultRoute, route.getOriginalId())) {
- mediaDevice =
- new PhoneMediaDevice(mContext, mRouterManager, route, mPackageName);
- } else {
- mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route,
- mPackageName);
- if (!TextUtils.isEmpty(mPackageName)
- && TextUtils.equals(route.getClientPackageName(), mPackageName)) {
- mCurrentConnectedDevice = mediaDevice;
- }
+ mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route,
+ mPackageName);
+ if (!TextUtils.isEmpty(mPackageName)
+ && TextUtils.equals(route.getClientPackageName(), mPackageName)) {
+ mCurrentConnectedDevice = mediaDevice;
}
break;
- case DEVICE_TYPE_REMOTE_TV:
+ case TYPE_BUILTIN_SPEAKER:
+ case TYPE_WIRED_HEADSET:
+ case TYPE_WIRED_HEADPHONES:
+ mediaDevice =
+ new PhoneMediaDevice(mContext, mRouterManager, route, mPackageName);
break;
- case DEVICE_TYPE_BLUETOOTH:
+ case TYPE_HEARING_AID:
+ case TYPE_BLUETOOTH_A2DP:
final BluetoothDevice device =
BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getOriginalId());
final CachedBluetoothDevice cachedDevice =
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 617da6e..c70811f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -282,6 +282,13 @@
}
/**
+ * Release session to stop playing media on MediaDevice.
+ */
+ public boolean releaseSession() {
+ return mInfoMediaManager.releaseSession();
+ }
+
+ /**
* Get the MediaDevice list that has been selected to current media.
*
* @return list of MediaDevice
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 3f55cea..8bf48e59 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -68,6 +68,7 @@
private WifiInfo mWifiInfo;
public boolean enabled;
+ public boolean isCaptivePortal;
public int state;
public boolean connected;
public String ssid;
@@ -155,9 +156,11 @@
private void updateStatusLabel() {
final NetworkCapabilities networkCapabilities
= mConnectivityManager.getNetworkCapabilities(mWifiManager.getCurrentNetwork());
+ isCaptivePortal = false;
if (networkCapabilities != null) {
if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) {
statusLabel = mContext.getString(R.string.wifi_status_sign_in_required);
+ isCaptivePortal = true;
return;
} else if (networkCapabilities.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY)) {
statusLabel = mContext.getString(R.string.wifi_limited_connection);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index d233649..0e6a60b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -68,7 +68,8 @@
for (int reason = 0; reason <= getMaxNetworkSelectionDisableReason(); reason++) {
if (networkStatus.getDisableReasonCounter(reason) != 0) {
summary.append(" ")
- .append(NetworkSelectionStatus.getNetworkDisableReasonString(reason))
+ .append(NetworkSelectionStatus
+ .getNetworkSelectionDisableReasonString(reason))
.append("=")
.append(networkStatus.getDisableReasonCounter(reason));
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 9668629..edb121b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -96,6 +96,7 @@
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.isSystemRoute()).thenReturn(true);
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
@@ -166,6 +167,7 @@
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.isSystemRoute()).thenReturn(true);
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
@@ -221,6 +223,7 @@
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.isSystemRoute()).thenReturn(true);
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
@@ -438,4 +441,23 @@
assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1);
}
+
+ @Test
+ public void releaseSession_packageNameIsNull_returnFalse() {
+ mInfoMediaManager.mPackageName = null;
+
+ assertThat(mInfoMediaManager.releaseSession()).isFalse();
+ }
+
+ @Test
+ public void releaseSession_removeSuccessfully_returnTrue() {
+ final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+ final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
+ routingSessionInfos.add(info);
+
+ mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ assertThat(mInfoMediaManager.releaseSession()).isTrue();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java
new file mode 100644
index 0000000..5ec89ed
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.settingslib.schedulesprovider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class ScheduleInfoTest {
+ private static final String TEST_TITLE = "Night Light";
+ private static final String TEST_SUMMARY = "Night Light summary";
+ private static final String TEST_EMPTY_SUMMARY = "";
+
+ @Test
+ public void builder_usedValidArguments_isValid() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent);
+
+ assertThat(info).isNotNull();
+ assertThat(info.isValid()).isTrue();
+ }
+
+ @Test
+ public void builder_useEmptySummary_isInvalid() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_EMPTY_SUMMARY, intent);
+
+ assertThat(info).isNotNull();
+ assertThat(info.isValid()).isFalse();
+ }
+
+ @Test
+ public void builder_intentIsNull_isInvalid() {
+ final ScheduleInfo info = new ScheduleInfo.Builder()
+ .setTitle(TEST_TITLE)
+ .setSummary(TEST_SUMMARY)
+ .build();
+
+ assertThat(info).isNotNull();
+ assertThat(info.isValid()).isFalse();
+ }
+
+ @Test
+ public void getTitle_setValidTitle_shouldReturnSameCorrectTitle() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent);
+
+ assertThat(info.getTitle()).isEqualTo(TEST_TITLE);
+ }
+
+ @Test
+ public void getSummary_setValidSummary_shouldReturnSameCorrectSummary() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent);
+
+ assertThat(info.getSummary()).isEqualTo(TEST_SUMMARY);
+ }
+
+ @Test
+ public void getIntent_setValidIntent_shouldReturnSameCorrectIntent() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent);
+
+ assertThat(info.getIntent()).isEqualTo(intent);
+ }
+
+ private static Intent createTestIntent() {
+ return new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ }
+
+ private static ScheduleInfo createTestScheduleInfo(String title, String summary,
+ Intent intent) {
+ return new ScheduleInfo.Builder()
+ .setTitle(title)
+ .setSummary(summary)
+ .setIntent(intent)
+ .build();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java
new file mode 100644
index 0000000..eb2e8e0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.settingslib.schedulesprovider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.ArrayList;
+
+@RunWith(RobolectricTestRunner.class)
+public class SchedulesProviderTest {
+ private static final String INVALID_PACKAGE = "com.android.sunny";
+ private static final String VALID_PACKAGE = "com.android.settings";
+ private static final String INVALID_METHOD = "queryTestData";
+ private TestSchedulesProvider mProvider;
+
+ @Before
+ public void setUp() {
+ mProvider = Robolectric.setupContentProvider(TestSchedulesProvider.class);
+ shadowOf(mProvider).setCallingPackage(VALID_PACKAGE);
+ mProvider.setScheduleInfos(TestSchedulesProvider.createOneValidScheduleInfo());
+ }
+
+ @Test
+ public void call_invalidCallingPkg_returnNull() {
+ shadowOf(mProvider).setCallingPackage(INVALID_PACKAGE);
+
+ final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST,
+ null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNull();
+ }
+
+ @Test
+ public void call_invalidMethod_returnBundleWithoutScheduleInfoData() {
+ final Bundle bundle = mProvider.call(INVALID_METHOD, null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.containsKey(SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST)).isFalse();
+ }
+
+ @Test
+ public void call_validMethod_returnScheduleInfoData() {
+ final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST,
+ null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.containsKey(SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST)).isTrue();
+ final ArrayList<ScheduleInfo> infos = bundle.getParcelableArrayList(
+ SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST);
+ assertThat(infos).hasSize(1);
+ }
+
+ @Test
+ public void call_addTwoValidData_returnScheduleInfoData() {
+ mProvider.setScheduleInfos(TestSchedulesProvider.createTwoValidScheduleInfos());
+ final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST,
+ null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.containsKey(SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST)).isTrue();
+ final ArrayList<ScheduleInfo> infos = bundle.getParcelableArrayList(
+ SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST);
+ assertThat(infos).hasSize(2);
+ }
+
+ @Test
+ public void call_addTwoValidDataAndOneInvalidData_returnTwoScheduleInfoData() {
+ mProvider.setScheduleInfos(TestSchedulesProvider.createTwoValidAndOneInvalidScheduleInfo());
+ final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST,
+ null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.containsKey(SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST)).isTrue();
+ final ArrayList<ScheduleInfo> infos = bundle.getParcelableArrayList(
+ SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST);
+ assertThat(infos).hasSize(2);
+ }
+
+ private static class TestSchedulesProvider extends SchedulesProvider {
+ private ArrayList<ScheduleInfo> mScheduleInfos = new ArrayList<>();
+
+ @Override
+ public ArrayList<ScheduleInfo> getScheduleInfoList() {
+ return mScheduleInfos;
+ }
+
+ void setScheduleInfos(ArrayList<ScheduleInfo> scheduleInfos) {
+ mScheduleInfos = scheduleInfos;
+ }
+
+ private static ArrayList<ScheduleInfo> createOneValidScheduleInfo() {
+ final ArrayList<ScheduleInfo> scheduleInfos = new ArrayList<>();
+ final Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ final ScheduleInfo info = new ScheduleInfo.Builder().setTitle(
+ "Night Light").setSummary("This a sunny test").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ return scheduleInfos;
+ }
+
+ private static ArrayList<ScheduleInfo> createTwoValidScheduleInfos() {
+ final ArrayList<ScheduleInfo> scheduleInfos = new ArrayList<>();
+ Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ ScheduleInfo info = new ScheduleInfo.Builder().setTitle(
+ "Night Light").setSummary("This a sunny test").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ intent = new Intent("android.settings.DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ info = new ScheduleInfo.Builder().setTitle("Display").setSummary(
+ "Display summary").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ return scheduleInfos;
+ }
+
+ private static ArrayList<ScheduleInfo> createTwoValidAndOneInvalidScheduleInfo() {
+ final ArrayList<ScheduleInfo> scheduleInfos = new ArrayList<>();
+ Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ ScheduleInfo info = new ScheduleInfo.Builder().setTitle(
+ "Night Light").setSummary("This a sunny test").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ intent = new Intent("android.settings.DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ info = new ScheduleInfo.Builder().setTitle("Display").setSummary(
+ "Display summary").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ intent = new Intent("android.settings.DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ info = new ScheduleInfo.Builder().setTitle("").setSummary("Display summary").setIntent(
+ intent).build();
+ scheduleInfos.add(info);
+
+ return scheduleInfos;
+ }
+ }
+}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 0f2ee6a..610165a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -268,6 +268,7 @@
Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+ Settings.Global.ENHANCED_CONNECTIVITY_ENABLED,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
Settings.Global.ERROR_LOGCAT_PREFIX,
diff --git a/packages/StatementService/AndroidManifest.xml b/packages/StatementService/AndroidManifest.xml
index d79d900..b00c37f 100644
--- a/packages/StatementService/AndroidManifest.xml
+++ b/packages/StatementService/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application
android:label="@string/service_name"
diff --git a/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml b/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
index 7964609..015e9f9 100644
--- a/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
+++ b/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
@@ -16,6 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
- android:color="@color/notification_guts_priority_button_bg_stroke_color_selected" />
+ android:color="?android:attr/colorAccent" />
<item android:color="@color/notification_guts_priority_button_bg_stroke_color" />
</selector>
diff --git a/packages/SystemUI/res/color/notification_guts_priority_contents.xml b/packages/SystemUI/res/color/notification_guts_priority_contents.xml
index 56c43f0..42f0189 100644
--- a/packages/SystemUI/res/color/notification_guts_priority_contents.xml
+++ b/packages/SystemUI/res/color/notification_guts_priority_contents.xml
@@ -16,6 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
- android:color="@color/notification_guts_priority_button_content_color_selected" />
+ android:color="?android:attr/colorAccent" />
<item android:color="@color/notification_guts_priority_button_content_color" />
</selector>
diff --git a/packages/SystemUI/res/drawable/notification_guts_bg.xml b/packages/SystemUI/res/drawable/notification_guts_bg.xml
index 1730dce..2fe6c7b 100644
--- a/packages/SystemUI/res/drawable/notification_guts_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_guts_bg.xml
@@ -16,7 +16,7 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/notification_guts_bg_color" />
+ <solid android:color="@color/notification_material_background_color" />
<!--The radius is 1dp smaller than the notification one, to avoid aliasing bugs on the corners -->
<corners android:radius="1dp" />
</shape>
diff --git a/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml b/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml
new file mode 100644
index 0000000..e3d010e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape>
+ <solid android:color="@color/qs_user_detail_avatar_frame" />
+
+ <padding
+ android:left="1dp"
+ android:right="1dp"
+ android:bottom="1dp"
+ android:top="1dp" />
+
+ <corners android:radius="48dp" />
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml
new file mode 100644
index 0000000..1f38b1e
--- /dev/null
+++ b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<!-- LinearLayout -->
+<com.android.systemui.statusbar.policy.KeyguardUserDetailItemView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:layout_marginEnd="8dp"
+ android:gravity="end|center_vertical"
+ android:clickable="true"
+ android:background="@drawable/rounded_user_switcher_bg"
+ sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
+ sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
+ <TextView android:id="@+id/user_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="13dp"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
+ />
+ <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture"
+ android:layout_width="@dimen/framed_avatar_size"
+ android:layout_height="@dimen/framed_avatar_size"
+ android:contentDescription="@null"
+ sysui:badgeDiameter="18dp"
+ sysui:badgeMargin="1dp" />
+</com.android.systemui.statusbar.policy.KeyguardUserDetailItemView>
diff --git a/packages/SystemUI/res/layout/app_ops_info.xml b/packages/SystemUI/res/layout/app_ops_info.xml
index 82a0115..bfa252c 100644
--- a/packages/SystemUI/res/layout/app_ops_info.xml
+++ b/packages/SystemUI/res/layout/app_ops_info.xml
@@ -26,7 +26,7 @@
android:orientation="vertical"
android:paddingStart="@*android:dimen/notification_content_margin_start"
android:paddingEnd="@*android:dimen/notification_content_margin_end"
- android:background="@color/notification_guts_bg_color"
+ android:background="@color/notification_material_background_color"
android:theme="@*android:style/Theme.DeviceDefault.Light">
<!-- Package Info -->
diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml
index a7379be..6533c18 100644
--- a/packages/SystemUI/res/layout/controls_management.xml
+++ b/packages/SystemUI/res/layout/controls_management.xml
@@ -70,12 +70,14 @@
android:layout_height="match_parent"
android:padding="4dp">
- <TextView
+ <Button
+ android:id="@+id/other_apps"
+ android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="match_parent"
+ android:gravity="center_vertical"
android:text="See other apps"
- android:textAppearance="@style/TextAppearance.Control.Title"
- android:textColor="?android:attr/colorPrimary"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
@@ -85,6 +87,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="Done"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
diff --git a/packages/SystemUI/res/layout/controls_management_favorites.xml b/packages/SystemUI/res/layout/controls_management_favorites.xml
index 62056e6..aab32f4 100644
--- a/packages/SystemUI/res/layout/controls_management_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_management_favorites.xml
@@ -14,97 +14,26 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<androidx.constraintlayout.widget.ConstraintLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
- android:id="@+id/error_message"
+ android:id="@+id/status_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/controls_management_list_margin"
- android:text="@string/controls_favorite_load_error"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:visibility="gone"
android:gravity="center_horizontal"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="parent"
- app:layout_constraintBottom_toTopOf="@id/text_favorites"
/>
- <TextView
- android:id="@+id/text_favorites"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/controls_management_list_margin"
- android:text="@string/controls_favorite_header_favorites"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textAllCaps="true"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toTopOf="@id/divider1"
- app:layout_constraintTop_toBottomOf="@id/error_message"
- />
-
- <View
- android:id="@+id/divider1"
- android:layout_width="match_parent"
- android:layout_height="@dimen/controls_app_divider_height"
- android:layout_gravity="center_horizontal|top"
- android:background="?android:attr/listDivider"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toTopOf="@id/listFavorites"
- app:layout_constraintTop_toBottomOf="@id/text_favorites"
- />
-
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/listFavorites"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginTop="@dimen/controls_management_list_margin"
- android:nestedScrollingEnabled="false"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toTopOf="@id/text_all"
- app:layout_constraintTop_toBottomOf="@id/divider1"/>
-
- <TextView
- android:id="@+id/text_all"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/controls_management_list_margin"
- android:text="@string/controls_favorite_header_all"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textAllCaps="true"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toTopOf="@id/divider2"
- app:layout_constraintTop_toBottomOf="@id/listFavorites"
- />
-
- <View
- android:id="@+id/divider2"
- android:layout_width="match_parent"
- android:layout_height="@dimen/controls_app_divider_height"
- android:layout_gravity="center_horizontal|top"
- android:background="?android:attr/listDivider"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toTopOf="@id/listAll"
- app:layout_constraintTop_toBottomOf="@id/text_all"
- />
-
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/listAll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/controls_management_list_margin"
- android:nestedScrollingEnabled="false"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/divider2"/>
+ android:nestedScrollingEnabled="false"/>
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 0b74a11a..a76f961 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -32,10 +32,10 @@
android:alpha="0"/>
<HorizontalScrollView
android:id="@+id/global_screenshot_actions_container"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="bottom|center"
- android:elevation="3dp"
+ android:layout_gravity="bottom|left"
+ android:elevation="1dp"
android:fillViewport="true"
android:layout_marginHorizontal="@dimen/screenshot_action_container_margin_horizontal"
android:gravity="center"
@@ -63,7 +63,7 @@
android:id="@+id/global_screenshot_dismiss_button"
android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
- android:elevation="9dp"
+ android:elevation="7dp"
android:visibility="gone">
<ImageView
android:layout_width="match_parent"
@@ -76,7 +76,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@android:color/white"
- android:elevation="8dp"
+ android:elevation="@dimen/screenshot_preview_elevation"
android:visibility="gone"/>
<com.android.systemui.screenshot.ScreenshotSelectorView
android:id="@+id/global_screenshot_selector"
diff --git a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
index 79867a16b..6b94bef 100644
--- a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
@@ -19,7 +19,7 @@
android:id="@+id/global_screenshot_action_chip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/screenshot_action_chip_margin_horizontal"
+ android:layout_marginRight="@dimen/screenshot_action_chip_margin_right"
android:layout_gravity="center"
android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical"
android:background="@drawable/action_chip_background"
@@ -35,7 +35,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/screenshot_action_chip_padding_end"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textSize="@dimen/screenshot_action_chip_text_size"
- android:textStyle="bold"
android:textColor="@color/global_screenshot_button_text"/>
</com.android.systemui.screenshot.ScreenshotActionChip>
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index fb38e1c..b1e5165 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -27,8 +27,6 @@
android:gravity="center_vertical"
android:clickable="true"
android:background="@drawable/ripple_drawable"
- android:clipChildren="false"
- android:clipToPadding="false"
sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
<TextView android:id="@+id/user_name"
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 87de9d4..5d03eee 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -24,8 +24,7 @@
android:clipChildren="false"
android:clipToPadding="true"
android:orientation="vertical"
- android:paddingStart="@*android:dimen/notification_content_margin_start"
- android:background="@color/notification_guts_bg_color">
+ android:paddingStart="@*android:dimen/notification_content_margin_start">
<!-- Package Info -->
<RelativeLayout
diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml
index ffe2eee..c350ed2 100644
--- a/packages/SystemUI/res/layout/notification_snooze.xml
+++ b/packages/SystemUI/res/layout/notification_snooze.xml
@@ -21,7 +21,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:clickable="true"
- android:background="@color/notification_guts_bg_color"
+ android:background="@color/notification_material_background_color"
android:theme="@style/Theme.SystemUI">
<RelativeLayout
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 4fae3c5..f8db97d 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -24,7 +24,6 @@
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_height"
android:id="@+id/status_bar"
- android:background="@drawable/system_bar_background"
android:orientation="vertical"
android:focusable="false"
android:descendantFocusability="afterDescendants"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 36ba66a..5a1e5c4 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -33,8 +33,8 @@
android:layout_height="wrap_content"
android:layout_gravity="start"
android:focusable="true"
- android:contentDescription="@string/accessibility_manage_notification"
- android:text="@string/manage_notifications_text"
+ android:contentDescription="@string/manage_notifications_history_text"
+ android:text="@string/manage_notifications_history_text"
/>
<com.android.systemui.statusbar.notification.row.FooterViewButton
style="@style/TextAppearance.NotificationSectionHeaderButton"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 8fee2cf9..7142929 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -28,5 +28,6 @@
<FrameLayout
android:id="@+id/status_bar_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:background="@drawable/system_bar_background" />
</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 9a66e8b..9b0fe46 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -39,23 +39,16 @@
<!-- The color of the ripples on the untinted notifications -->
<color name="notification_ripple_untinted_color">#30ffffff</color>
- <!-- The "inside" of a notification, reached via longpress -->
- <color name="notification_guts_bg_color">@color/GM2_grey_900</color>
-
<!-- The color of the text inside a notification -->
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
<color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color>
<color name="notification_guts_sub_text_color">@color/GM2_grey_300</color>
<color name="notification_guts_header_text_color">@color/GM2_grey_200</color>
- <color name="notification_guts_info_button_color">@color/GM2_blue_300</color>
<color name="notification_guts_priority_button_content_color">@color/GM2_grey_500</color>
- <color name="notification_guts_priority_button_content_color_selected">@color/GM2_blue_300</color>
<color name="notification_guts_priority_button_bg_fill_color">@color/transparent</color>
<color name="notification_guts_priority_button_bg_fill_color_selected">@color/GM2_grey_800</color>
<color name="notification_guts_priority_button_bg_stroke_color">@color/GM2_grey_700</color>
- <color name="notification_guts_priority_button_bg_stroke_color_selected">@color/GM2_blue_300</color>
-
<color name="notification_section_header_label_color">@color/GM2_grey_200</color>
<color name="notification_section_clear_all_btn_color">@color/GM2_grey_500</color>
<color name="notification_channel_dialog_separator">@color/GM2_grey_700</color>
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index 23b64e3..b375364 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -22,4 +22,28 @@
<style name="UserDetailView">
<item name="numColumns">4</item>
</style>
+
+ <style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
+ <item name="android:textSize">@dimen/kg_user_switcher_text_size</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">?attr/wallpaperTextColor</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
+ <item name="android:fontWeight">700</item>
+ <item name="android:textStyle">bold</item>
+ <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
+ </style>
+
+ <style name="TextAppearance.QS.UserSwitcher">
+ <item name="android:textSize">@dimen/qs_detail_item_primary_text_size</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ </style>
+
+ <style name="TextAppearance.QS.UserSwitcher.Activated">
+ <item name="android:fontWeight">700</item>
+ <item name="android:textStyle">bold</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 43ceb4e..73e49ce 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -88,21 +88,16 @@
<!-- The color of the text inside a notification -->
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_light</color>
- <!-- The "inside" of a notification, reached via longpress -->
- <color name="notification_guts_bg_color">@color/GM2_grey_50</color>
<color name="notification_guts_link_icon_tint">@color/GM2_grey_700</color>
<color name="notification_guts_sub_text_color">@color/GM2_grey_700</color>
<color name="notification_guts_header_text_color">@color/GM2_grey_900</color>
<color name="notification_silence_color">#FF32c1de</color>
<color name="notification_alert_color">#FFF87B2B</color>
- <color name="notification_guts_info_button_color">@color/GM2_blue_700</color>
<color name="notification_guts_priority_button_content_color">@color/GM2_grey_700</color>
- <color name="notification_guts_priority_button_content_color_selected">@color/GM2_blue_700</color>
<color name="notification_guts_priority_button_bg_fill_color">@color/transparent</color>
<color name="notification_guts_priority_button_bg_fill_color_selected">#FFFFFF</color>
<color name="notification_guts_priority_button_bg_stroke_color">@color/GM2_grey_300</color>
- <color name="notification_guts_priority_button_bg_stroke_color_selected">@color/GM2_blue_600</color>
<color name="notification_section_header_label_color">@color/GM2_grey_900</color>
<color name="notification_section_clear_all_btn_color">@color/GM2_grey_700</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 8de2df5..27ffcee 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -201,6 +201,13 @@
always-on display) -->
<string name="doze_brightness_sensor_type" translatable="false"></string>
+ <!-- Override value to use for proximity sensor. -->
+ <string name="proximity_sensor_type" translatable="false"></string>
+
+ <!-- If using proximity_sensor_type, specifies a threshold value to distinguish near and
+ far break points. A sensor value less than this is considered "near". -->
+ <item name="proximity_sensor_threshold" translatable="false" format="float" type="dimen"></item>
+
<!-- Doze: pulse parameter - how long does it take to fade in? -->
<integer name="doze_pulse_duration_in">130</integer>
@@ -464,6 +471,9 @@
<!-- Allow dragging the PIP to a location to close it -->
<bool name="config_pipEnableDismissDragToEdge">true</bool>
+ <!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu -->
+ <bool name="config_pipEnableResizeForMenu">true</bool>
+
<!-- SystemUI Plugins that can be loaded on user builds. -->
<string-array name="config_pluginWhitelist" translatable="false">
<item>com.android.systemui</item>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1c2404f..12b9254 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -124,6 +124,9 @@
<!-- Increased height of a collapsed media notification in the status bar -->
<dimen name="notification_min_height_media">160dp</dimen>
+ <!-- Increased height of a collapsed messaging notification in the status bar -->
+ <dimen name="notification_min_height_messaging">118dp</dimen>
+
<!-- Height of a small notification in the status bar which was used before android N -->
<dimen name="notification_min_height_legacy">64dp</dimen>
@@ -302,7 +305,7 @@
<dimen name="global_screenshot_legacy_bg_padding">20dp</dimen>
<dimen name="global_screenshot_bg_padding">20dp</dimen>
<dimen name="global_screenshot_x_scale">80dp</dimen>
- <dimen name="screenshot_preview_elevation">8dp</dimen>
+ <dimen name="screenshot_preview_elevation">6dp</dimen>
<dimen name="screenshot_offset_y">48dp</dimen>
<dimen name="screenshot_offset_x">16dp</dimen>
<dimen name="screenshot_dismiss_button_tappable_size">48dp</dimen>
@@ -311,13 +314,13 @@
<dimen name="screenshot_action_container_corner_radius">10dp</dimen>
<dimen name="screenshot_action_container_padding_vertical">10dp</dimen>
<dimen name="screenshot_action_container_margin_horizontal">8dp</dimen>
- <dimen name="screenshot_action_container_padding_left">100dp</dimen>
+ <dimen name="screenshot_action_container_padding_left">96dp</dimen>
<dimen name="screenshot_action_container_padding_right">8dp</dimen>
<!-- Radius of the chip background on global screenshot actions -->
<dimen name="screenshot_button_corner_radius">20dp</dimen>
- <dimen name="screenshot_action_chip_margin_horizontal">4dp</dimen>
- <dimen name="screenshot_action_chip_padding_vertical">10dp</dimen>
- <dimen name="screenshot_action_chip_icon_size">20dp</dimen>
+ <dimen name="screenshot_action_chip_margin_right">8dp</dimen>
+ <dimen name="screenshot_action_chip_padding_vertical">7dp</dimen>
+ <dimen name="screenshot_action_chip_icon_size">18dp</dimen>
<dimen name="screenshot_action_chip_padding_start">8dp</dimen>
<!-- Padding between icon and text -->
<dimen name="screenshot_action_chip_padding_middle">8dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4aafec8..ff28b4d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2614,8 +2614,8 @@
<string name="controls_providers_subtitle">Choose an app from which to add controls</string>
<!-- Number of favorites for controls management screen [CHAR LIMIT=NONE]-->
<plurals name="controls_number_of_favorites">
- <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> current favorite.</item>
- <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> current favorites.</item>
+ <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> control added.</item>
+ <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> controls added.</item>
</plurals>
<!-- Controls management controls screen default title [CHAR LIMIT=30] -->
@@ -2626,6 +2626,10 @@
<string name="controls_favorite_header_favorites">Favorites</string>
<!-- Controls management controls screen all header [CHAR LIMIT=50] -->
<string name="controls_favorite_header_all">All</string>
- <!-- Controls management controls screen error on load message [CHAR LIMIT=50] -->
+ <!-- Controls management controls screen error on load message [CHAR LIMIT=60] -->
<string name="controls_favorite_load_error">The list of all controls could not be loaded.</string>
+ <!-- Controls management controls screen header for Other zone [CHAR LIMIT=60] -->
+ <string name="controls_favorite_other_zone_header">Other</string>
+
+
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 36c4526..d9b1452 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -514,7 +514,7 @@
<style name="TextAppearance.NotificationInfo.Button">
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:textSize">14sp</item>
- <item name="android:textColor">@color/notification_guts_info_button_color</item>
+ <item name="android:textColor">?android:attr/colorAccent</item>
<item name="android:background">@drawable/btn_borderless_rect</item>
<item name="android:gravity">center_vertical</item>
<item name="android:focusable">true</item>
@@ -550,7 +550,7 @@
<style name="TextAppearance.NotificationImportanceButton">
<item name="android:textSize">@dimen/notification_importance_button_text</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:textColor">@color/notification_guts_priority_contents</item>
+ <item name="android:textColor">?android:attr/colorAccent</item>
<item name="android:gravity">center</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl
new file mode 100644
index 0000000..97aa512
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.shared.recents;
+
+/**
+ * Listener interface that Launcher attaches to SystemUI to get
+ * pinned stack animation callbacks.
+ */
+oneway interface IPinnedStackAnimationListener {
+ /**
+ * Notifies the pinned stack animation is started.
+ */
+ void onPinnedStackAnimationStarted();
+}
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 b1d39f5..80fd826 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
@@ -22,9 +22,11 @@
import android.os.Bundle;
import android.view.MotionEvent;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+
/**
* Temporary callbacks into SystemUI.
- * Next id = 22
+ * Next id = 25
*/
interface ISystemUiProxy {
@@ -121,11 +123,21 @@
/**
* Handle the provided image as if it was a screenshot.
*/
- void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
+ void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
in Insets visibleInsets, int taskId) = 21;
/**
* Sets the split-screen divider minimized state
*/
void setSplitScreenMinimized(boolean minimized) = 22;
+
+ /*
+ * Notifies that the swipe-to-home (recents animation) is finished.
+ */
+ void notifySwipeToHomeFinished() = 23;
+
+ /**
+ * Sets listener to get pinned stack animation callbacks.
+ */
+ void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) = 24;
}
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 4474a49..eca6ebf 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
@@ -62,7 +62,9 @@
orientation = snapshot.getOrientation();
rotation = snapshot.getRotation();
reducedResolution = snapshot.isLowResolution();
- scale = snapshot.getScale();
+ // TODO(b/149579527): Pass task size instead of computing scale.
+ // Assume width and height were scaled the same; compute scale only for width
+ scale = (float) thumbnail.getWidth() / snapshot.getTaskSize().x;
isRealSnapshot = snapshot.isRealSnapshot();
isTranslucent = snapshot.isTranslucent();
windowingMode = snapshot.getWindowingMode();
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 a130092..49e3e57 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
@@ -370,8 +370,7 @@
Rect initialBounds) {
try {
return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(taskId,
- createMode, true /* onTop */, false /* animate */, initialBounds,
- true /* showRecents */);
+ true /* onTop */);
} catch (RemoteException e) {
return false;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
index 8c0ffb8..360244c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
@@ -53,11 +53,9 @@
}
@Override
- public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
- boolean fromShelfAdjustment) {
+ public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {
for (PinnedStackListener listener : mListeners) {
- listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment,
- fromShelfAdjustment);
+ listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment);
}
}
@@ -69,13 +67,6 @@
}
@Override
- public void onMinimizedStateChanged(boolean isMinimized) {
- for (PinnedStackListener listener : mListeners) {
- listener.onMinimizedStateChanged(isMinimized);
- }
- }
-
- @Override
public void onActionsChanged(ParceledListSlice actions) {
for (PinnedStackListener listener : mListeners) {
listener.onActionsChanged(actions);
@@ -117,13 +108,6 @@
}
}
- @Override
- public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
- for (PinnedStackListener listener : mListeners) {
- listener.onPrepareAnimation(sourceRectHint, aspectRatio, bounds);
- }
- }
-
/**
* A counterpart of {@link IPinnedStackListener} with empty implementations.
* Subclasses can ignore those methods they do not intend to take action upon.
@@ -131,13 +115,10 @@
public static class PinnedStackListener {
public void onListenerRegistered(IPinnedStackController controller) {}
- public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
- boolean fromShelfAdjustment) {}
+ public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {}
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
- public void onMinimizedStateChanged(boolean isMinimized) {}
-
public void onActionsChanged(ParceledListSlice actions) {}
public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {}
@@ -149,7 +130,5 @@
public void onConfigurationChanged() {}
public void onAspectRatioChanged(float aspectRatio) {}
-
- public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {}
}
}
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 748f356..bbb83c7 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
@@ -61,14 +61,6 @@
}
}
- public void setSplitScreenMinimized(boolean minimized) {
- try {
- mAnimationController.setSplitScreenMinimized(minimized);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to set minimize dock", e);
- }
- }
-
public void hideCurrentInputMethod() {
try {
mAnimationController.hideCurrentInputMethod();
@@ -91,15 +83,6 @@
}
}
- @Deprecated
- public void setCancelWithDeferredScreenshot(boolean screenshot) {
- try {
- mAnimationController.setCancelWithDeferredScreenshot(screenshot);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to set cancel with deferred screenshot", e);
- }
- }
-
public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
try {
mAnimationController.setDeferCancelUntilNextTransition(defer, screenshot);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 5f92b28..1c6223b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -20,8 +20,6 @@
import android.app.ITaskStackListener;
import android.content.ComponentName;
import android.os.IBinder;
-import android.os.UserHandle;
-import android.util.Log;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -40,8 +38,6 @@
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
public void onActivityUnpinned() { }
public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
- public void onPinnedStackAnimationStarted() { }
- public void onPinnedStackAnimationEnded() { }
public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
public void onActivityDismissingDockedStack() { }
public void onActivityLaunchOnSecondaryDisplayFailed() { }
@@ -117,22 +113,4 @@
/** @see ITaskStackListener#onRecentTaskListFrozenChanged(boolean) */
public void onRecentTaskListFrozenChanged(boolean frozen) { }
-
- /**
- * Checks that the current user matches the process. Since
- * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
- * {@link TaskStackChangeListener} should make this call to verify that we don't act on events
- * originating from another user's interactions.
- */
- protected final boolean checkCurrentUserId(int currentUserId, boolean debug) {
- int processUserId = UserHandle.myUserId();
- if (processUserId != currentUserId) {
- if (debug) {
- Log.d("TaskStackChangeListener", "UID mismatch. Process is uid=" + processUserId
- + " and the current user is uid=" + currentUserId);
- }
- return false;
- }
- return true;
- }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index acce41c..cbdd3f8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -128,18 +128,6 @@
}
@Override
- public void onPinnedStackAnimationStarted() throws RemoteException {
- mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_STARTED);
- mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_STARTED);
- }
-
- @Override
- public void onPinnedStackAnimationEnded() throws RemoteException {
- mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
- mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
- }
-
- @Override
public void onActivityForcedResizable(String packageName, int taskId, int reason)
throws RemoteException {
mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
@@ -249,11 +237,9 @@
private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
private static final int ON_ACTIVITY_PINNED = 3;
private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4;
- private static final int ON_PINNED_STACK_ANIMATION_ENDED = 5;
private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6;
private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7;
private static final int ON_TASK_PROFILE_LOCKED = 8;
- private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9;
private static final int ON_ACTIVITY_UNPINNED = 10;
private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11;
private static final int ON_TASK_CREATED = 12;
@@ -317,18 +303,6 @@
}
break;
}
- case ON_PINNED_STACK_ANIMATION_STARTED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onPinnedStackAnimationStarted();
- }
- break;
- }
- case ON_PINNED_STACK_ANIMATION_ENDED: {
- for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
- }
- break;
- }
case ON_ACTIVITY_FORCED_RESIZABLE: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onActivityForcedResizable(
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index e28b1e2..d64bf77 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -191,18 +191,6 @@
}
/**
- * Registers a docked stack listener with the system.
- */
- public void registerDockedStackListener(DockedStackListenerCompat listener) {
- try {
- WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
- listener.mListener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to register docked stack listener");
- }
- }
-
- /**
* Adds a pinned stack listener, which will receive updates from the window manager service
* along with any other pinned stack listeners that were added via this method.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 4473b01..dbcdead 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -20,7 +20,9 @@
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.HandlerThread;
+import android.os.SystemClock;
import android.os.Trace;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
@@ -94,18 +96,28 @@
private EglHelper mEglHelper;
private StatusBarStateController mController;
private final Runnable mFinishRenderingTask = this::finishRendering;
- private final boolean mNeedTransition;
private boolean mShouldStopTransition;
- @VisibleForTesting
- final boolean mIsHighEndGfx;
- private final boolean mDisplayNeedsBlanking;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Object mMonitor = new Object();
+ @VisibleForTesting
+ boolean mIsHighEndGfx;
+ private boolean mDisplayNeedsBlanking;
+ private boolean mNeedTransition;
private boolean mNeedRedraw;
// This variable can only be accessed in synchronized block.
private boolean mWaitingForRendering;
GLEngine(Context context, DozeParameters dozeParameters) {
+ init(dozeParameters);
+ }
+
+ @VisibleForTesting
+ GLEngine(DozeParameters dozeParameters, Handler handler) {
+ super(SystemClock::elapsedRealtime, handler);
+ init(dozeParameters);
+ }
+
+ private void init(DozeParameters dozeParameters) {
mIsHighEndGfx = ActivityManager.isHighEndGfx();
mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking();
mNeedTransition = mIsHighEndGfx && !mDisplayNeedsBlanking;
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 10ae343..f0a82c5 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -164,7 +164,7 @@
get(context).unregisterOnSharedPreferenceChangeListener(listener);
}
- private static SharedPreferences get(Context context) {
+ public static SharedPreferences get(Context context) {
return context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 6ce6353..0f896c4 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -99,7 +99,8 @@
private static final boolean DEBUG_COLOR = DEBUG_SCREENSHOT_ROUNDED_CORNERS;
private DisplayManager mDisplayManager;
- private boolean mIsRegistered;
+ @VisibleForTesting
+ protected boolean mIsRegistered;
private final BroadcastDispatcher mBroadcastDispatcher;
private final Handler mMainHandler;
private final TunerService mTunerService;
@@ -168,7 +169,6 @@
mDisplayManager = mContext.getSystemService(DisplayManager.class);
updateRoundedCornerRadii();
setupDecorations();
-
mDisplayListener = new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(int displayId) {
@@ -230,7 +230,10 @@
removeAllOverlays();
}
- if (hasOverlays() && !mIsRegistered) {
+ if (hasOverlays()) {
+ if (mIsRegistered) {
+ return;
+ }
DisplayMetrics metrics = new DisplayMetrics();
mDisplayManager.getDisplay(DEFAULT_DISPLAY).getMetrics(metrics);
mDensity = metrics.density;
@@ -271,7 +274,8 @@
return mContext.getDisplay().getCutout();
}
- private boolean hasOverlays() {
+ @VisibleForTesting
+ boolean hasOverlays() {
if (mOverlays == null) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 20b3386..2873811 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -73,9 +73,6 @@
private static final String WHITELISTED_AUTO_BUBBLE_APPS = "whitelisted_auto_bubble_apps";
- private static final String ALLOW_BUBBLE_OVERFLOW = "allow_bubble_overflow";
- private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = false;
-
/**
* When true, if a notification has the information necessary to bubble (i.e. valid
* contentIntent and an icon or image), then a {@link android.app.Notification.BubbleMetadata}
@@ -131,16 +128,6 @@
}
/**
- * When true, show a menu when a bubble is long-pressed, which will allow the user to take
- * actions on that bubble.
- */
- static boolean allowBubbleOverflow(Context context) {
- return Settings.Secure.getInt(context.getContentResolver(),
- ALLOW_BUBBLE_OVERFLOW,
- ALLOW_BUBBLE_OVERFLOW_DEFAULT ? 1 : 0) != 0;
- }
-
- /**
* If {@link #allowAnyNotifToBubble(Context)} is true, this method creates and adds
* {@link android.app.Notification.BubbleMetadata} to the notification entry as long as
* the notification has necessary info for BubbleMetadata.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 072c20c..df8e394 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -520,9 +520,6 @@
}
private void setUpOverflow() {
- if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
- return;
- }
int overflowBtnIndex = 0;
if (mBubbleOverflow == null) {
mBubbleOverflow = new BubbleOverflow(mContext);
@@ -733,8 +730,7 @@
@Nullable
Bubble getExpandedBubble() {
if (mExpandedBubble == null
- || (BubbleExperimentConfig.allowBubbleOverflow(mContext)
- && mExpandedBubble.getIconView() == mBubbleOverflow.getBtn()
+ || (mExpandedBubble.getIconView() == mBubbleOverflow.getBtn()
&& mExpandedBubble.getKey() == BubbleOverflow.KEY)) {
return null;
}
@@ -785,9 +781,6 @@
}
private void updateOverflowBtnVisibility(boolean apply) {
- if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
- return;
- }
if (mIsExpanded) {
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "Show overflow button.");
@@ -911,8 +904,7 @@
float y = event.getRawY();
if (mIsExpanded) {
if (isIntersecting(mBubbleContainer, x, y)) {
- if (BubbleExperimentConfig.allowBubbleOverflow(mContext)
- && isIntersecting(mBubbleOverflow.getBtn(), x, y)) {
+ if (isIntersecting(mBubbleOverflow.getBtn(), x, y)) {
return mBubbleOverflow.getBtn();
}
// Could be tapping or dragging a bubble while expanded
@@ -1645,11 +1637,8 @@
* @return the number of bubbles in the stack view.
*/
public int getBubbleCount() {
- if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
- // Subtract 1 for the overflow button which is always in the bubble container.
- return mBubbleContainer.getChildCount() - 1;
- }
- return mBubbleContainer.getChildCount();
+ // Subtract 1 for the overflow button that is always in the bubble container.
+ return mBubbleContainer.getChildCount() - 1;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 0ac1c12..79b691b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -39,6 +39,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.FalsingPlugin;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.ProximitySensor;
@@ -69,6 +70,7 @@
private final DockManager mDockManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private Executor mUiBgExecutor;
+ private final StatusBarStateController mStatusBarStateController;
@Inject
FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor,
@@ -76,12 +78,14 @@
DeviceConfigProxy deviceConfig, DockManager dockManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DumpManager dumpManager,
- @UiBackground Executor uiBgExecutor) {
+ @UiBackground Executor uiBgExecutor,
+ StatusBarStateController statusBarStateController) {
mDisplayMetrics = displayMetrics;
mProximitySensor = proximitySensor;
mDockManager = dockManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mUiBgExecutor = uiBgExecutor;
+ mStatusBarStateController = statusBarStateController;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME);
mDeviceConfig = deviceConfig;
@@ -143,7 +147,8 @@
mKeyguardUpdateMonitor,
mProximitySensor,
mDeviceConfig,
- mDockManager
+ mDockManager,
+ mStatusBarStateController
);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index a084ae6..ec81b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -32,6 +32,8 @@
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.ProximitySensor;
@@ -59,6 +61,7 @@
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ProximitySensor mProximitySensor;
private final DockManager mDockManager;
+ private final StatusBarStateController mStatusBarStateController;
private boolean mSessionStarted;
private MetricsLogger mMetricsLogger;
private int mIsFalseTouchCalls;
@@ -88,15 +91,29 @@
};
private boolean mPreviousResult = false;
+ private StatusBarStateController.StateListener mStatusBarStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ logDebug("StatusBarState=" + StatusBarState.toShortString(newState));
+ mState = newState;
+ updateSessionActive();
+ }
+ };
+ private int mState;
+
public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
KeyguardUpdateMonitor keyguardUpdateMonitor, ProximitySensor proximitySensor,
DeviceConfigProxy deviceConfigProxy,
- DockManager dockManager) {
+ DockManager dockManager, StatusBarStateController statusBarStateController) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDataProvider = falsingDataProvider;
mProximitySensor = proximitySensor;
mDockManager = dockManager;
+ mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mState = mStatusBarStateController.getState();
mMetricsLogger = new MetricsLogger();
mClassifiers = new ArrayList<>();
@@ -116,13 +133,12 @@
mProximitySensor.register(mSensorEventListener);
}
-
private void unregisterSensors() {
mProximitySensor.unregister(mSensorEventListener);
}
private void sessionStart() {
- if (!mSessionStarted && !mShowingAod && mScreenOn) {
+ if (!mSessionStarted && shouldSessionBeActive()) {
logDebug("Starting Session");
mSessionStarted = true;
mJustUnlockedWithFace = false;
@@ -145,6 +161,19 @@
}
}
+
+ private void updateSessionActive() {
+ if (shouldSessionBeActive()) {
+ sessionStart();
+ } else {
+ sessionEnd();
+ }
+ }
+
+ private boolean shouldSessionBeActive() {
+ return mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod;
+ }
+
private void updateInteractionType(@Classifier.InteractionType int type) {
logDebug("InteractionType: " + type);
mDataProvider.setInteractionType(type);
@@ -232,11 +261,7 @@
@Override
public void setShowingAod(boolean showingAod) {
mShowingAod = showingAod;
- if (showingAod) {
- sessionEnd();
- } else {
- sessionStart();
- }
+ updateSessionActive();
}
@Override
@@ -343,13 +368,13 @@
@Override
public void onScreenTurningOn() {
mScreenOn = true;
- sessionStart();
+ updateSessionActive();
}
@Override
public void onScreenOff() {
mScreenOn = false;
- sessionEnd();
+ updateSessionActive();
}
@@ -421,6 +446,7 @@
public void cleanup() {
unregisterSensors();
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
static void logDebug(String msg) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 6ff1bbc..a67f6bd 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.controller
+import android.app.ActivityManager
import android.content.ComponentName
import android.content.Context
import android.os.IBinder
@@ -50,7 +51,7 @@
private val refreshing = AtomicBoolean(false)
- private var currentUser = context.user
+ private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
override val currentUserId: Int
get() = currentUser.identifier
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index a2a08502..3b06ebe 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.controller
+import android.app.ActivityManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
@@ -67,6 +68,7 @@
internal const val CONTROLS_AVAILABLE = "systemui.controls_available"
internal val URI = Settings.Secure.getUriFor(CONTROLS_AVAILABLE)
private const val USER_CHANGE_RETRY_DELAY = 500L // ms
+ private const val DEFAULT_ENABLED = 1
}
// Map of map: ComponentName -> (String -> ControlInfo).
@@ -77,15 +79,16 @@
private var userChanging: Boolean = true
- private val contentResolver: ContentResolver
- get() = context.contentResolver
- override var available = Settings.Secure.getInt(contentResolver, CONTROLS_AVAILABLE, 0) != 0
- private set
-
- private var currentUser = context.user
+ private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
override val currentUserId
get() = currentUser.identifier
+ private val contentResolver: ContentResolver
+ get() = context.contentResolver
+ override var available = Settings.Secure.getIntForUser(
+ contentResolver, CONTROLS_AVAILABLE, DEFAULT_ENABLED, currentUserId) != 0
+ private set
+
private val persistenceWrapper = optionalWrapper.orElseGet {
ControlsFavoritePersistenceWrapper(
Environment.buildPath(
@@ -104,7 +107,7 @@
userContext.filesDir, ControlsFavoritePersistenceWrapper.FILE_NAME)
persistenceWrapper.changeFile(fileName)
available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
- /* default */ 0, newUser.identifier) != 0
+ DEFAULT_ENABLED, newUser.identifier) != 0
synchronized(currentFavorites) {
currentFavorites.clear()
}
@@ -140,7 +143,7 @@
return
}
available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
- /* default */ 0, currentUserId) != 0
+ DEFAULT_ENABLED, currentUserId) != 0
synchronized(currentFavorites) {
currentFavorites.clear()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
new file mode 100644
index 0000000..c053517
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.controls.management
+
+import android.text.TextUtils
+import android.util.ArrayMap
+import com.android.systemui.controls.ControlStatus
+import com.android.systemui.controls.controller.ControlInfo
+
+/**
+ * This model is used to show all controls separated by zones.
+ *
+ * The model will sort the controls and zones in the following manner:
+ * * The zones will be sorted in a first seen basis
+ * * The controls in each zone will be sorted in a first seen basis.
+ *
+ * @property controls List of all controls as returned by loading
+ * @property initialFavoriteIds sorted ids of favorite controls
+ * @property noZoneString text to use as header for all controls that have blank or `null` zone.
+ */
+class AllModel(
+ private val controls: List<ControlStatus>,
+ initialFavoriteIds: List<String>,
+ private val emptyZoneString: CharSequence
+) : ControlsModel {
+
+ override val favorites: List<ControlInfo.Builder>
+ get() = favoriteIds.mapNotNull { id ->
+ val control = controls.firstOrNull { it.control.controlId == id }?.control
+ control?.let {
+ ControlInfo.Builder().apply {
+ controlId = it.controlId
+ controlTitle = it.title
+ deviceType = it.deviceType
+ }
+ }
+ }
+
+ private val favoriteIds = initialFavoriteIds.toMutableList()
+
+ override val elements: List<ElementWrapper> = createWrappers(controls)
+
+ override fun changeFavoriteStatus(controlId: String, favorite: Boolean) {
+ if (favorite) {
+ favoriteIds.add(controlId)
+ } else {
+ favoriteIds.remove(controlId)
+ }
+ }
+
+ private fun createWrappers(list: List<ControlStatus>): List<ElementWrapper> {
+ val map = list.groupByTo(OrderedMap(ArrayMap<CharSequence, MutableList<ControlStatus>>())) {
+ it.control.zone ?: ""
+ }
+ val output = mutableListOf<ElementWrapper>()
+ var emptyZoneValues: Sequence<ControlWrapper>? = null
+ for (zoneName in map.orderedKeys) {
+ val values = map.getValue(zoneName).asSequence().map { ControlWrapper(it) }
+ if (TextUtils.isEmpty(zoneName)) {
+ emptyZoneValues = values
+ } else {
+ output.add(ZoneNameWrapper(zoneName))
+ output.addAll(values)
+ }
+ }
+ // Add controls with empty zone at the end
+ if (emptyZoneValues != null) {
+ if (map.size != 1) {
+ output.add(ZoneNameWrapper(emptyZoneString))
+ }
+ output.addAll(emptyZoneValues)
+ }
+ return output
+ }
+
+ private class OrderedMap<K, V>(private val map: MutableMap<K, V>) : MutableMap<K, V> by map {
+
+ val orderedKeys = mutableListOf<K>()
+
+ override fun put(key: K, value: V): V? {
+ if (key !in map) {
+ orderedKeys.add(key)
+ }
+ return map.put(key, value)
+ }
+
+ override fun clear() {
+ orderedKeys.clear()
+ map.clear()
+ }
+
+ override fun remove(key: K): V? {
+ val removed = map.remove(key)
+ if (removed != null) {
+ orderedKeys.remove(key)
+ }
+ return removed
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index ac5e089..25ebc65 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -116,6 +116,10 @@
fun renderFavoritesForComponent(component: ComponentName): String {
val qty = favoriteFunction(component)
- return resources.getQuantityString(R.plurals.controls_number_of_favorites, qty, qty)
+ if (qty != 0) {
+ return resources.getQuantityString(R.plurals.controls_number_of_favorites, qty, qty)
+ } else {
+ return ""
+ }
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index d3cabe6..0870a4d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -42,8 +42,7 @@
* @param onlyFavorites set to true to only display favorites instead of all controls
*/
class ControlAdapter(
- private val layoutInflater: LayoutInflater,
- private val onlyFavorites: Boolean = false
+ private val layoutInflater: LayoutInflater
) : RecyclerView.Adapter<Holder>() {
companion object {
@@ -57,22 +56,21 @@
}
}
- var modelList: List<ElementWrapper> = emptyList()
- private var favoritesModel: FavoriteModel? = null
+ private var model: ControlsModel? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
return when (viewType) {
TYPE_CONTROL -> {
ControlHolder(
- layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply {
- layoutParams.apply {
- width = ViewGroup.LayoutParams.MATCH_PARENT
- }
- elevation = 15f
- },
- { id, favorite ->
- favoritesModel?.changeFavoriteStatus(id, favorite)
- })
+ layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply {
+ layoutParams.apply {
+ width = ViewGroup.LayoutParams.MATCH_PARENT
+ }
+ elevation = 15f
+ }
+ ) { id, favorite ->
+ model?.changeFavoriteStatus(id, favorite)
+ }
}
TYPE_ZONE -> {
ZoneHolder(layoutInflater.inflate(R.layout.controls_zone_header, parent, false))
@@ -81,27 +79,26 @@
}
}
- fun changeFavoritesModel(favoritesModel: FavoriteModel) {
- this.favoritesModel = favoritesModel
- if (onlyFavorites) {
- modelList = favoritesModel.favorites
- } else {
- modelList = favoritesModel.all
- }
+ fun changeModel(model: ControlsModel) {
+ this.model = model
notifyDataSetChanged()
}
- override fun getItemCount() = modelList.size
+ override fun getItemCount() = model?.elements?.size ?: 0
override fun onBindViewHolder(holder: Holder, index: Int) {
- holder.bindData(modelList[index])
+ model?.let {
+ holder.bindData(it.elements[index])
+ }
}
override fun getItemViewType(position: Int): Int {
- return when (modelList[position]) {
- is ZoneNameWrapper -> TYPE_ZONE
- is ControlWrapper -> TYPE_CONTROL
- }
+ model?.let {
+ return when (it.elements.get(position)) {
+ is ZoneNameWrapper -> TYPE_ZONE
+ is ControlWrapper -> TYPE_CONTROL
+ }
+ } ?: throw IllegalStateException("Getting item type for null model")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index af4a977..2c01449 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -26,11 +26,9 @@
import android.widget.Button
import android.widget.TextView
import androidx.recyclerview.widget.GridLayoutManager
-import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.controls.controller.ControlInfo
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.CurrentUserTracker
@@ -51,33 +49,10 @@
private lateinit var recyclerViewAll: RecyclerView
private lateinit var adapterAll: ControlAdapter
- private lateinit var recyclerViewFavorites: RecyclerView
- private lateinit var adapterFavorites: ControlAdapter
- private lateinit var errorText: TextView
+ private lateinit var statusText: TextView
+ private var model: ControlsModel? = null
private var component: ComponentName? = null
- private var currentModel: FavoriteModel? = null
- private var itemTouchHelperCallback = object : ItemTouchHelper.SimpleCallback(
- /* dragDirs */ ItemTouchHelper.UP
- or ItemTouchHelper.DOWN
- or ItemTouchHelper.LEFT
- or ItemTouchHelper.RIGHT,
- /* swipeDirs */0
- ) {
- override fun onMove(
- recyclerView: RecyclerView,
- viewHolder: RecyclerView.ViewHolder,
- target: RecyclerView.ViewHolder
- ): Boolean {
- return currentModel?.onMoveItem(
- viewHolder.layoutPosition, target.layoutPosition) != null
- }
-
- override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
-
- override fun isItemViewSwipeEnabled() = false
- }
-
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = controller.currentUserId
@@ -89,6 +64,10 @@
}
}
+ override fun onBackPressed() {
+ finish()
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.controls_management)
@@ -99,21 +78,27 @@
val app = intent.getCharSequenceExtra(EXTRA_APP)
component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
- errorText = requireViewById(R.id.error_message)
+ statusText = requireViewById(R.id.status_message)
- setUpRecyclerViews()
+ setUpRecyclerView()
requireViewById<TextView>(R.id.title).text = app?.let { it }
?: resources.getText(R.string.controls_favorite_default_title)
requireViewById<TextView>(R.id.subtitle).text =
resources.getText(R.string.controls_favorite_subtitle)
+ requireViewById<Button>(R.id.other_apps).apply {
+ visibility = View.VISIBLE
+ setOnClickListener {
+ this@ControlsFavoritingActivity.onBackPressed()
+ }
+ }
+
requireViewById<Button>(R.id.done).setOnClickListener {
if (component == null) return@setOnClickListener
- val favoritesForStorage = currentModel?.favorites?.map {
- with(it.controlStatus.control) {
- ControlInfo(component!!, controlId, title, deviceType)
- }
+ val favoritesForStorage = model?.favorites?.map {
+ it.componentName = component!!
+ it.build()
}
if (favoritesForStorage != null) {
controller.replaceFavoritesForComponent(component!!, favoritesForStorage)
@@ -122,20 +107,22 @@
}
component?.let {
+ statusText.text = resources.getText(com.android.internal.R.string.loading)
controller.loadForComponent(it, Consumer { data ->
val allControls = data.allControls
val favoriteKeys = data.favoritesIds
val error = data.errorOnLoad
executor.execute {
- val favoriteModel = FavoriteModel(
- allControls,
- favoriteKeys,
- allAdapter = adapterAll,
- favoritesAdapter = adapterFavorites)
- adapterAll.changeFavoritesModel(favoriteModel)
- adapterFavorites.changeFavoritesModel(favoriteModel)
- currentModel = favoriteModel
- errorText.visibility = if (error) View.VISIBLE else View.GONE
+ val emptyZoneString = resources.getText(
+ R.string.controls_favorite_other_zone_header)
+ val model = AllModel(allControls, favoriteKeys, emptyZoneString)
+ adapterAll.changeModel(model)
+ this.model = model
+ if (error) {
+ statusText.text = resources.getText(R.string.controls_favorite_load_error)
+ } else {
+ statusText.visibility = View.GONE
+ }
}
})
}
@@ -143,7 +130,7 @@
currentUserTracker.startTracking()
}
- private fun setUpRecyclerViews() {
+ private fun setUpRecyclerView() {
val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin)
val itemDecorator = MarginItemDecorator(margin, margin)
val layoutInflater = LayoutInflater.from(applicationContext)
@@ -156,14 +143,6 @@
}
addItemDecoration(itemDecorator)
}
-
- adapterFavorites = ControlAdapter(layoutInflater, true)
- recyclerViewFavorites = requireViewById<RecyclerView>(R.id.listFavorites).apply {
- layoutManager = GridLayoutManager(applicationContext, 2)
- adapter = adapterFavorites
- addItemDecoration(itemDecorator)
- }
- ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerViewFavorites)
}
override fun onDestroy() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 882382c..53f3019 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.management
+import android.app.ActivityManager
import android.content.ComponentName
import android.content.Context
import android.content.pm.ServiceInfo
@@ -72,7 +73,7 @@
private var availableServices = emptyList<ServiceInfo>()
- override var currentUserId = context.userId
+ override var currentUserId = ActivityManager.getCurrentUser()
private set
private val serviceListingCallback = ServiceListing.Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
new file mode 100644
index 0000000..a995a2e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.controls.management
+
+import com.android.systemui.controls.ControlStatus
+import com.android.systemui.controls.controller.ControlInfo
+
+/**
+ * Model for using with [ControlAdapter].
+ *
+ * Implementations of this interface provide different views of the controls to show.
+ */
+interface ControlsModel {
+
+ /**
+ * List of favorites (builders) in order.
+ *
+ * This should be obtained prior to storing the favorites using
+ * [ControlsController.replaceFavoritesForComponent].
+ */
+ val favorites: List<ControlInfo.Builder>
+
+ /**
+ * List of all the elements to display by the corresponding [RecyclerView].
+ */
+ val elements: List<ElementWrapper>
+
+ /**
+ * Change the favorite status of a particular control.
+ */
+ fun changeFavoriteStatus(controlId: String, favorite: Boolean) {}
+
+ /**
+ * Move an item (in elements) from one position to another.
+ */
+ fun onMoveItem(from: Int, to: Int) {}
+}
+
+/**
+ * Wrapper classes for the different types of elements shown in the [RecyclerView]s in
+ * [ControlAdapter].
+ */
+sealed class ElementWrapper
+data class ZoneNameWrapper(val zoneName: CharSequence) : ElementWrapper()
+data class ControlWrapper(val controlStatus: ControlStatus) : ElementWrapper()
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index ad4bdef..098caf6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -21,6 +21,7 @@
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewStub
+import android.widget.Button
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@@ -86,6 +87,10 @@
requireViewById<TextView>(R.id.subtitle).text =
resources.getText(R.string.controls_providers_subtitle)
+ requireViewById<Button>(R.id.done).setOnClickListener {
+ this@ControlsProviderSelectorActivity.finishAffinity()
+ }
+
currentUserTracker.startTracking()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt
index 6bade0a..5c51e3d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt
@@ -142,12 +142,4 @@
else if (p0 != null && p1 == null) return 1
return p0.toString().compareTo(p1.toString())
}
-}
-
-/**
- * Wrapper classes for the different types of elements shown in the [RecyclerView]s in
- * [ControlsFavoritingActivity].
- */
-sealed class ElementWrapper
-data class ZoneNameWrapper(val zoneName: CharSequence) : ElementWrapper()
-data class ControlWrapper(val controlStatus: ControlStatus) : ElementWrapper()
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 7c0033c..024378d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -20,6 +20,7 @@
import android.app.INotificationManager;
import android.content.Context;
+import android.content.SharedPreferences;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
@@ -34,6 +35,7 @@
import com.android.internal.util.NotificationMessagingUtil;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.Prefs;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
@@ -79,6 +81,13 @@
/** */
@Provides
+ @Main
+ public SharedPreferences provideSharePreferences(Context context) {
+ return Prefs.get(context);
+ }
+
+ /** */
+ @Provides
public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) {
return new AmbientDisplayConfiguration(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 3aa14a3..352ee33 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -209,6 +209,7 @@
@Provides
@Singleton
+ @Nullable
static TelecomManager provideTelecomManager(Context context) {
return context.getSystemService(TelecomManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 9203e6d..24f505d 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -25,7 +25,6 @@
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.IActivityManager;
-import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.app.WallpaperManager;
@@ -93,6 +92,7 @@
import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.controls.management.ControlsListingController;
import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -152,7 +152,7 @@
private final IDreamManager mDreamManager;
private final DevicePolicyManager mDevicePolicyManager;
private final LockPatternUtils mLockPatternUtils;
- private final KeyguardManager mKeyguardManager;
+ private final KeyguardStateController mKeyguardStateController;
private final BroadcastDispatcher mBroadcastDispatcher;
private final ContentResolver mContentResolver;
private final Resources mResources;
@@ -190,6 +190,8 @@
private ControlsUiController mControlsUiController;
private final IWindowManager mIWindowManager;
private final Executor mBackgroundExecutor;
+ private final ControlsListingController mControlsListingController;
+ private boolean mAnyControlsProviders = false;
/**
* @param context everything needs a context :(
@@ -198,25 +200,26 @@
public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs,
AudioManager audioManager, IDreamManager iDreamManager,
DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils,
- KeyguardManager keyguardManager, BroadcastDispatcher broadcastDispatcher,
+ BroadcastDispatcher broadcastDispatcher,
ConnectivityManager connectivityManager, TelephonyManager telephonyManager,
ContentResolver contentResolver, @Nullable Vibrator vibrator, @Main Resources resources,
ConfigurationController configurationController, ActivityStarter activityStarter,
KeyguardStateController keyguardStateController, UserManager userManager,
TrustManager trustManager, IActivityManager iActivityManager,
- TelecomManager telecomManager, MetricsLogger metricsLogger,
+ @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
BlurUtils blurUtils, SysuiColorExtractor colorExtractor,
IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
ControlsUiController controlsUiController, IWindowManager iWindowManager,
- @Background Executor backgroundExecutor) {
+ @Background Executor backgroundExecutor,
+ ControlsListingController controlsListingController) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
mDreamManager = iDreamManager;
mDevicePolicyManager = devicePolicyManager;
mLockPatternUtils = lockPatternUtils;
- mKeyguardManager = keyguardManager;
+ mKeyguardStateController = keyguardStateController;
mBroadcastDispatcher = broadcastDispatcher;
mContentResolver = contentResolver;
mResources = resources;
@@ -233,6 +236,7 @@
mControlsUiController = controlsUiController;
mIWindowManager = iWindowManager;
mBackgroundExecutor = backgroundExecutor;
+ mControlsListingController = controlsListingController;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -269,6 +273,8 @@
}
}
});
+
+ mControlsListingController.addCallback(list -> mAnyControlsProviders = !list.isEmpty());
}
/**
@@ -427,7 +433,7 @@
.startPendingIntentDismissingKeyguard(intent);
}
},
- mKeyguardManager.isDeviceLocked())
+ !mKeyguardStateController.isUnlocked())
: null;
ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController,
@@ -444,12 +450,12 @@
}
private boolean shouldDisplayLockdown() {
- int userId = getCurrentUser().id;
// Lockdown is meaningless without a place to go.
- if (!mKeyguardManager.isDeviceSecure(userId)) {
+ if (!mKeyguardStateController.isMethodSecure()) {
return false;
}
+ int userId = getCurrentUser().id;
// Only show the lockdown button if the device isn't locked down (for whatever reason).
int state = mLockPatternUtils.getStrongAuthForUser(userId);
return (state == STRONG_AUTH_NOT_REQUIRED
@@ -562,13 +568,16 @@
@Override
public void onPress() {
mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU);
- Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(null /* number */);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
- EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ if (mTelecomManager != null) {
+ Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(
+ null /* number */);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
+ EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU);
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
}
}
@@ -1914,7 +1923,8 @@
}
private boolean shouldShowControls() {
- return !mKeyguardManager.isDeviceLocked()
- && mControlsUiController.getAvailable();
+ return mKeyguardStateController.isUnlocked()
+ && mControlsUiController.getAvailable()
+ && mAnyControlsProviders;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d688f0a..c129035 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -96,6 +96,7 @@
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.InjectionInflationController;
import java.io.FileDescriptor;
@@ -378,6 +379,17 @@
private IKeyguardDrawnCallback mDrawnCallback;
private CharSequence mCustomMessage;
+ private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (properties.getKeyset().contains(NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN)) {
+ mShowHomeOverLockscreen = properties.getBoolean(
+ NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN, true /* defaultValue */);
+ }
+ }
+ };
+
KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
@Override
@@ -692,6 +704,8 @@
}
};
+ private DeviceConfigProxy mDeviceConfig;
+
/**
* Injected constructor. See {@link KeyguardModule}.
*/
@@ -705,7 +719,8 @@
DismissCallbackRegistry dismissCallbackRegistry,
KeyguardUpdateMonitor keyguardUpdateMonitor, DumpManager dumpManager,
@UiBackground Executor uiBgExecutor, PowerManager powerManager,
- TrustManager trustManager) {
+ TrustManager trustManager,
+ DeviceConfigProxy deviceConfig) {
super(context);
mFalsingManager = falsingManager;
mLockPatternUtils = lockPatternUtils;
@@ -718,20 +733,15 @@
mPM = powerManager;
mTrustManager = trustManager;
dumpManager.registerDumpable(getClass().getName(), this);
- mShowHomeOverLockscreen = DeviceConfig.getBoolean(
+ mDeviceConfig = deviceConfig;
+ mShowHomeOverLockscreen = mDeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
/* defaultValue = */ true);
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post,
- new DeviceConfig.OnPropertiesChangedListener() {
- @Override
- public void onPropertiesChanged(DeviceConfig.Properties properties) {
- if (properties.getKeyset().contains(NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN)) {
- mShowHomeOverLockscreen = properties.getBoolean(
- NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN, true /* defaultValue */);
- }
- }
- });
+ mDeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ mHandler::post,
+ mOnPropertiesChangedListener);
}
public void userActivity() {
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 d7af360..367f464 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.util.DeviceConfigProxy;
import java.util.concurrent.Executor;
@@ -62,7 +63,8 @@
DumpManager dumpManager,
PowerManager powerManager,
TrustManager trustManager,
- @UiBackground Executor uiBgExecutor) {
+ @UiBackground Executor uiBgExecutor,
+ DeviceConfigProxy deviceConfig) {
return new KeyguardViewMediator(
context,
falsingManager,
@@ -75,6 +77,7 @@
dumpManager,
uiBgExecutor,
powerManager,
- trustManager);
+ trustManager,
+ deviceConfig);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
index adee7f2..38744fe 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
@@ -18,6 +18,8 @@
import android.content.res.Configuration;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+
import java.io.PrintWriter;
@@ -27,5 +29,7 @@
default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {}
void onConfigurationChanged(Configuration newConfig);
default void setShelfHeight(boolean visible, int height) {}
+ default void setPinnedStackAnimationType(int animationType) {}
+ default void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {}
default void dump(PrintWriter pw) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
new file mode 100644
index 0000000..b5fd406
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -0,0 +1,317 @@
+/*
+ * 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.pip;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.annotation.MainThread;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Controller class of PiP animations (both from and to PiP mode).
+ */
+public class PipAnimationController {
+ private static final float FRACTION_START = 0f;
+ private static final float FRACTION_END = 1f;
+
+ public static final int DURATION_NONE = 0;
+ public static final int DURATION_DEFAULT_MS = 425;
+ public static final int ANIM_TYPE_BOUNDS = 0;
+ public static final int ANIM_TYPE_ALPHA = 1;
+
+ @IntDef(prefix = { "ANIM_TYPE_" }, value = {
+ ANIM_TYPE_BOUNDS,
+ ANIM_TYPE_ALPHA
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AnimationType {}
+
+ private final Interpolator mFastOutSlowInInterpolator;
+
+ private PipTransitionAnimator mCurrentAnimator;
+
+ PipAnimationController(Context context) {
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ }
+
+ @MainThread
+ PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip,
+ Rect destinationBounds, float alphaStart, float alphaEnd) {
+ if (mCurrentAnimator == null) {
+ mCurrentAnimator = setupPipTransitionAnimator(
+ PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip,
+ destinationBounds, alphaStart, alphaEnd));
+ } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
+ && mCurrentAnimator.isRunning()) {
+ mCurrentAnimator.updateEndValue(alphaEnd);
+ } else {
+ mCurrentAnimator.cancel();
+ mCurrentAnimator = setupPipTransitionAnimator(
+ PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip,
+ destinationBounds, alphaStart, alphaEnd));
+ }
+ return mCurrentAnimator;
+ }
+
+ @MainThread
+ PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip,
+ Rect startBounds, Rect endBounds) {
+ if (mCurrentAnimator == null) {
+ mCurrentAnimator = setupPipTransitionAnimator(
+ PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds));
+ } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS
+ && mCurrentAnimator.isRunning()) {
+ mCurrentAnimator.setDestinationBounds(endBounds);
+ // construct new Rect instances in case they are recycled
+ mCurrentAnimator.updateEndValue(new Rect(endBounds));
+ } else {
+ mCurrentAnimator.cancel();
+ mCurrentAnimator = setupPipTransitionAnimator(
+ PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds));
+ }
+ return mCurrentAnimator;
+ }
+
+ private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
+ animator.setInterpolator(mFastOutSlowInInterpolator);
+ animator.setFloatValues(FRACTION_START, FRACTION_END);
+ return animator;
+ }
+
+ /**
+ * Additional callback interface for PiP animation
+ */
+ public static class PipAnimationCallback {
+ /**
+ * Called when PiP animation is started.
+ */
+ public void onPipAnimationStart(IWindowContainer wc, PipTransitionAnimator animator) {}
+
+ /**
+ * Called when PiP animation is ended.
+ */
+ public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx,
+ PipTransitionAnimator animator) {}
+
+ /**
+ * Called when PiP animation is cancelled.
+ */
+ public void onPipAnimationCancel(IWindowContainer wc, PipTransitionAnimator animator) {}
+ }
+
+ /**
+ * Animator for PiP transition animation which supports both alpha and bounds animation.
+ * @param <T> Type of property to animate, either alpha (float) or bounds (Rect)
+ */
+ public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
+ ValueAnimator.AnimatorUpdateListener,
+ ValueAnimator.AnimatorListener {
+ private final IWindowContainer mWindowContainer;
+ private final boolean mScheduleFinishPip;
+ private final SurfaceControl mLeash;
+ private final @AnimationType int mAnimationType;
+ private final Rect mDestinationBounds = new Rect();
+
+ private T mStartValue;
+ private T mEndValue;
+ private T mCurrentValue;
+ private PipAnimationCallback mPipAnimationCallback;
+ private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory;
+
+ private PipTransitionAnimator(IWindowContainer wc, boolean scheduleFinishPip,
+ @AnimationType int animationType, Rect destinationBounds,
+ T startValue, T endValue) {
+ mWindowContainer = wc;
+ mScheduleFinishPip = scheduleFinishPip;
+ try {
+ mLeash = wc.getLeash();
+ mAnimationType = animationType;
+ mDestinationBounds.set(destinationBounds);
+ mStartValue = startValue;
+ mEndValue = endValue;
+ addListener(this);
+ addUpdateListener(this);
+ mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mCurrentValue = mStartValue;
+ applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), FRACTION_START);
+ if (mPipAnimationCallback != null) {
+ mPipAnimationCallback.onPipAnimationStart(mWindowContainer, this);
+ }
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(),
+ animation.getAnimatedFraction());
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentValue = mEndValue;
+ final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ applySurfaceControlTransaction(mLeash, tx, FRACTION_END);
+ if (mPipAnimationCallback != null) {
+ mPipAnimationCallback.onPipAnimationEnd(mWindowContainer, tx, this);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (mPipAnimationCallback != null) {
+ mPipAnimationCallback.onPipAnimationCancel(mWindowContainer, this);
+ }
+ }
+
+ @Override public void onAnimationRepeat(Animator animation) {}
+
+ @AnimationType int getAnimationType() {
+ return mAnimationType;
+ }
+
+ PipTransitionAnimator<T> setPipAnimationCallback(PipAnimationCallback callback) {
+ mPipAnimationCallback = callback;
+ return this;
+ }
+
+ boolean shouldScheduleFinishPip() {
+ return mScheduleFinishPip;
+ }
+
+ T getStartValue() {
+ return mStartValue;
+ }
+
+ T getEndValue() {
+ return mEndValue;
+ }
+
+ Rect getDestinationBounds() {
+ return mDestinationBounds;
+ }
+
+ void setDestinationBounds(Rect destinationBounds) {
+ mDestinationBounds.set(destinationBounds);
+ }
+
+ void setCurrentValue(T value) {
+ mCurrentValue = value;
+ }
+
+ /**
+ * Updates the {@link #mEndValue}.
+ *
+ * NOTE: Do not forget to call {@link #setDestinationBounds(Rect)} for bounds animation.
+ * This is typically used when we receive a shelf height adjustment during the bounds
+ * animation. In which case we can update the end bounds and keep the existing animation
+ * running instead of cancelling it.
+ */
+ void updateEndValue(T endValue) {
+ mEndValue = endValue;
+ mStartValue = mCurrentValue;
+ }
+
+ SurfaceControl.Transaction newSurfaceControlTransaction() {
+ return mSurfaceControlTransactionFactory.getTransaction();
+ }
+
+ @VisibleForTesting
+ void setSurfaceControlTransactionFactory(SurfaceControlTransactionFactory factory) {
+ mSurfaceControlTransactionFactory = factory;
+ }
+
+ abstract void applySurfaceControlTransaction(SurfaceControl leash,
+ SurfaceControl.Transaction tx, float fraction);
+
+ static PipTransitionAnimator<Float> ofAlpha(IWindowContainer wc, boolean scheduleFinishPip,
+ Rect destinationBounds, float startValue, float endValue) {
+ return new PipTransitionAnimator<Float>(wc, scheduleFinishPip, ANIM_TYPE_ALPHA,
+ destinationBounds, startValue, endValue) {
+ @Override
+ void applySurfaceControlTransaction(SurfaceControl leash,
+ SurfaceControl.Transaction tx, float fraction) {
+ final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction;
+ setCurrentValue(alpha);
+ tx.setAlpha(leash, alpha);
+ if (Float.compare(fraction, FRACTION_START) == 0) {
+ // Ensure the start condition
+ final Rect bounds = getDestinationBounds();
+ tx.setPosition(leash, bounds.left, bounds.top)
+ .setWindowCrop(leash, bounds.width(), bounds.height());
+ }
+ tx.apply();
+ }
+ };
+ }
+
+ static PipTransitionAnimator<Rect> ofBounds(IWindowContainer wc, boolean scheduleFinishPip,
+ Rect startValue, Rect endValue) {
+ // construct new Rect instances in case they are recycled
+ return new PipTransitionAnimator<Rect>(wc, scheduleFinishPip, ANIM_TYPE_BOUNDS,
+ endValue, new Rect(startValue), new Rect(endValue)) {
+ private final Rect mTmpRect = new Rect();
+
+ private int getCastedFractionValue(float start, float end, float fraction) {
+ return (int) (start * (1 - fraction) + end * fraction + .5f);
+ }
+
+ @Override
+ void applySurfaceControlTransaction(SurfaceControl leash,
+ SurfaceControl.Transaction tx, float fraction) {
+ final Rect start = getStartValue();
+ final Rect end = getEndValue();
+ mTmpRect.set(
+ getCastedFractionValue(start.left, end.left, fraction),
+ getCastedFractionValue(start.top, end.top, fraction),
+ getCastedFractionValue(start.right, end.right, fraction),
+ getCastedFractionValue(start.bottom, end.bottom, fraction));
+ setCurrentValue(mTmpRect);
+ tx.setPosition(leash, mTmpRect.left, mTmpRect.top)
+ .setWindowCrop(leash, mTmpRect.width(), mTmpRect.height());
+ if (Float.compare(fraction, FRACTION_START) == 0) {
+ // Ensure the start condition
+ tx.setAlpha(leash, 1f);
+ }
+ tx.apply();
+ }
+ };
+ }
+ }
+
+ interface SurfaceControlTransactionFactory {
+ SurfaceControl.Transaction getTransaction();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 41b3130..8c3ccaa 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -36,7 +36,6 @@
import android.util.TypedValue;
import android.view.DisplayInfo;
import android.view.Gravity;
-import android.view.IPinnedStackController;
import android.view.IWindowManager;
import android.view.WindowContainerTransaction;
import android.view.WindowManagerGlobal;
@@ -56,9 +55,7 @@
private final IWindowManager mWindowManager;
private final PipSnapAlgorithm mSnapAlgorithm;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
- private final Rect mStableInsets = new Rect();
private final Rect mTmpInsets = new Rect();
- private final Point mTmpDisplaySize = new Point();
/**
* Tracks the destination bounds, used for any following
@@ -66,7 +63,6 @@
*/
private final Rect mLastDestinationBounds = new Rect();
- private IPinnedStackController mPinnedStackController;
private ComponentName mLastPipComponentName;
private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
private Size mReentrySize = null;
@@ -80,7 +76,6 @@
private Point mScreenEdgeInsets;
private int mCurrentMinSize;
- private boolean mIsMinimized;
private boolean mIsImeShowing;
private int mImeHeight;
private boolean mIsShelfShowing;
@@ -123,10 +118,6 @@
com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
}
- public void setPinnedStackController(IPinnedStackController controller) {
- mPinnedStackController = controller;
- }
-
public void setMinEdgeSize(int minEdgeSize) {
mCurrentMinSize = minEdgeSize;
}
@@ -155,14 +146,6 @@
}
/**
- * Responds to IPinnedStackListener on minimized state change.
- */
- public void onMinimizedStateChanged(boolean minimized) {
- mIsMinimized = minimized;
- mSnapAlgorithm.setMinimized(minimized);
- }
-
- /**
* Responds to IPinnedStackListener on movement bounds change.
* Note that both inset and normal bounds will be calculated here rather than in the caller.
*/
@@ -238,9 +221,9 @@
}
/**
- * Responds to IPinnedStackListener on preparing the pinned stack animation.
+ * @return {@link Rect} of the destination PiP window bounds.
*/
- public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
+ Rect getDestinationBounds(float aspectRatio, Rect bounds) {
final Rect destinationBounds;
final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
if (bounds == null) {
@@ -253,17 +236,16 @@
false /* useCurrentMinEdgeSize */);
}
if (destinationBounds.equals(bounds)) {
- return;
+ return bounds;
}
mAspectRatio = aspectRatio;
onResetReentryBoundsUnchecked();
- try {
- mPinnedStackController.startAnimation(destinationBounds, sourceRectHint,
- -1 /* animationDuration */);
- mLastDestinationBounds.set(destinationBounds);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to start PiP animation from SysUI", e);
- }
+ mLastDestinationBounds.set(destinationBounds);
+ return destinationBounds;
+ }
+
+ float getDefaultAspectRatio() {
+ return mDefaultAspectRatio;
}
/**
@@ -307,18 +289,10 @@
false /* adjustForIme */);
mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
snapFraction);
- if (mIsMinimized) {
- applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds);
- }
- try {
- outBounds.set(postChangeStackBounds);
- mLastDestinationBounds.set(outBounds);
- mPinnedStackController.resetBoundsAnimation(outBounds);
- t.setBounds(pinnedStackInfo.stackToken, outBounds);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to resize PiP on display rotation", e);
- }
+ outBounds.set(postChangeStackBounds);
+ mLastDestinationBounds.set(outBounds);
+ t.setBounds(pinnedStackInfo.stackToken, outBounds);
return true;
}
@@ -370,9 +344,6 @@
final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
- if (mIsMinimized) {
- applyMinimizedOffset(stackBounds, getMovementBounds(stackBounds));
- }
}
/**
@@ -436,20 +407,6 @@
}
/**
- * Applies the minimized offsets to the given stack bounds.
- */
- private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) {
- mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
- try {
- mWindowManager.getStableInsets(mContext.getDisplayId(), mStableInsets);
- mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize,
- mStableInsets);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get stable insets from WM", e);
- }
- }
-
- /**
* @return the default snap fraction to apply instead of the default gravity when calculating
* the default stack bounds when first entering PiP.
*/
@@ -486,7 +443,6 @@
pw.println(innerPrefix + "mMaxAspectRatio=" + mMaxAspectRatio);
pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
pw.println(innerPrefix + "mDefaultStackGravity=" + mDefaultStackGravity);
- pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java
index f3e707c..6b89718 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java
@@ -63,14 +63,9 @@
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
- private final int mMinimizedVisibleSize;
- private boolean mIsMinimized;
-
public PipSnapAlgorithm(Context context) {
Resources res = context.getResources();
mContext = context;
- mMinimizedVisibleSize = res.getDimensionPixelSize(
- com.android.internal.R.dimen.pip_minimized_visible_size);
mDefaultSizePercent = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent);
mMaxAspectRatioForMinSize = res.getFloat(
@@ -92,13 +87,6 @@
}
/**
- * Sets the PIP's minimized state.
- */
- public void setMinimized(boolean isMinimized) {
- mIsMinimized = isMinimized;
- }
-
- /**
* @return the closest absolute snap stack bounds for the given {@param stackBounds} moving at
* the given {@param velocityX} and {@param velocityY}. The {@param movementBounds} should be
* those for the given {@param stackBounds}.
@@ -235,20 +223,6 @@
}
/**
- * Applies the offset to the {@param stackBounds} to adjust it to a minimized state.
- */
- public void applyMinimizedOffset(Rect stackBounds, Rect movementBounds, Point displaySize,
- Rect stableInsets) {
- if (stackBounds.left <= movementBounds.centerX()) {
- stackBounds.offsetTo(stableInsets.left + mMinimizedVisibleSize - stackBounds.width(),
- stackBounds.top);
- } else {
- stackBounds.offsetTo(displaySize.x - stableInsets.right - mMinimizedVisibleSize,
- stackBounds.top);
- }
- }
-
- /**
* @return returns a fraction that describes where along the {@param movementBounds} the
* {@param stackBounds} are. If the {@param stackBounds} are not currently on the
* {@param movementBounds} exactly, then they will be snapped to the movement bounds.
@@ -402,16 +376,11 @@
* the new bounds out to {@param boundsOut}.
*/
private void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut) {
- // If the stackBounds are minimized, then it should only be snapped back horizontally
final int boundedLeft = Math.max(movementBounds.left, Math.min(movementBounds.right,
stackBounds.left));
final int boundedTop = Math.max(movementBounds.top, Math.min(movementBounds.bottom,
stackBounds.top));
boundsOut.set(stackBounds);
- if (mIsMinimized) {
- boundsOut.offsetTo(boundedLeft, boundedTop);
- return;
- }
// Otherwise, just find the closest edge
final int fromLeft = Math.abs(stackBounds.left - movementBounds.left);
@@ -479,7 +448,5 @@
pw.println(prefix + PipSnapAlgorithm.class.getSimpleName());
pw.println(innerPrefix + "mSnapMode=" + mSnapMode);
pw.println(innerPrefix + "mOrientation=" + mOrientation);
- pw.println(innerPrefix + "mMinimizedVisibleSize=" + mMinimizedVisibleSize);
- pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
new file mode 100644
index 0000000..1555153
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -0,0 +1,291 @@
+/*
+ * 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.pip;
+
+import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
+import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
+import static com.android.systemui.pip.PipAnimationController.DURATION_NONE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.ITaskOrganizerController;
+import android.app.PictureInPictureParams;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.DisplayInfo;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+import android.view.WindowContainerTransaction;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Manages PiP tasks such as resize and offset.
+ *
+ * This class listens on {@link ITaskOrganizer} callbacks for windowing mode change
+ * both to and from PiP and issues corresponding animation if applicable.
+ * Normally, we apply series of {@link SurfaceControl.Transaction} when the animator is running
+ * and files a final {@link WindowContainerTransaction} at the end of the transition.
+ *
+ * This class is also responsible for general resize/offset PiP operations within SysUI component,
+ * see also {@link com.android.systemui.pip.phone.PipMotionHelper}.
+ */
+public class PipTaskOrganizer extends ITaskOrganizer.Stub {
+ private static final String TAG = PipTaskOrganizer.class.getSimpleName();
+
+ private final Handler mMainHandler;
+ private final ITaskOrganizerController mTaskOrganizerController;
+ private final PipBoundsHandler mPipBoundsHandler;
+ private final PipAnimationController mPipAnimationController;
+ private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
+ private final Rect mDisplayBounds = new Rect();
+ private final Rect mLastReportedBounds = new Rect();
+
+ private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
+ new PipAnimationController.PipAnimationCallback() {
+ @Override
+ public void onPipAnimationStart(IWindowContainer wc,
+ PipAnimationController.PipTransitionAnimator animator) {
+ mMainHandler.post(() -> {
+ for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+ callback.onPipTransitionStarted();
+ }
+ });
+ }
+
+ @Override
+ public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx,
+ PipAnimationController.PipTransitionAnimator animator) {
+ mMainHandler.post(() -> {
+ for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+ callback.onPipTransitionFinished();
+ }
+ });
+ final Rect destinationBounds = animator.getDestinationBounds();
+ mLastReportedBounds.set(destinationBounds);
+ try {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (animator.shouldScheduleFinishPip()) {
+ wct.scheduleFinishEnterPip(wc, destinationBounds);
+ } else {
+ wct.setBounds(wc, destinationBounds);
+ }
+ wct.setBoundsChangeTransaction(wc, tx);
+ mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to apply container transaction", e);
+ }
+ }
+
+ @Override
+ public void onPipAnimationCancel(IWindowContainer wc,
+ PipAnimationController.PipTransitionAnimator animator) {
+ mMainHandler.post(() -> {
+ for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+ callback.onPipTransitionCanceled();
+ }
+ });
+ }
+ };
+
+ private ActivityManager.RunningTaskInfo mTaskInfo;
+ private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+
+ public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler) {
+ mMainHandler = new Handler(Looper.getMainLooper());
+ mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController();
+ mPipBoundsHandler = boundsHandler;
+ mPipAnimationController = new PipAnimationController(context);
+ }
+
+ /**
+ * Resize the PiP window, animate if the given duration is not {@link #DURATION_NONE}
+ */
+ public void resizePinnedStack(Rect destinationBounds, int durationMs) {
+ Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
+ resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip */,
+ mLastReportedBounds, destinationBounds, durationMs);
+ }
+
+ /**
+ * Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE}
+ */
+ public void offsetPinnedStack(Rect originalBounds, int xOffset, int yOffset, int durationMs) {
+ if (mTaskInfo == null) {
+ Log.w(TAG, "mTaskInfo is not set");
+ return;
+ }
+ final Rect destinationBounds = new Rect(originalBounds);
+ destinationBounds.offset(xOffset, yOffset);
+ resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip*/,
+ originalBounds, destinationBounds, durationMs);
+ }
+
+ /**
+ * Registers {@link PipTransitionCallback} to receive transition callbacks.
+ */
+ public void registerPipTransitionCallback(PipTransitionCallback callback) {
+ mPipTransitionCallbacks.add(callback);
+ }
+
+ /**
+ * Sets the preferred animation type for one time.
+ * This is typically used to set the animation type to {@link #ANIM_TYPE_ALPHA}.
+ */
+ public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
+ mOneShotAnimationType = animationType;
+ }
+
+ /**
+ * Updates the display dimension with given {@link DisplayInfo}
+ */
+ public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+ mDisplayBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+ }
+
+ /**
+ * Callback to issue the final {@link WindowContainerTransaction} on end of movements.
+ * @param destinationBounds the final bounds.
+ */
+ public void onMotionMovementEnd(Rect destinationBounds) {
+ try {
+ mLastReportedBounds.set(destinationBounds);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setBounds(mTaskInfo.token, destinationBounds);
+ mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to apply window container transaction", e);
+ }
+ }
+
+ @Override
+ public void taskAppeared(ActivityManager.RunningTaskInfo info) {
+ Objects.requireNonNull(info, "Requires RunningTaskInfo");
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+ getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */);
+ Objects.requireNonNull(destinationBounds, "Missing destination bounds");
+ mTaskInfo = info;
+ if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
+ final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
+ resizePinnedStackInternal(mTaskInfo.token, true /* scheduleFinishPip */,
+ currentBounds, destinationBounds,
+ PipAnimationController.DURATION_DEFAULT_MS);
+ } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ mMainHandler.post(() -> mPipAnimationController
+ .getAnimator(mTaskInfo.token, true /* scheduleFinishPip */,
+ destinationBounds, 0f, 1f)
+ .setPipAnimationCallback(mPipAnimationCallback)
+ .setDuration(PipAnimationController.DURATION_DEFAULT_MS)
+ .start());
+ mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+ } else {
+ throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType);
+ }
+ }
+
+ @Override
+ public void taskVanished(IWindowContainer token) {
+ Objects.requireNonNull(token, "Requires valid IWindowContainer");
+ if (token.asBinder() != mTaskInfo.token.asBinder()) {
+ Log.wtf(TAG, "Unrecognized token: " + token);
+ return;
+ }
+ resizePinnedStackInternal(token, false /* scheduleFinishPip */,
+ mLastReportedBounds, mDisplayBounds,
+ PipAnimationController.DURATION_DEFAULT_MS);
+ }
+
+ @Override
+ public void transactionReady(int id, SurfaceControl.Transaction t) {
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+ getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */);
+ Objects.requireNonNull(destinationBounds, "Missing destination bounds");
+ resizePinnedStack(destinationBounds, PipAnimationController.DURATION_DEFAULT_MS);
+ }
+
+ private void resizePinnedStackInternal(IWindowContainer wc, boolean scheduleFinishPip,
+ Rect currentBounds, Rect destinationBounds, int animationDurationMs) {
+ try {
+ // Could happen when dismissPip
+ if (wc == null || wc.getLeash() == null) {
+ Log.w(TAG, "Abort animation, invalid leash");
+ return;
+ }
+ final SurfaceControl leash = wc.getLeash();
+ if (animationDurationMs == DURATION_NONE) {
+ // Directly resize if no animation duration is set. When fling, wait for final
+ // callback to issue the proper WindowContainerTransaction with destination bounds.
+ new SurfaceControl.Transaction()
+ .setPosition(leash, destinationBounds.left, destinationBounds.top)
+ .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
+ .apply();
+ } else {
+ mMainHandler.post(() -> mPipAnimationController
+ .getAnimator(wc, scheduleFinishPip, currentBounds, destinationBounds)
+ .setPipAnimationCallback(mPipAnimationCallback)
+ .setDuration(animationDurationMs)
+ .start());
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Abort animation, invalid window container", e);
+ } catch (Exception e) {
+ Log.e(TAG, "Should not reach here, terrible thing happened", e);
+ }
+ }
+
+ private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
+ return params == null
+ ? mPipBoundsHandler.getDefaultAspectRatio()
+ : params.getAspectRatio();
+ }
+
+ /**
+ * Callback interface for PiP transitions (both from and to PiP mode)
+ */
+ public interface PipTransitionCallback {
+ /**
+ * Callback when the pip transition is started.
+ */
+ void onPipTransitionStarted();
+
+ /**
+ * Callback when the pip transition is finished.
+ */
+ void onPipTransitionFinished();
+
+ /**
+ * Callback when the pip transition is cancelled.
+ */
+ void onPipTransitionCanceled();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
index 599c845..4fb675e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -25,6 +25,7 @@
import android.os.UserManager;
import com.android.systemui.SystemUI;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.statusbar.CommandQueue;
import java.io.FileDescriptor;
@@ -98,6 +99,18 @@
mPipManager.setShelfHeight(visible, height);
}
+ public void setPinnedStackAnimationType(int animationType) {
+ if (mPipManager != null) {
+ mPipManager.setPinnedStackAnimationType(animationType);
+ }
+ }
+
+ public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
+ if (mPipManager != null) {
+ mPipManager.setPinnedStackAnimationListener(listener);
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mPipManager == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index cb94e28..7b96268 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -41,11 +41,14 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.pip.BasePipManager;
import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.wm.DisplayChangeController;
import com.android.systemui.wm.DisplayController;
@@ -59,12 +62,11 @@
* Manages the picture-in-picture (PIP) UI and states for Phones.
*/
@Singleton
-public class PipManager implements BasePipManager {
+public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback {
private static final String TAG = "PipManager";
private Context mContext;
private IActivityManager mActivityManager;
- private IActivityTaskManager mActivityTaskManager;
private Handler mHandler = new Handler();
private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener();
@@ -78,7 +80,9 @@
private PipMenuActivityController mMenuController;
private PipMediaController mMediaController;
private PipTouchHandler mTouchHandler;
+ private PipTaskOrganizer mPipTaskOrganizer;
private PipAppOpsListener mAppOpsListener;
+ private IPinnedStackAnimationListener mPinnedStackAnimationRecentsListener;
/**
* Handler for display rotation changes.
@@ -124,20 +128,6 @@
}
@Override
- public void onPinnedStackAnimationStarted() {
- // Disable touches while the animation is running
- mTouchHandler.setTouchEnabled(false);
- }
-
- @Override
- public void onPinnedStackAnimationEnded() {
- // Re-enable touches after the animation completes
- mTouchHandler.setTouchEnabled(true);
- mTouchHandler.onPinnedStackAnimationEnded();
- mMenuController.onPinnedStackAnimationEnded();
- }
-
- @Override
public void onPinnedActivityRestartAttempt(boolean clearedTask) {
mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */);
}
@@ -149,10 +139,7 @@
private class PipManagerPinnedStackListener extends PinnedStackListener {
@Override
public void onListenerRegistered(IPinnedStackController controller) {
- mHandler.post(() -> {
- mPipBoundsHandler.setPinnedStackController(controller);
- mTouchHandler.setPinnedStackController(controller);
- });
+ mHandler.post(() -> mTouchHandler.setPinnedStackController(controller));
}
@Override
@@ -164,18 +151,9 @@
}
@Override
- public void onMinimizedStateChanged(boolean isMinimized) {
- mHandler.post(() -> {
- mPipBoundsHandler.onMinimizedStateChanged(isMinimized);
- mTouchHandler.setMinimizedState(isMinimized, true /* fromController */);
- });
- }
-
- @Override
- public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
- boolean fromShelfAdjustment) {
+ public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {
mHandler.post(() -> updateMovementBounds(animatingBounds, fromImeAdjustment,
- fromShelfAdjustment));
+ false /* fromShelfAdjustment */));
}
@Override
@@ -207,7 +185,10 @@
@Override
public void onDisplayInfoChanged(DisplayInfo displayInfo) {
- mHandler.post(() -> mPipBoundsHandler.onDisplayInfoChanged(displayInfo));
+ mHandler.post(() -> {
+ mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
+ mPipTaskOrganizer.onDisplayInfoChanged(displayInfo);
+ });
}
@Override
@@ -219,22 +200,15 @@
public void onAspectRatioChanged(float aspectRatio) {
mHandler.post(() -> mPipBoundsHandler.onAspectRatioChanged(aspectRatio));
}
-
- @Override
- public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
- mHandler.post(() -> {
- mPipBoundsHandler.onPrepareAnimation(sourceRectHint, aspectRatio, bounds);
- });
- }
}
@Inject
public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
DisplayController displayController,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ DeviceConfigProxy deviceConfig) {
mContext = context;
mActivityManager = ActivityManager.getService();
- mActivityTaskManager = ActivityTaskManager.getService();
try {
WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
@@ -243,24 +217,29 @@
}
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService();
mPipBoundsHandler = new PipBoundsHandler(context);
+ mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler);
+ mPipTaskOrganizer.registerPipTransitionCallback(this);
mInputConsumerController = InputConsumerController.getPipInputConsumer();
mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher);
- mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
+ mMenuController = new PipMenuActivityController(context, mMediaController,
mInputConsumerController);
- mTouchHandler = new PipTouchHandler(context, mActivityManager, mActivityTaskManager,
- mMenuController, mInputConsumerController, mPipBoundsHandler,
- floatingContentCoordinator);
+ mTouchHandler = new PipTouchHandler(context, mActivityManager, activityTaskManager,
+ mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
+ floatingContentCoordinator, deviceConfig);
mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
mTouchHandler.getMotionHelper());
displayController.addDisplayChangingController(mRotationController);
- // If SystemUI restart, and it already existed a pinned stack,
- // register the pip input consumer to ensure touch can send to it.
try {
- ActivityManager.StackInfo stackInfo = mActivityTaskManager.getStackInfo(
+ ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(
+ mPipTaskOrganizer, WINDOWING_MODE_PINNED);
+ ActivityManager.StackInfo stackInfo = activityTaskManager.getStackInfo(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
if (stackInfo != null) {
+ // If SystemUI restart, and it already existed a pinned stack,
+ // register the pip input consumer to ensure touch can send to it.
mInputConsumerController.registerInputConsumer();
}
} catch (RemoteException e) {
@@ -320,9 +299,52 @@
});
}
+ @Override
+ public void setPinnedStackAnimationType(int animationType) {
+ mHandler.post(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType));
+ }
+
+ @Override
+ public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
+ mHandler.post(() -> mPinnedStackAnimationRecentsListener = listener);
+ }
+
+ @Override
+ public void onPipTransitionStarted() {
+ // Disable touches while the animation is running
+ mTouchHandler.setTouchEnabled(false);
+ if (mPinnedStackAnimationRecentsListener != null) {
+ try {
+ mPinnedStackAnimationRecentsListener.onPinnedStackAnimationStarted();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to callback recents", e);
+ }
+ }
+ }
+
+ @Override
+ public void onPipTransitionFinished() {
+ onPipTransitionFinishedOrCanceled();
+ }
+
+ @Override
+ public void onPipTransitionCanceled() {
+ onPipTransitionFinishedOrCanceled();
+ }
+
+ private void onPipTransitionFinishedOrCanceled() {
+ // Re-enable touches after the animation completes
+ mTouchHandler.setTouchEnabled(true);
+ mTouchHandler.onPinnedStackAnimationEnded();
+ mMenuController.onPinnedStackAnimationEnded();
+ }
+
private void updateMovementBounds(Rect animatingBounds, boolean fromImeAdjustment,
boolean fromShelfAdjustment) {
- // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
+ mPipTaskOrganizer.onDisplayInfoChanged(mTmpDisplayInfo);
+ // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler before
+ // passing to mTouchHandler, mTouchHandler would rely on the bounds calculated by
+ // mPipBoundsHandler with up-to-dated information
mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
animatingBounds, mTmpDisplayInfo);
mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index c7bfc06..d660b67 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -22,7 +22,6 @@
import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
-import android.app.IActivityManager;
import android.app.RemoteAction;
import android.content.Context;
import android.content.Intent;
@@ -68,11 +67,8 @@
public static final int MESSAGE_MENU_STATE_CHANGED = 100;
public static final int MESSAGE_EXPAND_PIP = 101;
- public static final int MESSAGE_MINIMIZE_PIP = 102;
public static final int MESSAGE_DISMISS_PIP = 103;
public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104;
- public static final int MESSAGE_REGISTER_INPUT_CONSUMER = 105;
- public static final int MESSAGE_UNREGISTER_INPUT_CONSUMER = 106;
public static final int MESSAGE_SHOW_MENU = 107;
public static final int MENU_STATE_NONE = 0;
@@ -100,11 +96,6 @@
void onPipExpand();
/**
- * Called when the PIP requested to be minimized.
- */
- void onPipMinimize();
-
- /**
* Called when the PIP requested to be dismissed.
*/
void onPipDismiss();
@@ -116,7 +107,6 @@
}
private Context mContext;
- private IActivityManager mActivityManager;
private PipMediaController mMediaController;
private InputConsumerController mInputConsumerController;
@@ -146,10 +136,6 @@
mListeners.forEach(l -> l.onPipExpand());
break;
}
- case MESSAGE_MINIMIZE_PIP: {
- mListeners.forEach(l -> l.onPipMinimize());
- break;
- }
case MESSAGE_DISMISS_PIP: {
mListeners.forEach(l -> l.onPipDismiss());
break;
@@ -194,10 +180,9 @@
}
};
- public PipMenuActivityController(Context context, IActivityManager activityManager,
+ public PipMenuActivityController(Context context,
PipMediaController mediaController, InputConsumerController inputConsumerController) {
mContext = context;
- mActivityManager = activityManager;
mMediaController = mediaController;
mInputConsumerController = inputConsumerController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index c6e2852..980d18b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -22,7 +22,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.StackInfo;
-import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.content.Context;
import android.graphics.Point;
@@ -39,7 +38,9 @@
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.os.SomeArgs;
+import com.android.systemui.pip.PipAnimationController;
import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.util.FloatingContentCoordinator;
@@ -65,8 +66,6 @@
/** Friction to use for PIP when it moves via physics fling animations. */
private static final float DEFAULT_FRICTION = 2f;
- // The fraction of the stack width that the user has to drag offscreen to minimize the PiP
- private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.3f;
// The fraction of the stack height that the user has to drag offscreen to dismiss the PiP
private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f;
@@ -74,10 +73,10 @@
private static final int MSG_RESIZE_ANIMATE = 2;
private static final int MSG_OFFSET_ANIMATE = 3;
- private Context mContext;
- private IActivityManager mActivityManager;
- private IActivityTaskManager mActivityTaskManager;
- private Handler mHandler;
+ private final Context mContext;
+ private final IActivityTaskManager mActivityTaskManager;
+ private final PipTaskOrganizer mPipTaskOrganizer;
+ private final Handler mHandler;
private PipMenuActivityController mMenuController;
private PipSnapAlgorithm mSnapAlgorithm;
@@ -139,14 +138,14 @@
new PhysicsAnimator.SpringConfig(
SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
- public PipMotionHelper(Context context, IActivityManager activityManager,
- IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
+ public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
+ PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
FloatingContentCoordinator floatingContentCoordinator) {
mContext = context;
mHandler = new Handler(ForegroundThread.get().getLooper(), this);
- mActivityManager = activityManager;
mActivityTaskManager = activityTaskManager;
+ mPipTaskOrganizer = pipTaskOrganizer;
mMenuController = menuController;
mSnapAlgorithm = snapAlgorithm;
mFlingAnimationUtils = flingAnimationUtils;
@@ -285,35 +284,6 @@
}
/**
- * @return the closest minimized PiP bounds.
- */
- Rect getClosestMinimizedBounds(Rect stackBounds) {
- Point displaySize = new Point();
- mContext.getDisplay().getRealSize(displaySize);
- Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mMovementBounds, stackBounds);
- mSnapAlgorithm.applyMinimizedOffset(toBounds, mMovementBounds, displaySize, mStableInsets);
- return toBounds;
- }
-
- /**
- * @return whether the PiP at the current bounds should be minimized.
- */
- boolean shouldMinimizePip() {
- Point displaySize = new Point();
- mContext.getDisplay().getRealSize(displaySize);
- if (mBounds.left < 0) {
- float offscreenFraction = (float) -mBounds.left / mBounds.width();
- return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION;
- } else if (mBounds.right > displaySize.x) {
- float offscreenFraction = (float) (mBounds.right - displaySize.x) /
- mBounds.width();
- return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION;
- } else {
- return false;
- }
- }
-
- /**
* @return whether the PiP at the current bounds should be dismissed.
*/
boolean shouldDismissPip() {
@@ -328,25 +298,6 @@
}
/**
- * Animates the PiP to the minimized state, slightly offscreen.
- */
- void animateToClosestMinimizedState(@Nullable Runnable updateAction) {
- final Rect toBounds = getClosestMinimizedBounds(mBounds);
-
- mAnimatedBounds.set(mBounds);
- mAnimatedBoundsPhysicsAnimator
- .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
- .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig);
-
- if (updateAction != null) {
- mAnimatedBoundsPhysicsAnimator.addUpdateListener(
- (target, values) -> updateAction.run());
- }
-
- startBoundsAnimation();
- }
-
- /**
* Flings the PiP to the closest snap target.
*/
void flingToSnapTarget(
@@ -436,8 +387,7 @@
* Animates the PiP from the expanded state to the normal state after the menu is hidden.
*/
void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction,
- Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized,
- boolean immediate) {
+ Rect normalMovementBounds, Rect currentMovementBounds, boolean immediate) {
if (savedSnapFraction < 0f) {
// If there are no saved snap fractions, then just use the current bounds
savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds),
@@ -445,10 +395,6 @@
}
mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
- if (minimized) {
- normalBounds = getClosestMinimizedBounds(normalBounds);
- }
-
if (immediate) {
movePip(normalBounds);
} else {
@@ -465,11 +411,9 @@
}
private void adjustAndAnimatePipOffset(Rect originalBounds, int offset, int duration) {
- if (offset == 0) {
- return;
- }
SomeArgs args = SomeArgs.obtain();
args.arg1 = originalBounds;
+ // offset would be zero if triggered from screen rotation.
args.argi1 = offset;
args.argi2 = duration;
mHandler.sendMessage(mHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
@@ -503,6 +447,7 @@
cancelAnimations();
mAnimatedBoundsPhysicsAnimator
+ .withEndActions(() -> mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds))
.addUpdateListener(mResizePipVsyncUpdateListener)
.start();
}
@@ -594,13 +539,6 @@
}
/**
- * @return the distance between points {@param p1} and {@param p2}.
- */
- private float distanceBetweenRectOffsets(Rect r1, Rect r2) {
- return PointF.length(r1.left - r2.left, r1.top - r2.top);
- }
-
- /**
* Handles messages to be processed on the background thread.
*/
public boolean handleMessage(Message msg) {
@@ -608,13 +546,8 @@
case MSG_RESIZE_IMMEDIATE: {
SomeArgs args = (SomeArgs) msg.obj;
Rect toBounds = (Rect) args.arg1;
- try {
- mActivityTaskManager.resizePinnedStack(
- toBounds, null /* tempPinnedTaskBounds */);
- mBounds.set(toBounds);
- } catch (RemoteException e) {
- Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e);
- }
+ mPipTaskOrganizer.resizePinnedStack(toBounds, PipAnimationController.DURATION_NONE);
+ mBounds.set(toBounds);
return true;
}
@@ -631,8 +564,7 @@
return true;
}
- mActivityTaskManager.animateResizePinnedStack(stackInfo.stackId, toBounds,
- duration);
+ mPipTaskOrganizer.resizePinnedStack(toBounds, duration);
mBounds.set(toBounds);
} catch (RemoteException e) {
Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
@@ -654,8 +586,8 @@
return true;
}
- mActivityTaskManager.offsetPinnedStackBounds(stackInfo.stackId, originalBounds,
- 0/* xOffset */, offset, duration);
+ mPipTaskOrganizer.offsetPinnedStack(originalBounds,
+ 0 /* xOffset */, offset, duration);
Rect toBounds = new Rect(originalBounds);
toBounds.offset(0, offset);
mBounds.set(toBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 9fb6234..389793e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -41,6 +41,7 @@
import com.android.internal.policy.TaskResizingAlgorithm;
import com.android.systemui.R;
import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.util.DeviceConfigProxy;
import java.util.concurrent.Executor;
@@ -77,7 +78,8 @@
private int mCtrlType;
public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
- PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper) {
+ PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper,
+ DeviceConfigProxy deviceConfig) {
final Resources res = context.getResources();
context.getDisplay().getMetrics(mDisplayMetrics);
mDisplayId = context.getDisplayId();
@@ -93,7 +95,7 @@
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_USER_RESIZE,
/* defaultValue = */ false);
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
+ deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 8e588e6..3f73d01 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -45,8 +45,10 @@
import com.android.systemui.R;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
import java.io.PrintWriter;
@@ -58,8 +60,6 @@
public class PipTouchHandler {
private static final String TAG = "PipTouchHandler";
- // Allow the PIP to be dragged to the edge of the screen to be minimized.
- private static final boolean ENABLE_MINIMIZE = false;
// Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed.
private static final boolean ENABLE_FLING_DISMISS = false;
@@ -67,12 +67,11 @@
private static final int BOTTOM_OFFSET_BUFFER_DP = 1;
// Allow dragging the PIP to a location to close it
- private final boolean mEnableDimissDragToEdge;
+ private final boolean mEnableDismissDragToEdge;
+ // Allow PIP to resize to a slightly bigger state upon touch
+ private final boolean mEnableResize;
private final Context mContext;
private final IActivityManager mActivityManager;
- private final IActivityTaskManager mActivityTaskManager;
- private final ViewConfiguration mViewConfig;
- private final PipMenuListener mMenuListener = new PipMenuListener();
private final PipBoundsHandler mPipBoundsHandler;
private final PipResizeGestureHandler mPipResizeGestureHandler;
private IPinnedStackController mPinnedStackController;
@@ -104,7 +103,7 @@
private Runnable mShowDismissAffordance = new Runnable() {
@Override
public void run() {
- if (mEnableDimissDragToEdge) {
+ if (mEnableDismissDragToEdge) {
mDismissViewController.showDismissTarget();
}
}
@@ -112,7 +111,6 @@
// Behaviour states
private int mMenuState = MENU_STATE_NONE;
- private boolean mIsMinimized;
private boolean mIsImeShowing;
private int mImeHeight;
private int mImeOffset;
@@ -121,7 +119,6 @@
private int mMovementBoundsExtraOffsets;
private float mSavedSnapFraction = -1f;
private boolean mSendingHoverAccessibilityEvents;
- private boolean mMovementWithinMinimize;
private boolean mMovementWithinDismiss;
private PipAccessibilityInteractionConnection mConnection;
@@ -146,15 +143,7 @@
@Override
public void onPipExpand() {
- if (!mIsMinimized) {
- mMotionHelper.expandPip();
- }
- }
-
- @Override
- public void onPipMinimize() {
- setMinimizedStateInternal(true);
- mMotionHelper.animateToClosestMinimizedState(null /* updateAction */);
+ mMotionHelper.expandPip();
}
@Override
@@ -175,26 +164,26 @@
IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
InputConsumerController inputConsumerController,
PipBoundsHandler pipBoundsHandler,
- FloatingContentCoordinator floatingContentCoordinator) {
-
+ PipTaskOrganizer pipTaskOrganizer,
+ FloatingContentCoordinator floatingContentCoordinator,
+ DeviceConfigProxy deviceConfig) {
// Initialize the Pip input consumer
mContext = context;
mActivityManager = activityManager;
- mActivityTaskManager = activityTaskManager;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
- mViewConfig = ViewConfiguration.get(context);
mMenuController = menuController;
- mMenuController.addListener(mMenuListener);
+ mMenuController.addListener(new PipMenuListener());
mDismissViewController = new PipDismissViewController(context);
mSnapAlgorithm = new PipSnapAlgorithm(mContext);
mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(),
2.5f);
mGesture = new DefaultPipTouchGesture();
- mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
+ mMotionHelper = new PipMotionHelper(mContext, activityTaskManager, pipTaskOrganizer,
mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator);
mPipResizeGestureHandler =
- new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper);
- mTouchState = new PipTouchState(mViewConfig, mHandler,
+ new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper,
+ deviceConfig);
+ mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
() -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
@@ -203,7 +192,8 @@
R.dimen.pip_expanded_shortest_edge_size);
mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
- mEnableDimissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
+ mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
+ mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu);
// Register the listener for input consumer touch events
inputConsumerController.setInputListener(this::handleTouchEvent);
@@ -279,6 +269,10 @@
public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect curBounds,
boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) {
final int bottomOffset = mIsImeShowing ? mImeHeight : 0;
+ final boolean fromDisplayRotationChanged = (mDisplayRotation != displayRotation);
+ if (fromDisplayRotationChanged) {
+ mTouchState.reset();
+ }
// Re-calculate the expanded bounds
mNormalBounds = normalBounds;
@@ -306,14 +300,14 @@
// If this is from an IME or shelf adjustment, then we should move the PiP so that it is not
// occluded by the IME or shelf.
- if (fromImeAdjustment || fromShelfAdjustment) {
+ if (fromImeAdjustment || fromShelfAdjustment || fromDisplayRotationChanged) {
if (mTouchState.isUserInteracting()) {
// Defer the update of the current movement bounds until after the user finishes
// touching the screen
} else {
final float offsetBufferPx = BOTTOM_OFFSET_BUFFER_DP
* mContext.getResources().getDisplayMetrics().density;
- final Rect toMovementBounds = mMenuState == MENU_STATE_FULL
+ final Rect toMovementBounds = mMenuState == MENU_STATE_FULL && willResizeMenu()
? new Rect(expandedMovementBounds)
: new Rect(normalMovementBounds);
final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
@@ -339,8 +333,7 @@
// If we have a deferred resize, apply it now
if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
- mNormalMovementBounds, mMovementBounds, mIsMinimized,
- true /* immediate */);
+ mNormalMovementBounds, mMovementBounds, true /* immediate */);
mSavedSnapFraction = -1f;
mDeferResizeToNormalBoundsUntilRotation = -1;
}
@@ -482,44 +475,6 @@
}
/**
- * Sets the minimized state.
- */
- private void setMinimizedStateInternal(boolean isMinimized) {
- if (!ENABLE_MINIMIZE) {
- return;
- }
- setMinimizedState(isMinimized, false /* fromController */);
- }
-
- /**
- * Sets the minimized state.
- */
- void setMinimizedState(boolean isMinimized, boolean fromController) {
- if (!ENABLE_MINIMIZE) {
- return;
- }
- if (mIsMinimized != isMinimized) {
- MetricsLoggerWrapper.logPictureInPictureMinimize(mContext,
- isMinimized, PipUtils.getTopPinnedActivity(mContext, mActivityManager));
- }
- mIsMinimized = isMinimized;
- mSnapAlgorithm.setMinimized(isMinimized);
-
- if (fromController) {
- if (isMinimized) {
- // Move the PiP to the new bounds immediately if minimized
- mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds));
- }
- } else if (mPinnedStackController != null) {
- try {
- mPinnedStackController.setIsMinimized(isMinimized);
- } catch (RemoteException e) {
- Log.e(TAG, "Could not set minimized state", e);
- }
- }
- }
-
- /**
* Sets the menu visibility.
*/
private void setMenuState(int menuState, boolean resize) {
@@ -562,8 +517,7 @@
if (mDeferResizeToNormalBoundsUntilRotation == -1) {
Rect normalBounds = new Rect(mNormalBounds);
mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
- mNormalMovementBounds, mMovementBounds, mIsMinimized,
- false /* immediate */);
+ mNormalMovementBounds, mMovementBounds, false /* immediate */);
mSavedSnapFraction = -1f;
}
} else {
@@ -601,8 +555,6 @@
* Gesture controlling normal movement of the PIP.
*/
private class DefaultPipTouchGesture extends PipTouchGesture {
- // Whether the PiP was on the left side of the screen at the start of the gesture
- private boolean mStartedOnLeft;
private final Point mStartPosition = new Point();
private final PointF mDelta = new PointF();
@@ -615,17 +567,15 @@
Rect bounds = mMotionHelper.getBounds();
mDelta.set(0f, 0f);
mStartPosition.set(bounds.left, bounds.top);
- mStartedOnLeft = bounds.left < mMovementBounds.centerX();
- mMovementWithinMinimize = true;
mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
- // If the menu is still visible, and we aren't minimized, then just poke the menu
+ // If the menu is still visible then just poke the menu
// so that it will timeout after the user stops touching it
- if (mMenuState != MENU_STATE_NONE && !mIsMinimized) {
+ if (mMenuState != MENU_STATE_NONE) {
mMenuController.pokeMenu();
}
- if (mEnableDimissDragToEdge) {
+ if (mEnableDismissDragToEdge) {
mDismissViewController.createDismissTarget();
mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
}
@@ -640,7 +590,7 @@
if (touchState.startedDragging()) {
mSavedSnapFraction = -1f;
- if (mEnableDimissDragToEdge) {
+ if (mEnableDismissDragToEdge) {
mHandler.removeCallbacks(mShowDismissAffordance);
mDismissViewController.showDismissTarget();
}
@@ -662,17 +612,11 @@
mTmpBounds.offsetTo((int) left, (int) top);
mMotionHelper.movePip(mTmpBounds, true /* isDragging */);
- if (mEnableDimissDragToEdge) {
+ if (mEnableDismissDragToEdge) {
updateDismissFraction();
}
final PointF curPos = touchState.getLastTouchPosition();
- if (mMovementWithinMinimize) {
- // Track if movement remains near starting edge to identify swipes to minimize
- mMovementWithinMinimize = mStartedOnLeft
- ? curPos.x <= mMovementBounds.left + mTmpBounds.width()
- : curPos.x >= mMovementBounds.right;
- }
if (mMovementWithinDismiss) {
// Track if movement remains near the bottom edge to identify swipe to dismiss
mMovementWithinDismiss = curPos.y >= mMovementBounds.bottom;
@@ -684,7 +628,7 @@
@Override
public boolean onUp(PipTouchState touchState) {
- if (mEnableDimissDragToEdge) {
+ if (mEnableDismissDragToEdge) {
// Clean up the dismiss target regardless of the touch state in case the touch
// enabled state changes while the user is interacting
cleanUpDismissTarget();
@@ -704,7 +648,7 @@
vel.y, isFling);
final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal
&& (mMovementWithinDismiss || isUpWithinDimiss);
- if (mEnableDimissDragToEdge) {
+ if (mEnableDismissDragToEdge) {
// Check if the user dragged or flung the PiP offscreen to dismiss it
if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
@@ -717,33 +661,10 @@
}
if (touchState.isDragging()) {
- final boolean isFlingToEdge = isFling && isHorizontal && mMovementWithinMinimize
- && (mStartedOnLeft ? vel.x < 0 : vel.x > 0);
- if (ENABLE_MINIMIZE &&
- !mIsMinimized && (mMotionHelper.shouldMinimizePip() || isFlingToEdge)) {
- // Pip should be minimized
- setMinimizedStateInternal(true);
- if (mMenuState == MENU_STATE_FULL) {
- // If the user dragged the expanded PiP to the edge, then hiding the menu
- // will trigger the PiP to be scaled back to the normal size with the
- // minimize offset adjusted
- mMenuController.hideMenu();
- } else {
- mMotionHelper.animateToClosestMinimizedState(
- PipTouchHandler.this::updateDismissFraction /* updateAction */);
- }
- return true;
- }
- if (mIsMinimized) {
- // If we're dragging and it wasn't a minimize gesture then we shouldn't be
- // minimized.
- setMinimizedStateInternal(false);
- }
-
Runnable endAction = null;
if (mMenuState != MENU_STATE_NONE) {
- // If the menu is still visible, and we aren't minimized, then just poke the
- // menu so that it will timeout after the user stops touching it
+ // If the menu is still visible, then just poke the menu so that
+ // it will timeout after the user stops touching it
mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(),
mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
} else {
@@ -759,10 +680,6 @@
} else {
mMotionHelper.animateToClosestSnapTarget();
}
- } else if (mIsMinimized) {
- // This was a tap, so no longer minimized
- mMotionHelper.animateToClosestSnapTarget();
- setMinimizedStateInternal(false);
} else if (mTouchState.isDoubleTap()) {
// Expand to fullscreen if this is a double tap
mMotionHelper.expandPip();
@@ -784,11 +701,12 @@
};
/**
- * Updates the current movement bounds based on whether the menu is currently visible.
+ * Updates the current movement bounds based on whether the menu is currently visible and
+ * resized.
*/
private void updateMovementBounds(int menuState) {
boolean isMenuExpanded = menuState == MENU_STATE_FULL;
- mMovementBounds = isMenuExpanded
+ mMovementBounds = isMenuExpanded && willResizeMenu()
? mExpandedMovementBounds
: mNormalMovementBounds;
mPipBoundsHandler.setMinEdgeSize(
@@ -808,8 +726,11 @@
* @return whether the menu will resize as a part of showing the full menu.
*/
private boolean willResizeMenu() {
- return mExpandedBounds.width() != mNormalBounds.width() ||
- mExpandedBounds.height() != mNormalBounds.height();
+ if (!mEnableResize) {
+ return false;
+ }
+ return mExpandedBounds.width() != mNormalBounds.width()
+ || mExpandedBounds.height() != mNormalBounds.height();
}
public void dump(PrintWriter pw, String prefix) {
@@ -821,14 +742,12 @@
pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
pw.println(innerPrefix + "mMenuState=" + mMenuState);
- pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
- pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDimissDragToEdge);
- pw.println(innerPrefix + "mEnableMinimize=" + ENABLE_MINIMIZE);
+ pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge);
mSnapAlgorithm.dump(pw, innerPrefix);
mTouchState.dump(pw, innerPrefix);
mMotionHelper.dump(pw, innerPrefix);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 487c253..cb1a218 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -50,7 +50,9 @@
import com.android.systemui.UiOffloadThread;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.pip.BasePipManager;
+import com.android.systemui.pip.PipAnimationController;
import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -66,7 +68,7 @@
* Manages the picture-in-picture (PIP) UI and states.
*/
@Singleton
-public class PipManager implements BasePipManager {
+public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback {
private static final String TAG = "PipManager";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -91,7 +93,6 @@
private static final int INVALID_RESOURCE_TYPE = -1;
public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1;
- public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2;
/**
* PIPed activity is playing a media and it can be paused.
@@ -112,6 +113,7 @@
private Context mContext;
private PipBoundsHandler mPipBoundsHandler;
+ private PipTaskOrganizer mPipTaskOrganizer;
private IActivityTaskManager mActivityTaskManager;
private MediaSessionManager mMediaSessionManager;
private int mState = STATE_NO_PIP;
@@ -205,8 +207,7 @@
}
@Override
- public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
- boolean fromShelfAdjustment) {
+ public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {
mHandler.post(() -> {
// Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
@@ -234,6 +235,8 @@
mInitialized = true;
mContext = context;
mPipBoundsHandler = new PipBoundsHandler(context);
+ mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler);
+ mPipTaskOrganizer.registerPipTransitionCallback(this);
mActivityTaskManager = ActivityTaskManager.getService();
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
IntentFilter intentFilter = new IntentFilter();
@@ -279,9 +282,12 @@
mMediaSessionManager =
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+ mPipTaskOrganizer.registerPipTransitionCallback(this);
try {
WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
+ ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(
+ mPipTaskOrganizer, WINDOWING_MODE_PINNED);
} catch (RemoteException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
}
@@ -422,20 +428,13 @@
case STATE_PIP_MENU:
mCurrentPipBounds = mMenuModePipBounds;
break;
- case STATE_PIP:
- mCurrentPipBounds = mPipBounds;
- break;
+ case STATE_PIP: // fallthrough
default:
mCurrentPipBounds = mPipBounds;
break;
}
- try {
- int animationDurationMs = -1;
- mActivityTaskManager.animateResizePinnedStack(mPinnedStackId, mCurrentPipBounds,
- animationDurationMs);
- } catch (RemoteException e) {
- Log.e(TAG, "resizeStack failed", e);
- }
+ mPipTaskOrganizer.resizePinnedStack(
+ mCurrentPipBounds, PipAnimationController.DURATION_DEFAULT_MS);
}
/**
@@ -449,13 +448,6 @@
}
/**
- * Returns the default PIP bound.
- */
- public Rect getPipBounds() {
- return mPipBounds;
- }
-
- /**
* Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned
* stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}.
*/
@@ -692,19 +684,28 @@
// If PIPed activity is launched again by Launcher or intent, make it fullscreen.
movePipToFullscreen();
}
-
- @Override
- public void onPinnedStackAnimationEnded() {
- if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
-
- switch (getState()) {
- case STATE_PIP_MENU:
- showPipMenu();
- break;
- }
- }
};
+ @Override
+ public void onPipTransitionStarted() { }
+
+ @Override
+ public void onPipTransitionFinished() {
+ onPipTransitionFinishedOrCanceled();
+ }
+
+ @Override
+ public void onPipTransitionCanceled() {
+ onPipTransitionFinishedOrCanceled();
+ }
+
+ private void onPipTransitionFinishedOrCanceled() {
+ if (DEBUG) Log.d(TAG, "onPipTransitionFinishedOrCanceled()");
+ if (getState() == STATE_PIP_MENU) {
+ showPipMenu();
+ }
+ }
+
/**
* A listener interface to receive notification on changes in PIP.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index fb89ed2..bfac85b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -37,8 +37,8 @@
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.qs.QS;
@@ -86,6 +86,7 @@
private int mY;
private boolean mOpening;
private boolean mIsShowingNavBackdrop;
+ private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
@Inject
public QSCustomizer(Context context, AttributeSet attrs,
@@ -187,7 +188,7 @@
int containerLocation[] = findViewById(R.id.customize_container).getLocationOnScreen();
mX = x - containerLocation[0];
mY = y - containerLocation[1];
- MetricsLogger.visible(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_OPEN);
isShown = true;
mOpening = true;
setTileSpecs();
@@ -224,7 +225,7 @@
public void hide() {
final boolean animate = mScreenLifecycle.getScreenState() != ScreenLifecycle.SCREEN_OFF;
if (isShown) {
- MetricsLogger.hidden(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED);
isShown = false;
mToolbar.dismissPopupMenus();
setCustomizing(false);
@@ -258,7 +259,7 @@
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case MENU_RESET:
- MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_QS_EDIT_RESET);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_RESET);
reset();
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt
new file mode 100644
index 0000000..ff8ddec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.qs.customize
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class QSEditEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+
+ @UiEvent(doc = "Tile removed from current tiles")
+ QS_EDIT_REMOVE(210),
+ @UiEvent(doc = "Tile added to current tiles")
+ QS_EDIT_ADD(211),
+ @UiEvent(doc = "Tile moved")
+ QS_EDIT_MOVE(212),
+ @UiEvent(doc = "QS customizer open")
+ QS_EDIT_OPEN(213),
+ @UiEvent(doc = "QS customizer closed")
+ QS_EDIT_CLOSED(214),
+ @UiEvent(doc = "QS tiles reset")
+ QS_EDIT_RESET(215);
+
+ override fun getId() = _id
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 3afc460..58de95d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -40,8 +40,8 @@
import androidx.recyclerview.widget.RecyclerView.State;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.customize.TileAdapter.Holder;
@@ -92,6 +92,7 @@
private int mAccessibilityFromIndex;
private CharSequence mAccessibilityFromLabel;
private QSTileHost mHost;
+ private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
public TileAdapter(Context context) {
mContext = context;
@@ -436,20 +437,11 @@
move(from, to, mTiles);
updateDividerLocations();
if (to >= mEditIndex) {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE_SPEC,
- strip(mTiles.get(to)));
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE,
- from);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_REMOVE, 0, strip(mTiles.get(to)));
} else if (from >= mEditIndex) {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD_SPEC,
- strip(mTiles.get(to)));
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD,
- to);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_ADD, 0, strip(mTiles.get(to)));
} else {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE_SPEC,
- strip(mTiles.get(to)));
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE,
- to);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_MOVE, 0, strip(mTiles.get(to)));
}
saveSpecs(mHost);
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index ebf45a6..9f7b84a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -53,6 +53,7 @@
import com.android.systemui.R;
import com.android.systemui.SysUIToast;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -79,6 +80,7 @@
private final ZenModeController mController;
private final DndDetailAdapter mDetailAdapter;
private final ActivityStarter mActivityStarter;
+ private final SharedPreferences mSharedPreferences;
private final BroadcastDispatcher mBroadcastDispatcher;
private boolean mListening;
@@ -87,10 +89,12 @@
@Inject
public DndTile(QSHost host, ZenModeController zenModeController,
- ActivityStarter activityStarter, BroadcastDispatcher broadcastDispatcher) {
+ ActivityStarter activityStarter, BroadcastDispatcher broadcastDispatcher,
+ @Main SharedPreferences sharedPreferences) {
super(host);
mController = zenModeController;
mActivityStarter = activityStarter;
+ mSharedPreferences = sharedPreferences;
mDetailAdapter = new DndDetailAdapter();
mBroadcastDispatcher = broadcastDispatcher;
broadcastDispatcher.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE));
@@ -111,16 +115,16 @@
Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible);
}
- public static boolean isVisible(Context context) {
- return Prefs.getBoolean(context, Prefs.Key.DND_TILE_VISIBLE, false /* defaultValue */);
+ public static boolean isVisible(SharedPreferences prefs) {
+ return prefs.getBoolean(Prefs.Key.DND_TILE_VISIBLE, false /* defaultValue */);
}
public static void setCombinedIcon(Context context, boolean combined) {
Prefs.putBoolean(context, Prefs.Key.DND_TILE_COMBINED_ICON, combined);
}
- public static boolean isCombinedIcon(Context context) {
- return Prefs.getBoolean(context, Prefs.Key.DND_TILE_COMBINED_ICON,
+ public static boolean isCombinedIcon(SharedPreferences sharedPreferences) {
+ return sharedPreferences.getBoolean(Prefs.Key.DND_TILE_COMBINED_ICON,
false /* defaultValue */);
}
@@ -301,7 +305,7 @@
@Override
public boolean isAvailable() {
- return isVisible(mContext);
+ return isVisible(mSharedPreferences);
}
private final OnSharedPreferenceChangeListener mPrefListener
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 2557226..5dcb4e3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -16,9 +16,9 @@
package com.android.systemui.qs.tiles;
-import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.widget.Switch;
@@ -81,11 +81,11 @@
}
public static boolean isCurrentOrientationLockPortrait(RotationLockController controller,
- Context context) {
+ Resources resources) {
int lockOrientation = controller.getRotationLockOrientation();
if (lockOrientation == Configuration.ORIENTATION_UNDEFINED) {
// Freely rotating device; use current rotation
- return context.getResources().getConfiguration().orientation
+ return resources.getConfiguration().orientation
!= Configuration.ORIENTATION_LANDSCAPE;
} else {
return lockOrientation != Configuration.ORIENTATION_LANDSCAPE;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 39bfd5a..f169501 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -206,7 +206,7 @@
state.icon = ResourceIcon.get(cb.wifiSignalIconId);
state.label = removeDoubleQuotes(cb.ssid);
} else if (wifiNotConnected) {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disconnected);
+ state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
state.label = r.getString(R.string.quick_settings_wifi_label);
} else {
state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 34cad51..1cd6388 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -61,9 +61,11 @@
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.Dumpable;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.pip.PipAnimationController;
import com.android.systemui.pip.PipUI;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
@@ -388,6 +390,32 @@
}
}
+ @Override
+ public void notifySwipeToHomeFinished() {
+ if (!verifyCaller("notifySwipeToHomeFinished")) {
+ return;
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ mPipUI.setPinnedStackAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
+ if (!verifyCaller("setPinnedStackAnimationListener")) {
+ return;
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ mPipUI.setPinnedStackAnimationListener(listener);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 626f298..390ac09 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -63,7 +63,7 @@
/**
* A service which records the device screen and optionally microphone input.
*/
-public class RecordingService extends Service {
+public class RecordingService extends Service implements MediaRecorder.OnInfoListener {
public static final int REQUEST_CODE = 2;
private static final int NOTIFICATION_ID = 1;
@@ -85,6 +85,8 @@
private static final int VIDEO_FRAME_RATE = 30;
private static final int AUDIO_BIT_RATE = 16;
private static final int AUDIO_SAMPLE_RATE = 44100;
+ private static final int MAX_DURATION_MS = 60 * 60 * 1000;
+ private static final long MAX_FILESIZE_BYTES = 5000000000L;
private final RecordingController mController;
private MediaProjection mMediaProjection;
@@ -250,6 +252,8 @@
mMediaRecorder.setVideoSize(screenWidth, screenHeight);
mMediaRecorder.setVideoFrameRate(VIDEO_FRAME_RATE);
mMediaRecorder.setVideoEncodingBitRate(VIDEO_BIT_RATE);
+ mMediaRecorder.setMaxDuration(MAX_DURATION_MS);
+ mMediaRecorder.setMaxFileSize(MAX_FILESIZE_BYTES);
// Set up audio
if (mUseAudio) {
@@ -274,6 +278,7 @@
null,
null);
+ mMediaRecorder.setOnInfoListener(this);
mMediaRecorder.start();
mController.updateState(true);
} catch (IOException e) {
@@ -454,4 +459,10 @@
return new Intent(context, RecordingService.class).setAction(ACTION_DELETE)
.putExtra(EXTRA_PATH, path);
}
+
+ @Override
+ public void onInfo(MediaRecorder mr, int what, int extra) {
+ Log.d(TAG, "Media recorder info: " + what);
+ onStartCommand(getStopIntent(this), 0, 0);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index f06cd54..3afd5dc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -514,12 +514,17 @@
1, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct));
mScreenshotView.setScaleX(scale);
mScreenshotView.setScaleY(scale);
+ } else {
+ mScreenshotView.setScaleX(cornerScale);
+ mScreenshotView.setScaleY(cornerScale);
}
if (t < xPositionPct) {
float xCenter = MathUtils.lerp(startPos.x, finalPos.x,
mFastOutSlowIn.getInterpolation(t / xPositionPct));
mScreenshotView.setX(xCenter - width * mScreenshotView.getScaleX() / 2f);
+ } else {
+ mScreenshotView.setX(finalPos.x - width * mScreenshotView.getScaleX() / 2f);
}
float yCenter = MathUtils.lerp(startPos.y, finalPos.y,
mFastOutSlowIn.getInterpolation(t));
@@ -544,6 +549,10 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
+ mScreenshotView.setScaleX(cornerScale);
+ mScreenshotView.setScaleY(cornerScale);
+ mScreenshotView.setX(finalPos.x - height * cornerScale / 2f);
+ mScreenshotView.setY(finalPos.y - height * cornerScale / 2f);
Rect bounds = new Rect();
mScreenshotView.getBoundsOnScreen(bounds);
mDismissButton.setX(bounds.right - mDismissButtonSize / 2f);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 56cdff4..27b799b 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -401,6 +401,7 @@
return;
}
mMinimized = minimized;
+ WindowManagerProxy.applyPrimaryFocusable(mSplits, !mMinimized);
mView.setMinimizedDockStack(minimized, getAnimDuration(), mHomeStackResizable);
updateTouchable();
});
@@ -504,6 +505,7 @@
final boolean wasMinimized = mMinimized;
mMinimized = true;
setHomeStackResizable(mSplits.mSecondary.isResizable());
+ WindowManagerProxy.applyPrimaryFocusable(mSplits, false /* focusable */);
if (!inSplitMode()) {
// Wasn't in split-mode yet, so enter now.
if (DEBUG) {
@@ -521,6 +523,9 @@
}
void ensureNormalSplit() {
+ if (mMinimized) {
+ WindowManagerProxy.applyPrimaryFocusable(mSplits, true /* focusable */);
+ }
if (!inSplitMode()) {
// Wasn't in split-mode, so enter now.
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 7685733..167c33a 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -292,10 +292,23 @@
for (int i = freeHomeAndRecents.size() - 1; i >= 0; --i) {
wct.setBounds(freeHomeAndRecents.get(i).token, null);
}
+ // Reset focusable to true
+ wct.setFocusable(tiles.mPrimary.token, true /* focusable */);
ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
null /* organizer */);
} catch (RemoteException e) {
Log.w(TAG, "Failed to remove stack: " + e);
}
}
+
+ static void applyPrimaryFocusable(SplitScreenTaskOrganizer splits, boolean focusable) {
+ try {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setFocusable(splits.mPrimary.token, focusable);
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error setting focusability: " + e);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index f8db922..cdb2c53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -65,6 +65,7 @@
import com.android.systemui.statusbar.phone.ScrimState;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.DeviceConfigProxy;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -183,7 +184,8 @@
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
KeyguardBypassController keyguardBypassController,
- @Main Executor mainExecutor) {
+ @Main Executor mainExecutor,
+ DeviceConfigProxy deviceConfig) {
mContext = context;
mMediaArtworkProcessor = mediaArtworkProcessor;
mKeyguardBypassController = keyguardBypassController;
@@ -221,7 +223,7 @@
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED));
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
mContext.getMainExecutor(),
mPropertiesChangedListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 7e70c20..fe2f1f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -30,6 +30,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.notification.DynamicChildBindController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -59,11 +60,12 @@
private final Handler mHandler;
- //TODO: change this top <Entry, List<Entry>>?
- private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
- mTmpChildOrderMap = new HashMap<>();
+ /** Re-usable map of notifications to their sorted children.*/
+ private final HashMap<NotificationEntry, List<NotificationEntry>> mTmpChildOrderMap =
+ new HashMap<>();
// Dependencies:
+ private final DynamicChildBindController mDynamicChildBindController;
protected final NotificationLockscreenUserManager mLockscreenUserManager;
protected final NotificationGroupManager mGroupManager;
protected final VisualStabilityManager mVisualStabilityManager;
@@ -105,7 +107,8 @@
KeyguardBypassController bypassController,
BubbleController bubbleController,
DynamicPrivacyController privacyController,
- ForegroundServiceSectionController fgsSectionController) {
+ ForegroundServiceSectionController fgsSectionController,
+ DynamicChildBindController dynamicChildBindController) {
mContext = context;
mHandler = mainHandler;
mLockscreenUserManager = notificationLockscreenUserManager;
@@ -121,6 +124,7 @@
mBubbleController = bubbleController;
mDynamicPrivacyController = privacyController;
privacyController.addListener(this);
+ mDynamicChildBindController = dynamicChildBindController;
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -175,13 +179,12 @@
ent.getRow().setNeedsRedaction(needsRedaction);
if (mGroupManager.isChildInGroupWithSummary(ent.getSbn())) {
NotificationEntry summary = mGroupManager.getGroupSummary(ent.getSbn());
- List<ExpandableNotificationRow> orderedChildren =
- mTmpChildOrderMap.get(summary.getRow());
+ List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(summary);
if (orderedChildren == null) {
orderedChildren = new ArrayList<>();
- mTmpChildOrderMap.put(summary.getRow(), orderedChildren);
+ mTmpChildOrderMap.put(summary, orderedChildren);
}
- orderedChildren.add(ent.getRow());
+ orderedChildren.add(ent);
} else {
toShow.add(ent.getRow());
}
@@ -260,6 +263,7 @@
}
+ mDynamicChildBindController.updateChildContentViews(mTmpChildOrderMap);
mVisualStabilityManager.onReorderingFinished();
// clear the map again for the next usage
mTmpChildOrderMap.clear();
@@ -274,6 +278,7 @@
private void addNotificationChildrenAndSort() {
// Let's now add all notification children which are missing
boolean orderChanged = false;
+ ArrayList<ExpandableNotificationRow> orderedRows = new ArrayList<>();
for (int i = 0; i < mListContainer.getContainerChildCount(); i++) {
View view = mListContainer.getContainerChildAt(i);
if (!(view instanceof ExpandableNotificationRow)) {
@@ -283,11 +288,11 @@
ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
List<ExpandableNotificationRow> children = parent.getNotificationChildren();
- List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
+ List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent.getEntry());
for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
childIndex++) {
- ExpandableNotificationRow childView = orderedChildren.get(childIndex);
+ ExpandableNotificationRow childView = orderedChildren.get(childIndex).getRow();
if (children == null || !children.contains(childView)) {
if (childView.getParent() != null) {
Log.wtf(TAG, "trying to add a notification child that already has " +
@@ -300,11 +305,13 @@
parent.addChildNotification(childView, childIndex);
mListContainer.notifyGroupChildAdded(childView);
}
+ orderedRows.add(childView);
}
// Finally after removing and adding has been performed we can apply the order.
- orderChanged |= parent.applyChildOrder(orderedChildren, mVisualStabilityManager,
+ orderChanged |= parent.applyChildOrder(orderedRows, mVisualStabilityManager,
mEntryManager);
+ orderedRows.clear();
}
if (orderChanged) {
mListContainer.generateChildOrderChangedEvent();
@@ -323,13 +330,13 @@
ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
List<ExpandableNotificationRow> children = parent.getNotificationChildren();
- List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
+ List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent.getEntry());
if (children != null) {
toRemove.clear();
for (ExpandableNotificationRow childRow : children) {
if ((orderedChildren == null
- || !orderedChildren.contains(childRow))
+ || !orderedChildren.contains(childRow.getEntry()))
&& !childRow.keepInParent()) {
toRemove.add(childRow);
}
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 cd5bb77..4c99a90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.DynamicChildBindController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -42,6 +43,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.tracing.ProtoTracer;
+import com.android.systemui.util.DeviceConfigProxy;
import java.util.concurrent.Executor;
@@ -91,7 +93,8 @@
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
KeyguardBypassController keyguardBypassController,
- @Main Executor mainExecutor) {
+ @Main Executor mainExecutor,
+ DeviceConfigProxy deviceConfigProxy) {
return new NotificationMediaManager(
context,
statusBarLazy,
@@ -99,7 +102,8 @@
notificationEntryManager,
mediaArtworkProcessor,
keyguardBypassController,
- mainExecutor);
+ mainExecutor,
+ deviceConfigProxy);
}
/** */
@@ -135,7 +139,8 @@
KeyguardBypassController bypassController,
BubbleController bubbleController,
DynamicPrivacyController privacyController,
- ForegroundServiceSectionController fgsSectionController) {
+ ForegroundServiceSectionController fgsSectionController,
+ DynamicChildBindController dynamicChildBindController) {
return new NotificationViewHierarchyManager(
context,
mainHandler,
@@ -147,7 +152,8 @@
bypassController,
bubbleController,
privacyController,
- fgsSectionController);
+ fgsSectionController,
+ dynamicChildBindController);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java
new file mode 100644
index 0000000..059d6ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java
@@ -0,0 +1,118 @@
+/*
+ * 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.notification;
+
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_EXPANDED;
+import static com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
+
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.RowContentBindParams;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+/**
+ * Controller that binds/unbinds views content views on notification group children.
+ *
+ * We currently only show a limited number of notification children even if more exist, so we
+ * can save memory by freeing content views when they're not visible and binding them again when
+ * they get close to being visible.
+ *
+ * Eventually, when {@link NotifPipeline} takes over as the new notification pipeline, we'll have
+ * more control over which notifications even make it to inflation in the first place and be able
+ * to enforce this at an earlier stage at the level of the {@link ExpandableNotificationRow}, but
+ * for now, we're just doing it at the level of content views.
+ */
+public class DynamicChildBindController {
+ private final RowContentBindStage mStage;
+ private final int mChildBindCutoff;
+
+ @Inject
+ public DynamicChildBindController(RowContentBindStage stage) {
+ this(stage, CHILD_BIND_CUTOFF);
+ }
+
+ /**
+ * @param childBindCutoff the cutoff where we no longer bother having content views bound
+ */
+ DynamicChildBindController(
+ RowContentBindStage stage,
+ int childBindCutoff) {
+ mStage = stage;
+ mChildBindCutoff = childBindCutoff;
+ }
+
+ /**
+ * Update the child content views, unbinding content views on children that won't be visible
+ * and binding content views on children that will be visible eventually.
+ *
+ * @param groupNotifs map of notification summaries to their children
+ */
+ public void updateChildContentViews(
+ Map<NotificationEntry, List<NotificationEntry>> groupNotifs) {
+ for (NotificationEntry entry : groupNotifs.keySet()) {
+ List<NotificationEntry> children = groupNotifs.get(entry);
+ for (int j = 0; j < children.size(); j++) {
+ NotificationEntry childEntry = children.get(j);
+ if (j >= mChildBindCutoff) {
+ if (hasChildContent(childEntry)) {
+ freeChildContent(childEntry);
+ }
+ } else {
+ if (!hasChildContent(childEntry)) {
+ bindChildContent(childEntry);
+ }
+ }
+ }
+ }
+ }
+
+ private boolean hasChildContent(NotificationEntry entry) {
+ ExpandableNotificationRow row = entry.getRow();
+ return row.getPrivateLayout().getContractedChild() != null
+ || row.getPrivateLayout().getExpandedChild() != null;
+ }
+
+ private void freeChildContent(NotificationEntry entry) {
+ RowContentBindParams params = mStage.getStageParams(entry);
+ params.freeContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
+ params.freeContentViews(FLAG_CONTENT_VIEW_EXPANDED);
+ mStage.requestRebind(entry, null);
+ }
+
+ private void bindChildContent(NotificationEntry entry) {
+ RowContentBindParams params = mStage.getStageParams(entry);
+ params.requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
+ params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED);
+ mStage.requestRebind(entry, null);
+ }
+
+ /**
+ * How big the buffer of extra views we keep around to be ready to show when we do need to
+ * dynamically inflate.
+ */
+ private static final int EXTRA_VIEW_BUFFER_COUNT = 1;
+
+ private static final int CHILD_BIND_CUTOFF =
+ NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED + EXTRA_VIEW_BUFFER_COUNT;
+}
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 5008133..6d3f126 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
@@ -18,7 +18,10 @@
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -28,6 +31,7 @@
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
import android.app.NotificationChannel;
import android.content.Context;
import android.content.pm.PackageInfo;
@@ -148,6 +152,7 @@
private int mNotificationMinHeight;
private int mNotificationMinHeightLarge;
private int mNotificationMinHeightMedia;
+ private int mNotificationMinHeightMessaging;
private int mNotificationMaxHeight;
private int mIncreasedPaddingBetweenElements;
private int mNotificationLaunchHeight;
@@ -465,6 +470,14 @@
}
};
switch (inflationFlag) {
+ case FLAG_CONTENT_VIEW_CONTRACTED:
+ getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_CONTRACTED,
+ freeViewRunnable);
+ break;
+ case FLAG_CONTENT_VIEW_EXPANDED:
+ getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_EXPANDED,
+ freeViewRunnable);
+ break;
case FLAG_CONTENT_VIEW_HEADS_UP:
getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
freeViewRunnable);
@@ -630,10 +643,16 @@
&& expandedView.findViewById(com.android.internal.R.id.media_actions) != null;
boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar();
+ Class<? extends Notification.Style> style =
+ mEntry.getSbn().getNotification().getNotificationStyle();
+ boolean isMessagingLayout = Notification.MessagingStyle.class.equals(style);
+
if (customView && beforeP && !mIsSummaryWithChildren) {
minHeight = beforeN ? mNotificationMinHeightBeforeN : mNotificationMinHeightBeforeP;
} else if (isMediaLayout && showCompactMediaSeekbar) {
minHeight = mNotificationMinHeightMedia;
+ } else if (isMessagingLayout) {
+ minHeight = mNotificationMinHeightMessaging;
} else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
minHeight = mNotificationMinHeightLarge;
} else {
@@ -799,7 +818,7 @@
// TODO: Move inflation logic out of this call
if (mIsChildInGroup != isChildInGroup) {
mIsChildInGroup = isChildInGroup;
- if (mIsLowPriority) {
+ if (!isRemoved() && mIsLowPriority) {
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
params.setUseLowPriority(mIsLowPriority);
mRowContentBindStage.requestRebind(mEntry, null /* callback */);
@@ -1568,13 +1587,15 @@
// TODO: Move inflation logic out of this call and remove this method
if (mNeedsRedaction != needsRedaction) {
mNeedsRedaction = needsRedaction;
- RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
- if (needsRedaction) {
- params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
- } else {
- params.freeContentViews(FLAG_CONTENT_VIEW_PUBLIC);
+ if (!isRemoved()) {
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ if (needsRedaction) {
+ params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
+ } else {
+ params.freeContentViews(FLAG_CONTENT_VIEW_PUBLIC);
+ }
+ mRowContentBindStage.requestRebind(mEntry, null /* callback */);
}
- mRowContentBindStage.requestRebind(mEntry, null /* callback */);
}
}
@@ -1635,6 +1656,8 @@
R.dimen.notification_min_height_increased);
mNotificationMinHeightMedia = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_min_height_media);
+ mNotificationMinHeightMessaging = NotificationUtils.getFontScaledHeight(mContext,
+ R.dimen.notification_min_height_messaging);
mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_height);
mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index e3ca283..6dd4ff9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -18,6 +18,7 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
import android.annotation.NonNull;
@@ -191,6 +192,18 @@
private void freeNotificationView(NotificationEntry entry, ExpandableNotificationRow row,
@InflationFlag int inflateFlag) {
switch (inflateFlag) {
+ case FLAG_CONTENT_VIEW_CONTRACTED:
+ if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
+ row.getPrivateLayout().setContractedChild(null);
+ mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED);
+ }
+ break;
+ case FLAG_CONTENT_VIEW_EXPANDED:
+ if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_EXPANDED)) {
+ row.getPrivateLayout().setExpandedChild(null);
+ mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED);
+ }
+ break;
case FLAG_CONTENT_VIEW_HEADS_UP:
if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
row.getPrivateLayout().setHeadsUpChild(null);
@@ -204,8 +217,6 @@
mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC);
}
break;
- case FLAG_CONTENT_VIEW_CONTRACTED:
- case FLAG_CONTENT_VIEW_EXPANDED:
default:
break;
}
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 d1b9a87..27fd1b2 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
@@ -71,7 +71,13 @@
public static final int VISIBLE_TYPE_EXPANDED = 1;
public static final int VISIBLE_TYPE_HEADSUP = 2;
private static final int VISIBLE_TYPE_SINGLELINE = 3;
- public static final int UNDEFINED = -1;
+ /**
+ * Used when there is no content on the view such as when we're a public layout but don't
+ * need to show.
+ */
+ private static final int VISIBLE_TYPE_NONE = -1;
+
+ private static final int UNDEFINED = -1;
private final Rect mClipBounds = new Rect();
@@ -99,7 +105,7 @@
private HybridGroupManager mHybridGroupManager;
private int mClipTopAmount;
private int mContentHeight;
- private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
+ private int mVisibleType = VISIBLE_TYPE_NONE;
private boolean mAnimate;
private boolean mIsHeadsUp;
private boolean mLegacy;
@@ -141,7 +147,7 @@
/** The visible type at the start of a touch driven transformation */
private int mTransformationStartVisibleType;
/** The visible type at the start of an animation driven transformation */
- private int mAnimationStartVisibleType = UNDEFINED;
+ private int mAnimationStartVisibleType = VISIBLE_TYPE_NONE;
private boolean mUserExpanding;
private int mSingleLineWidthIndention;
private boolean mForceSelectNextLayout = true;
@@ -386,7 +392,7 @@
mContractedChild = null;
mContractedWrapper = null;
if (mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED) {
- mTransformationStartVisibleType = UNDEFINED;
+ mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
}
return;
}
@@ -434,7 +440,7 @@
mExpandedChild = null;
mExpandedWrapper = null;
if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
- mTransformationStartVisibleType = UNDEFINED;
+ mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
}
if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
selectLayout(false /* animate */, true /* force */);
@@ -472,7 +478,7 @@
mHeadsUpChild = null;
mHeadsUpWrapper = null;
if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
- mTransformationStartVisibleType = UNDEFINED;
+ mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
}
if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
selectLayout(false /* animate */, true /* force */);
@@ -597,7 +603,7 @@
}
// Size change of the expanded version
- if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
+ if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart != UNDEFINED
&& mExpandedChild != null) {
return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
}
@@ -607,10 +613,12 @@
hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
} else if (mExpandedChild != null) {
hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
- } else {
+ } else if (mContractedChild != null) {
hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
+ mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_action_list_height);
+ } else {
+ hint = getMinHeight();
}
if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
@@ -646,7 +654,7 @@
if (mForceSelectNextLayout) {
forceUpdateVisibilities();
}
- if (mTransformationStartVisibleType != UNDEFINED
+ if (mTransformationStartVisibleType != VISIBLE_TYPE_NONE
&& mVisibleType != mTransformationStartVisibleType
&& getViewForVisibleType(mTransformationStartVisibleType) != null) {
final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
@@ -823,7 +831,7 @@
fireExpandedVisibleListenerIfVisible();
// forceUpdateVisibilities cancels outstanding animations without updating the
// mAnimationStartVisibleType. Do so here instead.
- mAnimationStartVisibleType = UNDEFINED;
+ mAnimationStartVisibleType = VISIBLE_TYPE_NONE;
}
private void fireExpandedVisibleListenerIfVisible() {
@@ -898,7 +906,7 @@
fireExpandedVisibleListenerIfVisible();
// updateViewVisibilities cancels outstanding animations without updating the
// mAnimationStartVisibleType. Do so here instead.
- mAnimationStartVisibleType = UNDEFINED;
+ mAnimationStartVisibleType = VISIBLE_TYPE_NONE;
}
private void updateViewVisibility(int visibleType, int type, View view,
@@ -924,7 +932,7 @@
if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
hiddenView.setVisible(false);
}
- mAnimationStartVisibleType = UNDEFINED;
+ mAnimationStartVisibleType = VISIBLE_TYPE_NONE;
}
});
fireExpandedVisibleListenerIfVisible();
@@ -1041,8 +1049,10 @@
&& (!mIsChildInGroup || isGroupExpanded()
|| !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
return VISIBLE_TYPE_CONTRACTED;
- } else {
+ } else if (!noExpandedChild) {
return VISIBLE_TYPE_EXPANDED;
+ } else {
+ return VISIBLE_TYPE_NONE;
}
}
}
@@ -1423,7 +1433,8 @@
if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
|| mHeadsUpChild == null || !mContainingNotification.canShowHeadsUp()) {
- if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
+ if (mContractedChild == null
+ || mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
expandable = false;
}
} else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
@@ -1514,7 +1525,7 @@
if (userExpanding) {
mTransformationStartVisibleType = mVisibleType;
} else {
- mTransformationStartVisibleType = UNDEFINED;
+ mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
mVisibleType = calculateVisibleType();
updateViewVisibilities(mVisibleType);
updateBackgroundColor(false);
@@ -1558,6 +1569,7 @@
}
public void setContentHeightAnimating(boolean animating) {
+ //TODO: It's odd that this does nothing when animating is true
if (!animating) {
mContentHeightAtAnimationStart = UNDEFINED;
}
@@ -1565,7 +1577,7 @@
@VisibleForTesting
boolean isAnimatingVisibleType() {
- return mAnimationStartVisibleType != UNDEFINED;
+ return mAnimationStartVisibleType != VISIBLE_TYPE_NONE;
}
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
@@ -1758,17 +1770,25 @@
}
public int getExpandHeight() {
- int viewType = VISIBLE_TYPE_EXPANDED;
- if (mExpandedChild == null) {
+ int viewType;
+ if (mExpandedChild != null) {
+ viewType = VISIBLE_TYPE_EXPANDED;
+ } else if (mContractedChild != null) {
viewType = VISIBLE_TYPE_CONTRACTED;
+ } else {
+ return getMinHeight();
}
return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
}
public int getHeadsUpHeight(boolean forceNoHeader) {
- int viewType = VISIBLE_TYPE_HEADSUP;
- if (mHeadsUpChild == null) {
+ int viewType;
+ if (mHeadsUpChild != null) {
+ viewType = VISIBLE_TYPE_HEADSUP;
+ } else if (mContractedChild != null) {
viewType = VISIBLE_TYPE_CONTRACTED;
+ } else {
+ return getMinHeight();
}
// The headsUp remote input quickly switches to the expanded one, so lets also include that
// one
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 60eda06..bab7840 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -20,6 +20,7 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
@@ -218,7 +219,7 @@
// TODO: consider querying this earlier in the notification pipeline and passing it in
LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
.setPackage(mPackageName)
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
+ .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED)
.setShortcutIds(Arrays.asList(mConversationId));
List<ShortcutInfo> shortcuts = mLauncherApps.getShortcuts(query, mSbn.getUser());
if (shortcuts != null && !shortcuts.isEmpty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 75ceb0f..d7c88e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -53,8 +53,7 @@
static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
@VisibleForTesting
static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
- @VisibleForTesting
- static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
+ public static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() {
private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
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 8729e04..f38d416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -44,8 +44,9 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -91,6 +92,7 @@
private boolean mExpandAnimationRunning;
private NotificationStackScrollLayout mStackScrollLayout;
private PhoneStatusBarView mStatusBarView;
+ private PhoneStatusBarTransitions mBarTransitions;
private StatusBar mService;
private DragDownHelper mDragDownHelper;
private boolean mDoubleTapEnabled;
@@ -98,6 +100,7 @@
private boolean mExpandingBelowNotch;
private final DockManager mDockManager;
private final NotificationPanelViewController mNotificationPanelViewController;
+ private final SuperStatusBarViewFactory mStatusBarViewFactory;
// Used for determining view / touch intersection
private int[] mTempLocation = new int[2];
@@ -124,8 +127,9 @@
ShadeController shadeController,
DockManager dockManager,
@Nullable NotificationShadeWindowBlurController blurController,
- NotificationShadeWindowView statusBarWindowView,
- NotificationPanelViewController notificationPanelViewController) {
+ NotificationShadeWindowView notificationShadeWindowView,
+ NotificationPanelViewController notificationPanelViewController,
+ SuperStatusBarViewFactory statusBarViewFactory) {
mInjectionInflationController = injectionInflationController;
mCoordinator = coordinator;
mPulseExpansionHandler = pulseExpansionHandler;
@@ -141,11 +145,12 @@
mDozeLog = dozeLog;
mDozeParameters = dozeParameters;
mCommandQueue = commandQueue;
- mView = statusBarWindowView;
+ mView = notificationShadeWindowView;
mShadeController = shadeController;
mDockManager = dockManager;
mNotificationPanelViewController = notificationPanelViewController;
mBlurController = blurController;
+ mStatusBarViewFactory = statusBarViewFactory;
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror);
@@ -440,8 +445,18 @@
}
}
+ public PhoneStatusBarTransitions getBarTransitions() {
+ return mBarTransitions;
+ }
+
public void setStatusBarView(PhoneStatusBarView statusBarView) {
mStatusBarView = statusBarView;
+ if (statusBarView != null && mStatusBarViewFactory != null) {
+ mBarTransitions = new PhoneStatusBarTransitions(
+ statusBarView,
+ mStatusBarViewFactory.getStatusBarWindowView()
+ .findViewById(R.id.status_bar_container));
+ }
}
public void setService(StatusBar statusBar) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 1ab36c5..14af466 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -16,15 +16,18 @@
package com.android.systemui.statusbar.phone;
-import android.app.ActivityManager;
+import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
import android.app.AlarmManager.AlarmClockInfo;
+import android.app.IActivityManager;
import android.app.SynchronousUserSwitchObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
import android.media.AudioManager;
import android.os.Handler;
import android.os.RemoteException;
@@ -33,13 +36,13 @@
import android.provider.Settings.Global;
import android.service.notification.ZenModeConfig;
import android.telecom.TelecomManager;
-import android.telephony.TelephonyManager;
import android.text.format.DateFormat;
import android.util.Log;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
@@ -61,10 +64,13 @@
import com.android.systemui.statusbar.policy.SensorPrivacyController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.time.DateFormatUtil;
import java.util.Locale;
import java.util.concurrent.Executor;
+import javax.inject.Inject;
+
/**
* This class contains all of the policy about which icons are installed in the status bar at boot
* time. It goes through the normal API for icons, even though it probably strictly doesn't need to.
@@ -82,7 +88,7 @@
private static final String TAG = "PhoneStatusBarPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- public static final int LOCATION_STATUS_ICON_ID =
+ static final int LOCATION_STATUS_ICON_ID =
com.android.internal.R.drawable.perm_group_location;
private final String mSlotCast;
@@ -97,20 +103,26 @@
private final String mSlotHeadset;
private final String mSlotDataSaver;
private final String mSlotLocation;
- private final String mSlotMicrophone;
- private final String mSlotCamera;
private final String mSlotSensorsOff;
private final String mSlotScreenRecord;
+ private final int mDisplayId;
+ private final SharedPreferences mSharedPreferences;
+ private final DateFormatUtil mDateFormatUtil;
+ private final TelecomManager mTelecomManager;
+ private final AudioManager mAudioManager;
- private final Context mContext;
private final Handler mHandler = new Handler();
private final CastController mCast;
private final HotspotController mHotspot;
private final NextAlarmController mNextAlarmController;
private final AlarmManager mAlarmManager;
private final UserInfoController mUserInfoController;
+ private final IActivityManager mIActivityManager;
private final UserManager mUserManager;
private final StatusBarIconController mIconController;
+ private final CommandQueue mCommandQueue;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final Resources mResources;
private final RotationLockController mRotationLockController;
private final DataSaverController mDataSaver;
private final ZenModeController mZenController;
@@ -121,10 +133,6 @@
private final SensorPrivacyController mSensorPrivacyController;
private final RecordingController mRecordingController;
- // Assume it's all good unless we hear otherwise. We don't always seem
- // to get broadcasts that it *is* there.
- int mSimState = TelephonyManager.SIM_STATE_READY;
-
private boolean mZenVisible;
private boolean mVolumeVisible;
private boolean mCurrentUserSetup;
@@ -134,47 +142,70 @@
private BluetoothController mBluetooth;
private AlarmManager.AlarmClockInfo mNextAlarm;
- public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController,
+ @Inject
+ public PhoneStatusBarPolicy(StatusBarIconController iconController,
CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher,
- @UiBackground Executor uiBgExecutor) {
- mContext = context;
+ @UiBackground Executor uiBgExecutor, @Main Resources resources,
+ CastController castController, HotspotController hotspotController,
+ BluetoothController bluetoothController, NextAlarmController nextAlarmController,
+ UserInfoController userInfoController, RotationLockController rotationLockController,
+ DataSaverController dataSaverController, ZenModeController zenModeController,
+ DeviceProvisionedController deviceProvisionedController,
+ KeyguardStateController keyguardStateController,
+ LocationController locationController,
+ SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager,
+ AlarmManager alarmManager, UserManager userManager, AudioManager audioManager,
+ RecordingController recordingController,
+ @Nullable TelecomManager telecomManager, @DisplayId int displayId,
+ @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil) {
mIconController = iconController;
- mCast = Dependency.get(CastController.class);
- mHotspot = Dependency.get(HotspotController.class);
- mBluetooth = Dependency.get(BluetoothController.class);
- mNextAlarmController = Dependency.get(NextAlarmController.class);
- mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- mUserInfoController = Dependency.get(UserInfoController.class);
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mRotationLockController = Dependency.get(RotationLockController.class);
- mDataSaver = Dependency.get(DataSaverController.class);
- mZenController = Dependency.get(ZenModeController.class);
- mProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mKeyguardStateController = Dependency.get(KeyguardStateController.class);
- mLocationController = Dependency.get(LocationController.class);
- mSensorPrivacyController = Dependency.get(SensorPrivacyController.class);
- mRecordingController = Dependency.get(RecordingController.class);
+ mCommandQueue = commandQueue;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mResources = resources;
+ mCast = castController;
+ mHotspot = hotspotController;
+ mBluetooth = bluetoothController;
+ mNextAlarmController = nextAlarmController;
+ mAlarmManager = alarmManager;
+ mUserInfoController = userInfoController;
+ mIActivityManager = iActivityManager;
+ mUserManager = userManager;
+ mRotationLockController = rotationLockController;
+ mDataSaver = dataSaverController;
+ mZenController = zenModeController;
+ mProvisionedController = deviceProvisionedController;
+ mKeyguardStateController = keyguardStateController;
+ mLocationController = locationController;
+ mSensorPrivacyController = sensorPrivacyController;
+ mRecordingController = recordingController;
mUiBgExecutor = uiBgExecutor;
+ mAudioManager = audioManager;
+ mTelecomManager = telecomManager;
- mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
- mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
- mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth);
- mSlotTty = context.getString(com.android.internal.R.string.status_bar_tty);
- mSlotZen = context.getString(com.android.internal.R.string.status_bar_zen);
- mSlotVolume = context.getString(com.android.internal.R.string.status_bar_volume);
- mSlotAlarmClock = context.getString(com.android.internal.R.string.status_bar_alarm_clock);
- mSlotManagedProfile = context.getString(
+ mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast);
+ mSlotHotspot = resources.getString(com.android.internal.R.string.status_bar_hotspot);
+ mSlotBluetooth = resources.getString(com.android.internal.R.string.status_bar_bluetooth);
+ mSlotTty = resources.getString(com.android.internal.R.string.status_bar_tty);
+ mSlotZen = resources.getString(com.android.internal.R.string.status_bar_zen);
+ mSlotVolume = resources.getString(com.android.internal.R.string.status_bar_volume);
+ mSlotAlarmClock = resources.getString(com.android.internal.R.string.status_bar_alarm_clock);
+ mSlotManagedProfile = resources.getString(
com.android.internal.R.string.status_bar_managed_profile);
- mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate);
- mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
- mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
- mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
- mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone);
- mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera);
- mSlotSensorsOff = context.getString(com.android.internal.R.string.status_bar_sensors_off);
- mSlotScreenRecord = context.getString(
+ mSlotRotate = resources.getString(com.android.internal.R.string.status_bar_rotate);
+ mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset);
+ mSlotDataSaver = resources.getString(com.android.internal.R.string.status_bar_data_saver);
+ mSlotLocation = resources.getString(com.android.internal.R.string.status_bar_location);
+ mSlotSensorsOff = resources.getString(com.android.internal.R.string.status_bar_sensors_off);
+ mSlotScreenRecord = resources.getString(
com.android.internal.R.string.status_bar_screen_record);
+ mDisplayId = displayId;
+ mSharedPreferences = sharedPreferences;
+ mDateFormatUtil = dateFormatUtil;
+ }
+
+ /** Initialize the object after construction. */
+ public void init() {
// listen for broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
@@ -185,11 +216,11 @@
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- broadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
+ mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
// listen for user / profile change.
try {
- ActivityManager.getService().registerUserSwitchObserver(mUserSwitchListener, TAG);
+ mIActivityManager.registerUserSwitchObserver(mUserSwitchListener, TAG);
} catch (RemoteException e) {
// Ignore
}
@@ -219,26 +250,26 @@
// hotspot
mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot,
- mContext.getString(R.string.accessibility_status_bar_hotspot));
+ mResources.getString(R.string.accessibility_status_bar_hotspot));
mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());
// managed profile
mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,
- mContext.getString(R.string.accessibility_managed_profile));
+ mResources.getString(R.string.accessibility_managed_profile));
mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible);
// data saver
mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,
- context.getString(R.string.accessibility_data_saver_on));
+ mResources.getString(R.string.accessibility_data_saver_on));
mIconController.setIconVisibility(mSlotDataSaver, false);
mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
- mContext.getString(R.string.accessibility_location_active));
+ mResources.getString(R.string.accessibility_location_active));
mIconController.setIconVisibility(mSlotLocation, false);
// sensors off
mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off,
- mContext.getString(R.string.accessibility_sensors_off_active));
+ mResources.getString(R.string.accessibility_sensors_off_active));
mIconController.setIconVisibility(mSlotSensorsOff,
mSensorPrivacyController.isSensorPrivacyEnabled());
@@ -259,7 +290,7 @@
mLocationController.addCallback(this);
mRecordingController.addCallback(this);
- commandQueue.addCallback(this);
+ mCommandQueue.addCallback(this);
}
@Override
@@ -284,51 +315,17 @@
private String buildAlarmContentDescription() {
if (mNextAlarm == null) {
- return mContext.getString(R.string.status_bar_alarm);
+ return mResources.getString(R.string.status_bar_alarm);
}
- return formatNextAlarm(mNextAlarm, mContext);
- }
- private static String formatNextAlarm(AlarmManager.AlarmClockInfo info, Context context) {
- if (info == null) {
- return "";
- }
- String skeleton = DateFormat.is24HourFormat(
- context, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma";
+ String skeleton = mDateFormatUtil.is24HourFormat() ? "EHm" : "Ehma";
String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
- String dateString = DateFormat.format(pattern, info.getTriggerTime()).toString();
+ String dateString = DateFormat.format(pattern, mNextAlarm.getTriggerTime()).toString();
- return context.getString(R.string.accessibility_quick_settings_alarm, dateString);
- }
-
- private final void updateSimState(Intent intent) {
- String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
- if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) {
- mSimState = TelephonyManager.SIM_STATE_READY;
- } else if (Intent.SIM_STATE_CARD_IO_ERROR.equals(stateExtra)) {
- mSimState = TelephonyManager.SIM_STATE_CARD_IO_ERROR;
- } else if (Intent.SIM_STATE_CARD_RESTRICTED.equals(stateExtra)) {
- mSimState = TelephonyManager.SIM_STATE_CARD_RESTRICTED;
- } else if (Intent.SIM_STATE_READY.equals(stateExtra)) {
- mSimState = TelephonyManager.SIM_STATE_READY;
- } else if (Intent.SIM_STATE_LOCKED.equals(stateExtra)) {
- final String lockedReason =
- intent.getStringExtra(Intent.EXTRA_SIM_LOCKED_REASON);
- if (Intent.SIM_LOCKED_ON_PIN.equals(lockedReason)) {
- mSimState = TelephonyManager.SIM_STATE_PIN_REQUIRED;
- } else if (Intent.SIM_LOCKED_ON_PUK.equals(lockedReason)) {
- mSimState = TelephonyManager.SIM_STATE_PUK_REQUIRED;
- } else {
- mSimState = TelephonyManager.SIM_STATE_NETWORK_LOCKED;
- }
- } else {
- mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
- }
+ return mResources.getString(R.string.accessibility_quick_settings_alarm, dateString);
}
private final void updateVolumeZen() {
- AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-
boolean zenVisible = false;
int zenIconId = 0;
String zenDescription = null;
@@ -338,29 +335,29 @@
String volumeDescription = null;
int zen = mZenController.getZen();
- if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) {
+ if (DndTile.isVisible(mSharedPreferences) || DndTile.isCombinedIcon(mSharedPreferences)) {
zenVisible = zen != Global.ZEN_MODE_OFF;
zenIconId = R.drawable.stat_sys_dnd;
- zenDescription = mContext.getString(R.string.quick_settings_dnd_label);
+ zenDescription = mResources.getString(R.string.quick_settings_dnd_label);
} else if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
zenVisible = true;
zenIconId = R.drawable.stat_sys_dnd;
- zenDescription = mContext.getString(R.string.interruption_level_none);
+ zenDescription = mResources.getString(R.string.interruption_level_none);
} else if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
zenVisible = true;
zenIconId = R.drawable.stat_sys_dnd;
- zenDescription = mContext.getString(R.string.interruption_level_priority);
+ zenDescription = mResources.getString(R.string.interruption_level_priority);
}
if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConsolidatedPolicy())) {
- if (audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
+ if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
volumeVisible = true;
volumeIconId = R.drawable.stat_sys_ringer_vibrate;
- volumeDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
- } else if (audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
+ volumeDescription = mResources.getString(R.string.accessibility_ringer_vibrate);
+ } else if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
volumeVisible = true;
volumeIconId = R.drawable.stat_sys_ringer_silent;
- volumeDescription = mContext.getString(R.string.accessibility_ringer_silent);
+ volumeDescription = mResources.getString(R.string.accessibility_ringer_silent);
}
}
@@ -395,13 +392,13 @@
private final void updateBluetooth() {
int iconId = R.drawable.stat_sys_data_bluetooth_connected;
String contentDescription =
- mContext.getString(R.string.accessibility_quick_settings_bluetooth_on);
+ mResources.getString(R.string.accessibility_quick_settings_bluetooth_on);
boolean bluetoothVisible = false;
if (mBluetooth != null) {
if (mBluetooth.isBluetoothConnected()
&& (mBluetooth.isBluetoothAudioActive()
|| !mBluetooth.isBluetoothAudioProfileOnly())) {
- contentDescription = mContext.getString(
+ contentDescription = mResources.getString(
R.string.accessibility_bluetooth_connected);
bluetoothVisible = mBluetooth.isBluetoothEnabled();
}
@@ -412,12 +409,10 @@
}
private final void updateTTY() {
- TelecomManager telecomManager =
- (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
- if (telecomManager == null) {
+ if (mTelecomManager == null) {
updateTTY(TelecomManager.TTY_MODE_OFF);
} else {
- updateTTY(telecomManager.getCurrentTtyMode());
+ updateTTY(mTelecomManager.getCurrentTtyMode());
}
}
@@ -430,7 +425,7 @@
// TTY is on
if (DEBUG) Log.v(TAG, "updateTTY: set TTY on");
mIconController.setIcon(mSlotTty, R.drawable.stat_sys_tty_mode,
- mContext.getString(R.string.accessibility_tty_enabled));
+ mResources.getString(R.string.accessibility_tty_enabled));
mIconController.setIconVisibility(mSlotTty, true);
} else {
// TTY is off
@@ -452,7 +447,7 @@
mHandler.removeCallbacks(mRemoveCastIconRunnable);
if (isCasting && !mRecordingController.isRecording()) { // screen record has its own icon
mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast,
- mContext.getString(R.string.accessibility_casting));
+ mResources.getString(R.string.accessibility_casting));
mIconController.setIconVisibility(mSlotCast, true);
} else {
// don't turn off the screen-record icon for a few seconds, just to make sure the user
@@ -478,7 +473,7 @@
showIcon = true;
mIconController.setIcon(mSlotManagedProfile,
R.drawable.stat_sys_managed_profile_status,
- mContext.getString(R.string.accessibility_managed_profile));
+ mResources.getString(R.string.accessibility_managed_profile));
} else {
showIcon = false;
}
@@ -545,7 +540,7 @@
@Override
public void appTransitionStarting(int displayId, long startTime, long duration,
boolean forced) {
- if (mContext.getDisplayId() == displayId) {
+ if (mDisplayId == displayId) {
updateManagedProfile();
}
}
@@ -567,14 +562,14 @@
@Override
public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
boolean portrait = RotationLockTile.isCurrentOrientationLockPortrait(
- mRotationLockController, mContext);
+ mRotationLockController, mResources);
if (rotationLocked) {
if (portrait) {
mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_portrait,
- mContext.getString(R.string.accessibility_rotation_lock_on_portrait));
+ mResources.getString(R.string.accessibility_rotation_lock_on_portrait));
} else {
mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_landscape,
- mContext.getString(R.string.accessibility_rotation_lock_on_landscape));
+ mResources.getString(R.string.accessibility_rotation_lock_on_landscape));
}
mIconController.setIconVisibility(mSlotRotate, true);
} else {
@@ -586,7 +581,7 @@
boolean connected = intent.getIntExtra("state", 0) != 0;
boolean hasMic = intent.getIntExtra("microphone", 0) != 0;
if (connected) {
- String contentDescription = mContext.getString(hasMic
+ String contentDescription = mResources.getString(hasMic
? R.string.accessibility_status_bar_headset
: R.string.accessibility_status_bar_headphones);
mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.stat_sys_headset_mic
@@ -630,7 +625,6 @@
if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
break;
}
- updateSimState(intent);
break;
case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:
updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
index e8bc2f5..2052ee6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
@@ -29,23 +29,21 @@
private static final float ICON_ALPHA_WHEN_LIGHTS_OUT_BATTERY_CLOCK = 0.5f;
private static final float ICON_ALPHA_WHEN_LIGHTS_OUT_NON_BATTERY_CLOCK = 0;
- private final PhoneStatusBarView mView;
private final float mIconAlphaWhenOpaque;
- private View mLeftSide, mStatusIcons, mBattery, mClock;
+ private View mLeftSide, mStatusIcons, mBattery;
private Animator mCurrentAnimation;
- public PhoneStatusBarTransitions(PhoneStatusBarView view) {
- super(view, R.drawable.status_background);
- mView = view;
- final Resources res = mView.getContext().getResources();
+ /**
+ * @param backgroundView view to apply the background drawable
+ */
+ public PhoneStatusBarTransitions(PhoneStatusBarView statusBarView, View backgroundView) {
+ super(backgroundView, R.drawable.status_background);
+ final Resources res = statusBarView.getContext().getResources();
mIconAlphaWhenOpaque = res.getFraction(R.dimen.status_bar_icon_drawing_alpha, 1, 1);
- }
-
- public void init() {
- mLeftSide = mView.findViewById(R.id.status_bar_left_side);
- mStatusIcons = mView.findViewById(R.id.statusIcons);
- mBattery = mView.findViewById(R.id.battery);
+ mLeftSide = statusBarView.findViewById(R.id.status_bar_left_side);
+ mStatusIcons = statusBarView.findViewById(R.id.statusIcons);
+ mBattery = statusBarView.findViewById(R.id.battery);
applyModeBackground(-1, getMode(), false /*animate*/);
applyMode(getMode(), false /*animate*/);
}
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 b949e3a..1359f74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -53,7 +53,6 @@
StatusBar mBar;
boolean mIsFullyOpenedPanel = false;
- private final PhoneStatusBarTransitions mBarTransitions;
private ScrimController mScrimController;
private float mMinFraction;
private Runnable mHideExpandedRunnable = new Runnable() {
@@ -83,15 +82,9 @@
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
-
- mBarTransitions = new PhoneStatusBarTransitions(this);
mCommandQueue = Dependency.get(CommandQueue.class);
}
- public BarTransitions getBarTransitions() {
- return mBarTransitions;
- }
-
public void setBar(StatusBar bar) {
mBar = bar;
}
@@ -102,7 +95,6 @@
@Override
public void onFinishInflate() {
- mBarTransitions.init();
mBattery = findViewById(R.id.battery);
mCutoutSpace = findViewById(R.id.cutout_space_view);
mCenterIconSpace = findViewById(R.id.centered_icon_area);
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 0d3b09a..b620d17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -676,6 +676,7 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ PhoneStatusBarPolicy phoneStatusBarPolicy,
DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager) {
super(context);
@@ -751,6 +752,7 @@
mKeyguardDismissUtil = keyguardDismissUtil;
mExtensionController = extensionController;
mUserInfoControllerImpl = userInfoControllerImpl;
+ mIconPolicy = phoneStatusBarPolicy;
mDismissCallbackRegistry = dismissCallbackRegistry;
mBubbleExpandListener =
@@ -875,8 +877,7 @@
// end old BaseStatusBar.start().
// Lastly, call to the icon policy to install/update all the icons.
- mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController, mCommandQueue,
- mBroadcastDispatcher, mUiBgExecutor);
+ mIconPolicy.init();
mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
mKeyguardStateController.addCallback(this);
@@ -2305,13 +2306,14 @@
}
protected BarTransitions getStatusBarTransitions() {
- return mStatusBarView.getBarTransitions();
+ return mNotificationShadeWindowViewController.getBarTransitions();
}
void checkBarModes() {
if (mDemoMode) return;
- if (mStatusBarView != null) checkBarMode(mStatusBarMode, mStatusBarWindowState,
- getStatusBarTransitions());
+ if (mNotificationShadeWindowViewController != null) {
+ checkBarMode(mStatusBarMode, mStatusBarWindowState, getStatusBarTransitions());
+ }
mNavigationBarController.checkNavBarModes(mDisplayId);
mNoAnimationOnNextBarModeChange = false;
}
@@ -2329,8 +2331,9 @@
}
private void finishBarAnimations() {
- if (mStatusBarView != null) {
- mStatusBarView.getBarTransitions().finishAnimations();
+ if (mNotificationShadeWindowController != null
+ && mNotificationShadeWindowViewController.getBarTransitions() != null) {
+ mNotificationShadeWindowViewController.getBarTransitions().finishAnimations();
}
mNavigationBarController.finishBarAnimations(mDisplayId);
}
@@ -2396,12 +2399,11 @@
pw.print(" mDozing="); pw.println(mDozing);
pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported);
- if (mStatusBarView != null) {
- dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
- }
pw.println(" StatusBarWindowView: ");
if (mNotificationShadeWindowViewController != null) {
mNotificationShadeWindowViewController.dump(fd, pw, args);
+ dumpBarTransitions(pw, "PhoneStatusBarTransitions",
+ mNotificationShadeWindowViewController.getBarTransitions());
}
pw.println(" mMediaManager: ");
@@ -3004,8 +3006,10 @@
-1;
if (barMode != -1) {
boolean animate = true;
- if (mStatusBarView != null) {
- mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
+ if (mNotificationShadeWindowController != null
+ && mNotificationShadeWindowViewController.getBarTransitions() != null) {
+ mNotificationShadeWindowViewController.getBarTransitions().transitionTo(
+ barMode, animate);
}
mNavigationBarController.transitionTo(mDisplayId, barMode, animate);
}
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 e64f821..0a4fdc9 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
@@ -79,6 +79,7 @@
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -193,6 +194,7 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ PhoneStatusBarPolicy phoneStatusBarPolicy,
DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager) {
return new StatusBar(
@@ -269,6 +271,7 @@
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
+ phoneStatusBarPolicy,
dismissCallbackRegistry,
statusBarTouchableRegionManager);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index d090404..7d532a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -56,8 +56,9 @@
private int mHotspotState;
private volatile int mNumConnectedDevices;
- private volatile boolean mIsTetheringSupported;
- private volatile boolean mHasTetherableWifiRegexs;
+ // Assume tethering is available until told otherwise
+ private volatile boolean mIsTetheringSupported = true;
+ private volatile boolean mHasTetherableWifiRegexs = true;
private boolean mWaitingForTerminalState;
private TetheringManager.TetheringEventCallback mTetheringCallback =
@@ -97,6 +98,15 @@
new HandlerExecutor(backgroundHandler), mTetheringCallback);
}
+ /**
+ * Whether hotspot is currently supported.
+ *
+ * This will return {@code true} immediately on creation of the controller, but may be updated
+ * later. Callbacks from this controllers will notify if the state changes.
+ *
+ * @return {@code true} if hotspot is supported (or we haven't been told it's not)
+ * @see #addCallback
+ */
@Override
public boolean isHotspotSupported() {
return mIsTetheringSupported && mHasTetherableWifiRegexs
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 312c4ac..d29f4fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -31,14 +31,12 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-import android.os.AsyncTask;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.security.KeyChain;
-import android.security.KeyChain.KeyChainConnection;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
@@ -55,6 +53,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -85,7 +84,7 @@
private final DevicePolicyManager mDevicePolicyManager;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
- private final Handler mBgHandler;
+ private final Executor mBgExecutor;
@GuardedBy("mCallbacks")
private final ArrayList<SecurityControllerCallback> mCallbacks = new ArrayList<>();
@@ -101,16 +100,14 @@
/**
*/
@Inject
- public SecurityControllerImpl(Context context, @Background Handler bgHandler,
- BroadcastDispatcher broadcastDispatcher) {
- this(context, bgHandler, broadcastDispatcher, null);
- }
-
- public SecurityControllerImpl(Context context, Handler bgHandler,
- BroadcastDispatcher broadcastDispatcher, SecurityControllerCallback callback) {
+ public SecurityControllerImpl(
+ Context context,
+ @Background Handler bgHandler,
+ BroadcastDispatcher broadcastDispatcher,
+ @Background Executor bgExecutor
+ ) {
super(broadcastDispatcher);
mContext = context;
- mBgHandler = bgHandler;
mDevicePolicyManager = (DevicePolicyManager)
context.getSystemService(Context.DEVICE_POLICY_SERVICE);
mConnectivityManager = (ConnectivityManager)
@@ -118,10 +115,8 @@
mConnectivityManagerService = IConnectivityManager.Stub.asInterface(
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
mPackageManager = context.getPackageManager();
- mUserManager = (UserManager)
- context.getSystemService(Context.USER_SERVICE);
-
- addCallback(callback);
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mBgExecutor = bgExecutor;
IntentFilter filter = new IntentFilter();
filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
@@ -305,7 +300,23 @@
}
private void refreshCACerts(int userId) {
- new CACertLoader().execute(userId);
+ mBgExecutor.execute(() -> {
+ Pair<Integer, Boolean> idWithCert = null;
+ try (KeyChain.KeyChainConnection conn = KeyChain.bindAsUser(mContext,
+ UserHandle.of(userId))) {
+ boolean hasCACerts = !(conn.getService().getUserCaAliases().getList().isEmpty());
+ idWithCert = new Pair<Integer, Boolean>(userId, hasCACerts);
+ } catch (RemoteException | InterruptedException | AssertionError e) {
+ Log.i(TAG, "failed to get CA certs", e);
+ idWithCert = new Pair<Integer, Boolean>(userId, null);
+ } finally {
+ if (DEBUG) Log.d(TAG, "Refreshing CA Certs " + idWithCert);
+ if (idWithCert != null && idWithCert.second != null) {
+ mHasCACerts.put(idWithCert.first, idWithCert.second);
+ fireCallbacks();
+ }
+ }
+ });
}
private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) {
@@ -408,28 +419,4 @@
}
}
};
-
- protected class CACertLoader extends AsyncTask<Integer, Void, Pair<Integer, Boolean> > {
-
- @Override
- protected Pair<Integer, Boolean> doInBackground(Integer... userId) {
- try (KeyChainConnection conn = KeyChain.bindAsUser(mContext,
- UserHandle.of(userId[0]))) {
- boolean hasCACerts = !(conn.getService().getUserCaAliases().getList().isEmpty());
- return new Pair<Integer, Boolean>(userId[0], hasCACerts);
- } catch (RemoteException | InterruptedException | AssertionError e) {
- Log.i(TAG, "failed to get CA certs", e);
- return new Pair<Integer, Boolean>(userId[0], null);
- }
- }
-
- @Override
- protected void onPostExecute(Pair<Integer, Boolean> result) {
- if (DEBUG) Log.d(TAG, "onPostExecute " + result);
- if (result.second != null) {
- mHasCACerts.put(result.first, result.second);
- fireCallbacks();
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 86fe3008..311e8738 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -25,10 +25,10 @@
import android.util.KeyValueListParser;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.DeviceConfigProxy;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -62,10 +62,15 @@
private final Handler mHandler;
private final Context mContext;
+ private final DeviceConfigProxy mDeviceConfig;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@Inject
- public SmartReplyConstants(@Main Handler handler, Context context) {
+ public SmartReplyConstants(
+ @Main Handler handler,
+ Context context,
+ DeviceConfigProxy deviceConfig
+ ) {
mHandler = handler;
mContext = context;
final Resources resources = mContext.getResources();
@@ -86,31 +91,35 @@
mDefaultOnClickInitDelay = resources.getInteger(
R.integer.config_smart_replies_in_notifications_onclick_init_delay);
+ mDeviceConfig = deviceConfig;
registerDeviceConfigListener();
updateConstants();
}
private void registerDeviceConfigListener() {
- DeviceConfig.addOnPropertiesChangedListener(
+ mDeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI,
this::postToHandler,
- (properties) -> onDeviceConfigPropertiesChanged(properties.getNamespace()));
+ mOnPropertiesChangedListener);
}
private void postToHandler(Runnable r) {
this.mHandler.post(r);
}
- @VisibleForTesting
- void onDeviceConfigPropertiesChanged(String namespace) {
- if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
- Log.e(TAG, "Received update from DeviceConfig for unrelated namespace: "
- + namespace);
- return;
- }
-
- updateConstants();
- }
+ private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) {
+ Log.e(TAG,
+ "Received update from DeviceConfig for unrelated namespace: "
+ + properties.getNamespace());
+ return;
+ }
+ updateConstants();
+ }
+ };
private void updateConstants() {
synchronized (SmartReplyConstants.this) {
@@ -120,7 +129,7 @@
mRequiresTargetingP = readDeviceConfigBooleanOrDefaultIfEmpty(
SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P,
mDefaultRequiresP);
- mMaxSqueezeRemeasureAttempts = DeviceConfig.getInt(
+ mMaxSqueezeRemeasureAttempts = mDeviceConfig.getInt(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS,
mDefaultMaxSqueezeRemeasureAttempts);
@@ -130,24 +139,24 @@
mShowInHeadsUp = readDeviceConfigBooleanOrDefaultIfEmpty(
SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP,
mDefaultShowInHeadsUp);
- mMinNumSystemGeneratedReplies = DeviceConfig.getInt(
+ mMinNumSystemGeneratedReplies = mDeviceConfig.getInt(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES,
mDefaultMinNumSystemGeneratedReplies);
- mMaxNumActions = DeviceConfig.getInt(
+ mMaxNumActions = mDeviceConfig.getInt(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS,
mDefaultMaxNumActions);
- mOnClickInitDelay = DeviceConfig.getInt(
+ mOnClickInitDelay = mDeviceConfig.getInt(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_ONCLICK_INIT_DELAY,
mDefaultOnClickInitDelay);
}
}
- private static boolean readDeviceConfigBooleanOrDefaultIfEmpty(String propertyName,
+ private boolean readDeviceConfigBooleanOrDefaultIfEmpty(String propertyName,
boolean defaultValue) {
- String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI, propertyName);
+ String value = mDeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI, propertyName);
if (TextUtils.isEmpty(value)) {
return defaultValue;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 8bd0f2c..7c96386 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -88,15 +88,16 @@
boolean wifiVisible = mCurrentState.enabled
&& ((mCurrentState.connected && mCurrentState.inetCondition == 1)
|| !mHasMobileData || visibleWhenEnabled);
- String wifiDesc = wifiVisible ? mCurrentState.ssid : null;
+ String wifiDesc = mCurrentState.connected ? mCurrentState.ssid : null;
boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
String contentDescription = getTextIfExists(getContentDescription()).toString();
if (mCurrentState.inetCondition == 0) {
contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet));
}
IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
- IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(),
- contentDescription);
+ IconState qsIcon = new IconState(mCurrentState.connected,
+ mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
+ : getQsCurrentIconId(), contentDescription);
callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut,
wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index cfa2947..5f82118 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -126,9 +126,10 @@
/**
* Allow the media player to be shown in the QS area, controlled by 2 flags.
+ * On by default, but can be disabled by setting to 0
*/
public static boolean useQsMediaPlayer(Context context) {
- int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0);
+ int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 1);
return flag > 0;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index b5bede4..13ba1a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -40,12 +40,11 @@
*/
public class ProximitySensor {
private static final String TAG = "ProxSensor";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Sensor mSensor;
private final AsyncSensorManager mSensorManager;
- private final boolean mUsingBrightnessSensor;
- private final float mMaxRange;
+ private final float mThreshold;
private List<ProximitySensorListener> mListeners = new ArrayList<>();
private String mTag = null;
@VisibleForTesting ProximityEvent mLastEvent;
@@ -68,20 +67,27 @@
public ProximitySensor(@Main Resources resources,
AsyncSensorManager sensorManager) {
mSensorManager = sensorManager;
- Sensor sensor = findBrightnessSensor(resources);
+ Sensor sensor = findCustomProxSensor(resources);
+ float threshold = 0;
+ if (sensor != null) {
+ try {
+ threshold = getCustomProxThreshold(resources);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Can not load custom proximity sensor.", e);
+ sensor = null;
+ }
+ }
if (sensor == null) {
- mUsingBrightnessSensor = false;
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- } else {
- mUsingBrightnessSensor = true;
+ if (sensor != null) {
+ threshold = sensor.getMaximumRange();
+ }
}
+
+ mThreshold = threshold;
+
mSensor = sensor;
- if (mSensor != null) {
- mMaxRange = mSensor.getMaximumRange();
- } else {
- mMaxRange = 0;
- }
}
public void setTag(String tag) {
@@ -107,9 +113,15 @@
mPaused = false;
registerInternal();
}
+ /**
+ * Returns a brightness sensor that can be used for proximity purposes.
+ */
+ private Sensor findCustomProxSensor(Resources resources) {
+ String sensorType = resources.getString(R.string.proximity_sensor_type);
+ if (sensorType.isEmpty()) {
+ return null;
+ }
- private Sensor findBrightnessSensor(Resources resources) {
- String sensorType = resources.getString(R.string.doze_brightness_sensor_type);
List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
Sensor sensor = null;
for (Sensor s : sensorList) {
@@ -123,6 +135,17 @@
}
/**
+ * Returns a threshold value that can be used along with {@link #findCustomProxSensor}
+ */
+ private float getCustomProxThreshold(Resources resources) {
+ try {
+ return resources.getFloat(R.dimen.proximity_sensor_threshold);
+ } catch (Resources.NotFoundException e) {
+ throw new IllegalStateException("R.dimen.proximity_sensor_threshold must be set.");
+ }
+ }
+
+ /**
* Returns true if we are registered with the SensorManager.
*/
public boolean isRegistered() {
@@ -157,7 +180,6 @@
if (mRegistered || mPaused || mListeners.isEmpty()) {
return;
}
- logDebug("Using brightness sensor? " + mUsingBrightnessSensor);
logDebug("Registering sensor listener");
mRegistered = true;
mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
@@ -196,10 +218,7 @@
}
private void onSensorEvent(SensorEvent event) {
- boolean near = event.values[0] < mMaxRange;
- if (mUsingBrightnessSensor) {
- near = event.values[0] == 0;
- }
+ boolean near = event.values[0] < mThreshold;
mLastEvent = new ProximityEvent(near, event.timestamp);
alertListeners();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/time/DateFormatUtil.java b/packages/SystemUI/src/com/android/systemui/util/time/DateFormatUtil.java
new file mode 100644
index 0000000..d7c4e93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/time/DateFormatUtil.java
@@ -0,0 +1,40 @@
+/*
+ * 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.util.time;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.text.format.DateFormat;
+
+import javax.inject.Inject;
+
+/**
+ * Instantiable wrapper around {@link DateFormat}.
+ */
+public class DateFormatUtil {
+ private final Context mContext;
+
+ @Inject
+ public DateFormatUtil(Context context) {
+ mContext = context;
+ }
+
+ /** Returns true if the phone is in 24 hour format. */
+ public boolean is24HourFormat() {
+ return DateFormat.is24HourFormat(mContext, ActivityManager.getCurrentUser());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 689eed9..678cfd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -34,6 +34,7 @@
import android.graphics.ColorSpace;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
+import android.os.Handler;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -72,6 +73,8 @@
private Bitmap mWallpaperBitmap;
@Mock
private DozeParameters mDozeParam;
+ @Mock
+ private Handler mHandler;
private CountDownLatch mEventCountdown;
@@ -104,7 +107,7 @@
return new ImageWallpaper(mDozeParam) {
@Override
public Engine onCreateEngine() {
- return new GLEngine(mMockContext, mDozeParam) {
+ return new GLEngine(mDozeParam, mHandler) {
@Override
public Context getDisplayContext() {
return mMockContext;
@@ -196,5 +199,6 @@
when(mSurfaceHolder.getSurfaceFrame()).thenReturn(frame);
assertThat(engineSpy.checkIfShouldStopTransition()).isEqualTo(assertion);
+ // destroy
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index a974c6d..1b34b3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -456,4 +456,52 @@
assertThat(rectsToRegion(Collections.singletonList(rect)).getBounds(), is(rect));
}
+ @Test
+ public void testRegistration_From_NoOverlay_To_HasOverlays() {
+ doReturn(false).when(mScreenDecorations).hasOverlays();
+ mScreenDecorations.start();
+ verify(mTunerService, times(0)).addTunable(any(), any());
+ verify(mTunerService, times(1)).removeTunable(any());
+ assertThat(mScreenDecorations.mIsRegistered, is(false));
+ reset(mTunerService);
+
+ doReturn(true).when(mScreenDecorations).hasOverlays();
+ mScreenDecorations.onConfigurationChanged(new Configuration());
+ verify(mTunerService, times(1)).addTunable(any(), any());
+ verify(mTunerService, times(0)).removeTunable(any());
+ assertThat(mScreenDecorations.mIsRegistered, is(true));
+ }
+
+ @Test
+ public void testRegistration_From_HasOverlays_To_HasOverlays() {
+ doReturn(true).when(mScreenDecorations).hasOverlays();
+
+ mScreenDecorations.start();
+ verify(mTunerService, times(1)).addTunable(any(), any());
+ verify(mTunerService, times(0)).removeTunable(any());
+ assertThat(mScreenDecorations.mIsRegistered, is(true));
+ reset(mTunerService);
+
+ mScreenDecorations.onConfigurationChanged(new Configuration());
+ verify(mTunerService, times(0)).addTunable(any(), any());
+ verify(mTunerService, times(0)).removeTunable(any());
+ assertThat(mScreenDecorations.mIsRegistered, is(true));
+ }
+
+ @Test
+ public void testRegistration_From_HasOverlays_To_NoOverlay() {
+ doReturn(true).when(mScreenDecorations).hasOverlays();
+
+ mScreenDecorations.start();
+ verify(mTunerService, times(1)).addTunable(any(), any());
+ verify(mTunerService, times(0)).removeTunable(any());
+ assertThat(mScreenDecorations.mIsRegistered, is(true));
+ reset(mTunerService);
+
+ doReturn(false).when(mScreenDecorations).hasOverlays();
+ mScreenDecorations.onConfigurationChanged(new Configuration());
+ verify(mTunerService, times(0)).addTunable(any(), any());
+ verify(mTunerService, times(1)).removeTunable(any());
+ assertThat(mScreenDecorations.mIsRegistered, is(false));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 7ac5443..a36f2c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -86,7 +86,9 @@
public void SysuiTeardown() {
InstrumentationRegistry.registerInstance(mRealInstrumentation,
InstrumentationRegistry.getArguments());
- // Reset the assert's testable looper to null.
+ if (TestableLooper.get(this) != null) {
+ TestableLooper.get(this).processAllMessages();
+ }
disallowTestableLooperAsMainThread();
SystemUIFactory.cleanup();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
index 545d2d4..5b78067 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
@@ -33,7 +33,9 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -63,6 +65,7 @@
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private DockManager mDockManager = new DockManagerFake();
+ private StatusBarStateController mStatusBarStateController = new StatusBarStateControllerImpl();
@Before
public void setup() {
@@ -83,7 +86,7 @@
public void test_brightLineFalsingManagerDisabled() {
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
- mDumpManager, mUiBgExecutor);
+ mDumpManager, mUiBgExecutor, mStatusBarStateController);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
}
@@ -94,7 +97,7 @@
mExecutor.runAllReady();
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
- mDumpManager, mUiBgExecutor);
+ mDumpManager, mUiBgExecutor, mStatusBarStateController);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
}
@@ -102,7 +105,7 @@
public void test_brightLineFalsingManagerToggled() throws InterruptedException {
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
- mDumpManager, mUiBgExecutor);
+ mDumpManager, mUiBgExecutor, mStatusBarStateController);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
index 0aaa3b6..8b5cc9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
@@ -22,12 +22,16 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.util.DisplayMetrics;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.sensors.ProximitySensor;
@@ -40,6 +44,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class BrightLineFalsingManagerTest extends SysuiTestCase {
@@ -47,6 +52,7 @@
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private ProximitySensor mProximitySensor;
+ private SysuiStatusBarStateController mStatusBarStateController;
private BrightLineFalsingManager mFalsingManager;
@@ -61,8 +67,11 @@
FalsingDataProvider falsingDataProvider = new FalsingDataProvider(dm);
DeviceConfigProxy deviceConfigProxy = new DeviceConfigProxyFake();
DockManager dockManager = new DockManagerFake();
+ mStatusBarStateController = new StatusBarStateControllerImpl();
+ mStatusBarStateController.setState(StatusBarState.KEYGUARD);
mFalsingManager = new BrightLineFalsingManager(falsingDataProvider,
- mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager);
+ mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager,
+ mStatusBarStateController);
}
@Test
@@ -98,4 +107,12 @@
mFalsingManager.onBouncerHidden();
verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class));
}
+
+ @Test
+ public void testUnregisterSensor_StateTransition() {
+ mFalsingManager.onScreenTurningOn();
+ reset(mProximitySensor);
+ mStatusBarStateController.setState(StatusBarState.SHADE);
+ verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
new file mode 100644
index 0000000..68e1ec1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
@@ -0,0 +1,192 @@
+/*
+ * 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.controls.management
+
+import android.app.PendingIntent
+import android.service.controls.Control
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlStatus
+import com.android.systemui.controls.controller.ControlInfo
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class AllModelTest : SysuiTestCase() {
+
+ companion object {
+ private const val EMPTY_STRING = "Other"
+ }
+
+ @Mock
+ lateinit var pendingIntent: PendingIntent
+
+ val idPrefix = "controlId"
+ val favoritesIndices = listOf(7, 3, 1, 9)
+ val favoritesList = favoritesIndices.map { "controlId$it" }
+ lateinit var controls: List<ControlStatus>
+
+ lateinit var model: AllModel
+
+ private fun zoneMap(id: Int): String? {
+ return when (id) {
+ 10 -> ""
+ 11 -> null
+ else -> ((id + 1) % 3).toString()
+ }
+ }
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ // controlId0 --> zone = 1
+ // controlId1 --> zone = 2, favorite
+ // controlId2 --> zone = 0
+ // controlId3 --> zone = 1, favorite
+ // controlId4 --> zone = 2
+ // controlId5 --> zone = 0
+ // controlId6 --> zone = 1
+ // controlId7 --> zone = 2, favorite
+ // controlId8 --> zone = 0
+ // controlId9 --> zone = 1, favorite
+ // controlId10 --> zone = ""
+ // controlId11 --> zone = null
+ controls = (0..11).map {
+ ControlStatus(
+ Control.StatelessBuilder("$idPrefix$it", pendingIntent)
+ .setZone(zoneMap(it))
+ .build(),
+ it in favoritesIndices
+ )
+ }
+ model = AllModel(controls, favoritesList, EMPTY_STRING)
+ }
+
+ @Test
+ fun testElements() {
+
+ // Zones are sorted by order of appearance, with empty at the end with special header.
+ val expected = listOf(
+ ZoneNameWrapper("1"),
+ ControlWrapper(controls[0]),
+ ControlWrapper(controls[3]),
+ ControlWrapper(controls[6]),
+ ControlWrapper(controls[9]),
+ ZoneNameWrapper("2"),
+ ControlWrapper(controls[1]),
+ ControlWrapper(controls[4]),
+ ControlWrapper(controls[7]),
+ ZoneNameWrapper("0"),
+ ControlWrapper(controls[2]),
+ ControlWrapper(controls[5]),
+ ControlWrapper(controls[8]),
+ ZoneNameWrapper(EMPTY_STRING),
+ ControlWrapper(controls[10]),
+ ControlWrapper(controls[11])
+ )
+ expected.zip(model.elements).forEachIndexed { index, it ->
+ assertEquals("Error in item at index $index", it.first, it.second)
+ }
+ }
+
+ private fun sameControl(controlInfo: ControlInfo.Builder, control: Control): Boolean {
+ return controlInfo.controlId == control.controlId &&
+ controlInfo.controlTitle == control.title &&
+ controlInfo.deviceType == control.deviceType
+ }
+
+ @Test
+ fun testAllEmpty_noHeader() {
+ val selected_controls = listOf(controls[10], controls[11])
+ val new_model = AllModel(selected_controls, emptyList(), EMPTY_STRING)
+ val expected = listOf(
+ ControlWrapper(controls[10]),
+ ControlWrapper(controls[11])
+ )
+
+ expected.zip(new_model.elements).forEachIndexed { index, it ->
+ assertEquals("Error in item at index $index", it.first, it.second)
+ }
+ }
+
+ @Test
+ fun testFavorites() {
+ val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
+ model.favorites.zip(expectedFavorites).forEach {
+ assertTrue(sameControl(it.first, it.second))
+ }
+ }
+
+ @Test
+ fun testAddFavorite() {
+ val indexToAdd = 6
+ model.changeFavoriteStatus("$idPrefix$indexToAdd", true)
+
+ val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control) +
+ controls[indexToAdd].control
+
+ model.favorites.zip(expectedFavorites).forEach {
+ assertTrue(sameControl(it.first, it.second))
+ }
+ }
+
+ @Test
+ fun testAddFavorite_alreadyThere() {
+ val indexToAdd = 7
+ model.changeFavoriteStatus("$idPrefix$indexToAdd", true)
+
+ val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
+
+ model.favorites.zip(expectedFavorites).forEach {
+ assertTrue(sameControl(it.first, it.second))
+ }
+ }
+
+ @Test
+ fun testRemoveFavorite() {
+ val indexToRemove = 3
+ model.changeFavoriteStatus("$idPrefix$indexToRemove", false)
+
+ val expectedFavorites = (favoritesIndices.filterNot { it == indexToRemove })
+ .map(controls::get)
+ .map(ControlStatus::control)
+
+ model.favorites.zip(expectedFavorites).forEach {
+ assertTrue(sameControl(it.first, it.second))
+ }
+ }
+
+ @Test
+ fun testRemoveFavorite_notThere() {
+ val indexToRemove = 4
+ model.changeFavoriteStatus("$idPrefix$indexToRemove", false)
+
+ val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
+
+ model.favorites.zip(expectedFavorites).forEach {
+ assertTrue(sameControl(it.first, it.second))
+ }
+ }
+}
\ No newline at end of file
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 9ef5520..8320b05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -44,6 +44,8 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+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;
@@ -69,6 +71,7 @@
private @Mock DumpManager mDumpManager;
private @Mock PowerManager mPowerManager;
private @Mock TrustManager mTrustManager;
+ private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private FalsingManagerFake mFalsingManager;
@@ -85,7 +88,7 @@
mContext, mFalsingManager, mLockPatternUtils, mBroadcastDispatcher,
mNotificationShadeWindowController, () -> mStatusBarKeyguardViewManager,
mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor,
- mPowerManager, mTrustManager);
+ mPowerManager, mTrustManager, mDeviceConfig);
mViewMediator.start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
new file mode 100644
index 0000000..6c09a46
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.pip;
+
+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.eq;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+
+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;
+
+/**
+ * Unit tests against {@link PipAnimationController} to ensure that it sends the right callbacks
+ * depending on the various interactions.
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipAnimationControllerTest extends SysuiTestCase {
+
+ private PipAnimationController mPipAnimationController;
+
+ @Mock
+ private IWindowContainer mWindowContainer;
+
+ @Mock
+ private PipAnimationController.PipAnimationCallback mPipAnimationCallback;
+
+ @Before
+ public void setUp() throws Exception {
+ mPipAnimationController = new PipAnimationController(mContext);
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void getAnimator_withAlpha_returnFloatAnimator() {
+ final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+ .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+ new Rect(), 0f, 1f);
+
+ assertEquals("Expect ANIM_TYPE_ALPHA animation",
+ animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA);
+ }
+
+ @Test
+ public void getAnimator_withBounds_returnBoundsAnimator() {
+ final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+ .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+ new Rect(), new Rect());
+
+ assertEquals("Expect ANIM_TYPE_BOUNDS animation",
+ animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
+ }
+
+ @Test
+ public void getAnimator_whenSameTypeRunning_updateExistingAnimator() {
+ final Rect startValue = new Rect(0, 0, 100, 100);
+ final Rect endValue1 = new Rect(100, 100, 200, 200);
+ final Rect endValue2 = new Rect(200, 200, 300, 300);
+ final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
+ .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+ startValue, endValue1);
+ oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
+ oldAnimator.start();
+
+ final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
+ .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+ startValue, endValue2);
+
+ assertEquals("getAnimator with same type returns same animator",
+ oldAnimator, newAnimator);
+ assertEquals("getAnimator with same type updates end value",
+ endValue2, newAnimator.getEndValue());
+ }
+
+ @Test
+ public void getAnimator_scheduleFinishPip() {
+ PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+ .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+ new Rect(), 0f, 1f);
+ assertTrue("scheduleFinishPip is true", animator.shouldScheduleFinishPip());
+
+ animator = mPipAnimationController
+ .getAnimator(mWindowContainer, false /* scheduleFinishPip */,
+ new Rect(), 0f, 1f);
+ assertFalse("scheduleFinishPip is false", animator.shouldScheduleFinishPip());
+ }
+
+ @Test
+ public void pipTransitionAnimator_updateEndValue() {
+ final Rect startValue = new Rect(0, 0, 100, 100);
+ final Rect endValue1 = new Rect(100, 100, 200, 200);
+ final Rect endValue2 = new Rect(200, 200, 300, 300);
+ final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+ .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+ startValue, endValue1);
+
+ animator.updateEndValue(endValue2);
+
+ assertEquals("updateEndValue updates end value", animator.getEndValue(), endValue2);
+ }
+
+ @Test
+ public void pipTransitionAnimator_setPipAnimationCallback() {
+ final Rect startValue = new Rect(0, 0, 100, 100);
+ final Rect endValue = new Rect(100, 100, 200, 200);
+ final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+ .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+ startValue, endValue);
+ animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
+
+ animator.setPipAnimationCallback(mPipAnimationCallback);
+
+ // onAnimationStart triggers onPipAnimationStart
+ animator.onAnimationStart(animator);
+ verify(mPipAnimationCallback).onPipAnimationStart(mWindowContainer, animator);
+
+ // onAnimationCancel triggers onPipAnimationCancel
+ animator.onAnimationCancel(animator);
+ verify(mPipAnimationCallback).onPipAnimationCancel(mWindowContainer, animator);
+
+ // onAnimationEnd triggers onPipAnimationEnd
+ animator.onAnimationEnd(animator);
+ verify(mPipAnimationCallback).onPipAnimationEnd(eq(mWindowContainer),
+ any(SurfaceControl.Transaction.class), eq(animator));
+ }
+
+ /**
+ * A dummy {@link SurfaceControl.Transaction} class.
+ * This is created as {@link Mock} does not support method chaining.
+ */
+ private static class DummySurfaceControlTx extends SurfaceControl.Transaction {
+ @Override
+ public SurfaceControl.Transaction setAlpha(SurfaceControl leash, float alpha) {
+ return this;
+ }
+
+ @Override
+ public SurfaceControl.Transaction setPosition(SurfaceControl leash, float x, float y) {
+ return this;
+ }
+
+ @Override
+ public SurfaceControl.Transaction setWindowCrop(SurfaceControl leash, int w, int h) {
+ return this;
+ }
+
+ @Override
+ public void apply() {}
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index bc3ce8b..1dbcf10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -16,13 +16,7 @@
package com.android.systemui.pip;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
import android.content.ComponentName;
import android.graphics.Rect;
@@ -31,7 +25,6 @@
import android.testing.TestableResources;
import android.view.DisplayInfo;
import android.view.Gravity;
-import android.view.IPinnedStackController;
import androidx.test.filters.SmallTest;
@@ -40,9 +33,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
/**
* Unit tests against {@link PipBoundsHandler}, including but not limited to:
@@ -55,22 +45,18 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class PipBoundsHandlerTest extends SysuiTestCase {
private static final int ROUNDING_ERROR_MARGIN = 10;
+ private static final float DEFAULT_ASPECT_RATIO = 1f;
+ private static final Rect EMPTY_CURRENT_BOUNDS = null;
private PipBoundsHandler mPipBoundsHandler;
private DisplayInfo mDefaultDisplayInfo;
- private Rect mDefaultDisplayRect;
-
- @Mock
- private IPinnedStackController mPinnedStackController;
@Before
public void setUp() throws Exception {
mPipBoundsHandler = new PipBoundsHandler(mContext);
- MockitoAnnotations.initMocks(this);
initializeMockResources();
mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo);
- mPipBoundsHandler.setPinnedStackController(mPinnedStackController);
}
private void initializeMockResources() {
@@ -94,142 +80,80 @@
mDefaultDisplayInfo.displayId = 1;
mDefaultDisplayInfo.logicalWidth = 1000;
mDefaultDisplayInfo.logicalHeight = 1500;
- mDefaultDisplayRect = new Rect(0, 0,
- mDefaultDisplayInfo.logicalWidth, mDefaultDisplayInfo.logicalHeight);
}
@Test
- public void setShelfHeight_offsetBounds() throws Exception {
- final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+ public void setShelfHeight_offsetBounds() {
final int shelfHeight = 100;
-
- mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
- verify(mPinnedStackController).startAnimation(
- destinationBounds.capture(), isNull(), anyInt());
- final Rect lastPosition = destinationBounds.getValue();
- // Reset the pinned stack controller since we will do another verify later on
- reset(mPinnedStackController);
+ final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
mPipBoundsHandler.setShelfHeight(true, shelfHeight);
- mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+ final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
- verify(mPinnedStackController).startAnimation(
- destinationBounds.capture(), isNull(), anyInt());
- lastPosition.offset(0, -shelfHeight);
- assertBoundsWithMargin("PiP bounds offset by shelf height",
- lastPosition, destinationBounds.getValue());
+ oldPosition.offset(0, -shelfHeight);
+ assertBoundsWithMargin("PiP bounds offset by shelf height", oldPosition, newPosition);
}
@Test
- public void onImeVisibilityChanged_offsetBounds() throws Exception {
- final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+ public void onImeVisibilityChanged_offsetBounds() {
final int imeHeight = 100;
-
- mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
- verify(mPinnedStackController).startAnimation(
- destinationBounds.capture(), isNull(), anyInt());
- final Rect lastPosition = destinationBounds.getValue();
- // Reset the pinned stack controller since we will do another verify later on
- reset(mPinnedStackController);
+ final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
- mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+ final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
- verify(mPinnedStackController).startAnimation(
- destinationBounds.capture(), isNull(), anyInt());
- lastPosition.offset(0, -imeHeight);
- assertBoundsWithMargin("PiP bounds offset by IME height",
- lastPosition, destinationBounds.getValue());
+ oldPosition.offset(0, -imeHeight);
+ assertBoundsWithMargin("PiP bounds offset by IME height", oldPosition, newPosition);
}
@Test
- public void onPrepareAnimation_startAnimation() throws Exception {
- final Rect sourceRectHint = new Rect(100, 100, 200, 200);
- final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
-
- mPipBoundsHandler.onPrepareAnimation(sourceRectHint, 1f, null);
-
- verify(mPinnedStackController).startAnimation(
- destinationBounds.capture(), eq(sourceRectHint), anyInt());
- final Rect capturedDestinationBounds = destinationBounds.getValue();
- assertFalse("Destination bounds is not empty",
- capturedDestinationBounds.isEmpty());
- assertBoundsWithMargin("Destination bounds within Display",
- mDefaultDisplayRect, capturedDestinationBounds);
- }
-
- @Test
- public void onSaveReentryBounds_restoreLastPosition() throws Exception {
+ public void onSaveReentryBounds_restoreLastPosition() {
final ComponentName componentName = new ComponentName(mContext, "component1");
- final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+ final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
- mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+ oldPosition.offset(0, -100);
+ mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition);
- verify(mPinnedStackController).startAnimation(
- destinationBounds.capture(), isNull(), anyInt());
- final Rect lastPosition = destinationBounds.getValue();
- lastPosition.offset(0, -100);
- mPipBoundsHandler.onSaveReentryBounds(componentName, lastPosition);
- // Reset the pinned stack controller since we will do another verify later on
- reset(mPinnedStackController);
+ final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
- mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
- verify(mPinnedStackController).startAnimation(
- destinationBounds.capture(), isNull(), anyInt());
- assertBoundsWithMargin("Last position is restored",
- lastPosition, destinationBounds.getValue());
+ assertBoundsWithMargin("Last position is restored", oldPosition, newPosition);
}
@Test
- public void onResetReentryBounds_componentMatch_useDefaultBounds() throws Exception {
+ public void onResetReentryBounds_componentMatch_useDefaultBounds() {
final ComponentName componentName = new ComponentName(mContext, "component1");
- final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
-
- mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
- verify(mPinnedStackController).startAnimation(
- destinationBounds.capture(), isNull(), anyInt());
- final Rect defaultBounds = new Rect(destinationBounds.getValue());
+ final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
final Rect newBounds = new Rect(defaultBounds);
newBounds.offset(0, -100);
mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
- // Reset the pinned stack controller since we will do another verify later on
- reset(mPinnedStackController);
mPipBoundsHandler.onResetReentryBounds(componentName);
- mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+ final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
- verify(mPinnedStackController).startAnimation(
- destinationBounds.capture(), isNull(), anyInt());
- final Rect actualBounds = destinationBounds.getValue();
assertBoundsWithMargin("Use default bounds", defaultBounds, actualBounds);
}
@Test
- public void onResetReentryBounds_componentMismatch_restoreLastPosition() throws Exception {
+ public void onResetReentryBounds_componentMismatch_restoreLastPosition() {
final ComponentName componentName = new ComponentName(mContext, "component1");
- final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
-
- mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
- verify(mPinnedStackController).startAnimation(
- destinationBounds.capture(), isNull(), anyInt());
- final Rect defaultBounds = new Rect(destinationBounds.getValue());
+ final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
final Rect newBounds = new Rect(defaultBounds);
newBounds.offset(0, -100);
mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
- // Reset the pinned stack controller since we will do another verify later on
- reset(mPinnedStackController);
mPipBoundsHandler.onResetReentryBounds(new ComponentName(mContext, "component2"));
- mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+ final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
- verify(mPinnedStackController).startAnimation(
- destinationBounds.capture(), isNull(), anyInt());
- final Rect actualBounds = destinationBounds.getValue();
assertBoundsWithMargin("Last position is restored", newBounds, actualBounds);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 8e87e0a..cc5f149 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -41,11 +41,11 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.DynamicChildBindController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -106,17 +106,14 @@
mock(KeyguardBypassController.class),
mock(BubbleController.class),
mock(DynamicPrivacyController.class),
- mock(ForegroundServiceSectionController.class));
+ mock(ForegroundServiceSectionController.class),
+ mock(DynamicChildBindController.class));
mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
}
private NotificationEntry createEntry() throws Exception {
ExpandableNotificationRow row = mHelper.createRow();
- NotificationEntry entry = new NotificationEntryBuilder()
- .setSbn(row.getEntry().getSbn())
- .build();
- entry.setRow(row);
- return entry;
+ return row.getEntry();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
new file mode 100644
index 0000000..bf2d598
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.notification;
+
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.ArrayMap;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+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.notification.row.RowContentBindParams;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DynamicChildBindControllerTest extends SysuiTestCase {
+
+ private DynamicChildBindController mDynamicChildBindController;
+ private Map<NotificationEntry, List<NotificationEntry>> mGroupNotifs = new ArrayMap<>();
+ private static final int TEST_CHILD_BIND_CUTOFF = 5;
+
+ @Mock private RowContentBindStage mBindStage;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ allowTestableLooperAsMainThread();
+ when(mBindStage.getStageParams(any())).thenReturn(new RowContentBindParams());
+ mDynamicChildBindController =
+ new DynamicChildBindController(mBindStage, TEST_CHILD_BIND_CUTOFF);
+ }
+
+ @Test
+ public void testContentViewsOfChildrenBeyondCutoffAreFreed() {
+ // GIVEN a group notification with one view beyond the cutoff with content bound
+ NotificationEntry summary = addGroup(TEST_CHILD_BIND_CUTOFF + 1);
+ NotificationEntry lastChild = mGroupNotifs.get(summary).get(TEST_CHILD_BIND_CUTOFF);
+
+ RowContentBindParams bindParams = mock(RowContentBindParams.class);
+ when(mBindStage.getStageParams(lastChild)).thenReturn(bindParams);
+
+ // WHEN the controller gets the list
+ mDynamicChildBindController.updateChildContentViews(mGroupNotifs);
+
+ // THEN we free content views
+ verify(bindParams).freeContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
+ verify(bindParams).freeContentViews(FLAG_CONTENT_VIEW_EXPANDED);
+ verify(mBindStage).requestRebind(eq(lastChild), any());
+ }
+
+ @Test
+ public void testContentViewsBeforeCutoffAreBound() {
+ // GIVEN a group notification with one view before the cutoff with content unbound
+ NotificationEntry summary = addGroup(TEST_CHILD_BIND_CUTOFF);
+ NotificationEntry lastChild = mGroupNotifs.get(summary).get(TEST_CHILD_BIND_CUTOFF - 1);
+
+ lastChild.getRow().getPrivateLayout().setContractedChild(null);
+ lastChild.getRow().getPrivateLayout().setExpandedChild(null);
+
+ RowContentBindParams bindParams = mock(RowContentBindParams.class);
+ when(mBindStage.getStageParams(lastChild)).thenReturn(bindParams);
+
+ // WHEN the controller gets the list
+ mDynamicChildBindController.updateChildContentViews(mGroupNotifs);
+
+ // THEN we bind content views
+ verify(bindParams).requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
+ verify(bindParams).requireContentViews(FLAG_CONTENT_VIEW_EXPANDED);
+ verify(mBindStage).requestRebind(eq(lastChild), any());
+ }
+
+ private NotificationEntry addGroup(int size) {
+ NotificationEntry summary = new NotificationEntryBuilder().build();
+ summary.setRow(createRow());
+ ArrayList<NotificationEntry> children = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ NotificationEntry child = new NotificationEntryBuilder().build();
+ child.setRow(createRow());
+ children.add(child);
+ }
+ mGroupNotifs.put(summary, children);
+ return summary;
+ }
+
+ private ExpandableNotificationRow createRow() {
+ ExpandableNotificationRow row = (ExpandableNotificationRow)
+ LayoutInflater.from(mContext).inflate(R.layout.status_bar_notification_row, null);
+ row.getPrivateLayout().setContractedChild(new View(mContext));
+ row.getPrivateLayout().setExpandedChild(new View(mContext));
+ return row;
+ }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index f11c42b..f37836c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -208,7 +208,7 @@
.setTag("tag")
.setNotification(aN)
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.getUser())
+ .setUser(mContext.user)
.setOverrideGroupKey("")
.build()
whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking))
@@ -226,12 +226,12 @@
.setTag("tag")
.setNotification(bN)
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.getUser())
+ .setUser(mContext.user)
.setOverrideGroupKey("")
.build()
- whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking))
- .thenReturn(false)
- whenever(personNotificationIdentifier.isPeopleNotification(a.sbn, a.ranking))
+ whenever(personNotificationIdentifier.isImportantPeopleNotification(b.sbn, b.ranking))
+ .thenReturn(true)
+ whenever(personNotificationIdentifier.isPeopleNotification(b.sbn, b.ranking))
.thenReturn(true)
assertEquals(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
index 69e4f22..18ea774 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
@@ -25,6 +25,7 @@
import android.media.MediaMetadata;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
@@ -66,6 +67,9 @@
allowTestableLooperAsMainThread();
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+
+ // These tests are for regular media style notifications, not controls in quick settings
+ Settings.System.putInt(mContext.getContentResolver(), "qs_media_player", 0);
}
private void makeTestNotification(long duration, boolean allowSeeking) throws Exception {
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 e917c93..c5b6969 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
@@ -37,8 +37,9 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -81,6 +82,7 @@
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock private NotificationShadeWindowBlurController mNotificationShadeWindowBlurController;
+ @Mock private SuperStatusBarViewFactory mStatusBarViewFactory;
@Before
public void setUp() {
@@ -116,7 +118,8 @@
mDockManager,
mNotificationShadeWindowBlurController,
mView,
- mNotificationPanelViewController);
+ mNotificationPanelViewController,
+ mStatusBarViewFactory);
mController.setupExpandedStatusBar();
mController.setService(mStatusBar);
mController.setDragDownHelper(mDragDownHelper);
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 d81b8c2..5253e2ca 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
@@ -247,6 +247,7 @@
@Mock private KeyguardDismissUtil mKeyguardDismissUtil;
@Mock private ExtensionController mExtensionController;
@Mock private UserInfoControllerImpl mUserInfoControllerImpl;
+ @Mock private PhoneStatusBarPolicy mPhoneStatusBarPolicy;
private ShadeController mShadeController;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private InitController mInitController = new InitController();
@@ -400,6 +401,7 @@
mKeyguardDismissUtil,
mExtensionController,
mUserInfoControllerImpl,
+ mPhoneStatusBarPolicy,
mDismissCallbackRegistry,
mStatusBarTouchableRegionManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
index cd91f22..5771472 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.policy;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -140,13 +139,16 @@
}
@Test
- public void testDefault_hotspotNotSupported() {
- assertFalse(mController.isHotspotSupported());
+ public void testHotspotSupported_default() {
+ assertTrue(mController.isHotspotSupported());
}
@Test
public void testHotspotSupported_rightConditions() {
mTetheringCallbackCaptor.getValue().onTetheringSupported(true);
+
+ assertTrue(mController.isHotspotSupported());
+
mTetheringCallbackCaptor.getValue()
.onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps);
@@ -154,13 +156,21 @@
}
@Test
- public void testHotspotSupported_callbackCalledOnChange() {
+ public void testHotspotSupported_callbackCalledOnChange_tetheringSupported() {
mController.addCallback(mCallback1);
- mTetheringCallbackCaptor.getValue().onTetheringSupported(true);
+ mTetheringCallbackCaptor.getValue().onTetheringSupported(false);
+
+ verify(mCallback1).onHotspotAvailabilityChanged(false);
+ }
+
+ @Test
+ public void testHotspotSupported_callbackCalledOnChange_tetherableInterfaces() {
+ when(mTetheringInterfaceRegexps.getTetherableWifiRegexs())
+ .thenReturn(Collections.emptyList());
+ mController.addCallback(mCallback1);
mTetheringCallbackCaptor.getValue()
.onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps);
- verify(mCallback1).onHotspotAvailabilityChanged(true);
+ verify(mCallback1).onHotspotAvailabilityChanged(false);
}
-
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 32da4c9..9c250c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -72,7 +72,7 @@
testSsid);
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[0][testLevel],
- null);
+ testSsid);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index 0c9130d0..44184ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -28,6 +29,7 @@
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -37,7 +39,6 @@
import android.net.ConnectivityManager.NetworkCallback;
import android.net.NetworkRequest;
import android.os.Handler;
-import android.os.Looper;
import android.os.UserManager;
import android.security.IKeyChainService;
import android.test.suitebuilder.annotation.SmallTest;
@@ -46,34 +47,30 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SecurityControllerTest extends SysuiTestCase implements SecurityControllerCallback {
+public class SecurityControllerTest extends SysuiTestCase {
private final DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
private final IKeyChainService.Stub mKeyChainService = mock(IKeyChainService.Stub.class);
private final UserManager mUserManager = mock(UserManager.class);
+ private final BroadcastDispatcher mBroadcastDispatcher = mock(BroadcastDispatcher.class);
+ private final Handler mHandler = mock(Handler.class);
private SecurityControllerImpl mSecurityController;
- private CountDownLatch mStateChangedLatch;
private ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
-
- // implementing SecurityControllerCallback
- @Override
- public void onStateChanged() {
- mStateChangedLatch.countDown();
- }
+ private FakeExecutor mBgExecutor;
+ private BroadcastReceiver mBroadcastReceiver;
@Before
public void setUp() throws Exception {
@@ -86,6 +83,7 @@
mContext.addMockService(comp, mKeyChainService);
when(mUserManager.getUserInfo(anyInt())).thenReturn(new UserInfo());
+ when(mUserManager.isUserUnlocked(any())).thenReturn(true);
when(mKeyChainService.getUserCaAliases())
.thenReturn(new StringParceledListSlice(new ArrayList<String>()));
@@ -94,18 +92,23 @@
when(mKeyChainService.queryLocalInterface("android.security.IKeyChainService"))
.thenReturn(mKeyChainService);
- // Wait for callbacks from the onUserSwitched() function in the
- // constructor of mSecurityController
- mStateChangedLatch = new CountDownLatch(1);
- // TODO: Migrate this test to TestableLooper and use a handler attached
- // to that.
- mSecurityController = new SecurityControllerImpl(mContext,
- new Handler(Looper.getMainLooper()), mock(BroadcastDispatcher.class), this);
- }
+ ArgumentCaptor<BroadcastReceiver> brCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
- @After
- public void tearDown() {
- mSecurityController.removeCallback(this);
+ mBgExecutor = new FakeExecutor(new FakeSystemClock());
+ mSecurityController = new SecurityControllerImpl(
+ mContext,
+ mHandler,
+ mBroadcastDispatcher,
+ mBgExecutor);
+
+ verify(mBroadcastDispatcher).registerReceiverWithHandler(
+ brCaptor.capture(),
+ anyObject(),
+ anyObject(),
+ anyObject());
+
+ mBroadcastReceiver = brCaptor.getValue();
}
@Test
@@ -125,8 +128,6 @@
@Test
public void testWorkAccount() throws Exception {
- // Wait for the callbacks from setUp()
- assertTrue(mStateChangedLatch.await(1, TimeUnit.SECONDS));
assertFalse(mSecurityController.hasCACertInCurrentUser());
final int PRIMARY_USER_ID = 0;
@@ -139,53 +140,41 @@
assertTrue(mSecurityController.hasWorkProfile());
assertFalse(mSecurityController.hasCACertInWorkProfile());
- mStateChangedLatch = new CountDownLatch(1);
-
when(mKeyChainService.getUserCaAliases())
.thenReturn(new StringParceledListSlice(Arrays.asList("One CA Alias")));
- mSecurityController.new CACertLoader()
- .execute(MANAGED_USER_ID);
+ refreshCACerts(MANAGED_USER_ID);
+ mBgExecutor.runAllReady();
- assertTrue(mStateChangedLatch.await(3, TimeUnit.SECONDS));
assertTrue(mSecurityController.hasCACertInWorkProfile());
}
@Test
public void testCaCertLoader() throws Exception {
- // Wait for the callbacks from setUp()
- assertTrue(mStateChangedLatch.await(1, TimeUnit.SECONDS));
assertFalse(mSecurityController.hasCACertInCurrentUser());
// With a CA cert
- mStateChangedLatch = new CountDownLatch(1);
-
when(mKeyChainService.getUserCaAliases())
.thenReturn(new StringParceledListSlice(Arrays.asList("One CA Alias")));
- mSecurityController.new CACertLoader()
- .execute(0);
+ refreshCACerts(0);
+ mBgExecutor.runAllReady();
- assertTrue(mStateChangedLatch.await(3, TimeUnit.SECONDS));
assertTrue(mSecurityController.hasCACertInCurrentUser());
// Exception
- mStateChangedLatch = new CountDownLatch(1);
-
when(mKeyChainService.getUserCaAliases())
.thenThrow(new AssertionError("Test AssertionError"))
.thenReturn(new StringParceledListSlice(new ArrayList<String>()));
- mSecurityController.new CACertLoader()
- .execute(0);
+ refreshCACerts(0);
+ mBgExecutor.runAllReady();
- assertFalse(mStateChangedLatch.await(1, TimeUnit.SECONDS));
assertTrue(mSecurityController.hasCACertInCurrentUser());
- mSecurityController.new CACertLoader()
- .execute(0);
+ refreshCACerts(0);
+ mBgExecutor.runAllReady();
- assertTrue(mStateChangedLatch.await(1, TimeUnit.SECONDS));
assertFalse(mSecurityController.hasCACertInCurrentUser());
}
@@ -196,4 +185,13 @@
&& request.networkCapabilities.getCapabilities().length == 0
), any(NetworkCallback.class));
}
+
+ /**
+ * refresh CA certs by sending a user unlocked broadcast for the desired user
+ */
+ private void refreshCACerts(int userId) {
+ Intent intent = new Intent(Intent.ACTION_USER_UNLOCKED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mBroadcastReceiver.onReceive(mContext, intent);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
index c761a44..e4e0dc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
@@ -22,7 +22,6 @@
import android.app.RemoteInput;
import android.os.Handler;
-import android.os.Looper;
import android.provider.DeviceConfig;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -32,8 +31,8 @@
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.DeviceConfigProxyFake;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,14 +41,13 @@
@TestableLooper.RunWithLooper
@SmallTest
public class SmartReplyConstantsTest extends SysuiTestCase {
-
- private static final int CONTENT_OBSERVER_TIMEOUT_SECONDS = 10;
-
private SmartReplyConstants mConstants;
+ private DeviceConfigProxyFake mDeviceConfig;
+ private TestableLooper mTestableLooper;
@Before
public void setUp() {
- resetAllDeviceConfigFlags();
+ mDeviceConfig = new DeviceConfigProxyFake();
TestableResources resources = mContext.getOrCreateTestableResources();
resources.addOverride(R.bool.config_smart_replies_in_notifications_enabled, true);
resources.addOverride(
@@ -62,12 +60,12 @@
2);
resources.addOverride(
R.integer.config_smart_replies_in_notifications_max_num_actions, -1);
- mConstants = new SmartReplyConstants(Handler.createAsync(Looper.myLooper()), mContext);
- }
-
- @After
- public void tearDown() {
- resetAllDeviceConfigFlags();
+ mTestableLooper = TestableLooper.get(this);
+ mConstants = new SmartReplyConstants(
+ new Handler(mTestableLooper.getLooper()),
+ mContext,
+ mDeviceConfig
+ );
}
@Test
@@ -78,25 +76,21 @@
@Test
public void testIsEnabledWithInvalidConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_ENABLED, "invalid config");
- triggerConstantsOnChange();
assertTrue(mConstants.isEnabled());
}
@Test
public void testIsEnabledWithValidConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_ENABLED, "false");
- triggerConstantsOnChange();
assertFalse(mConstants.isEnabled());
}
@Test
public void testRequiresTargetingPConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, "false");
- triggerConstantsOnChange();
assertEquals(false, mConstants.requiresTargetingP());
overrideSetting(SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, null);
- triggerConstantsOnChange();
assertEquals(true, mConstants.requiresTargetingP());
}
@@ -110,20 +104,17 @@
public void testGetMaxSqueezeRemeasureAttemptsWithInvalidConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS,
"invalid config");
- triggerConstantsOnChange();
assertEquals(7, mConstants.getMaxSqueezeRemeasureAttempts());
}
@Test
public void testGetMaxSqueezeRemeasureAttemptsWithValidConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS, "5");
- triggerConstantsOnChange();
assertEquals(5, mConstants.getMaxSqueezeRemeasureAttempts());
}
@Test
public void testGetEffectiveEditChoicesBeforeSendingWithNoConfig() {
- triggerConstantsOnChange();
assertFalse(
mConstants.getEffectiveEditChoicesBeforeSending(
RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
@@ -138,7 +129,6 @@
@Test
public void testGetEffectiveEditChoicesBeforeSendingWithEnabledConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING, "true");
- triggerConstantsOnChange();
assertTrue(
mConstants.getEffectiveEditChoicesBeforeSending(
RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
@@ -153,7 +143,6 @@
@Test
public void testGetEffectiveEditChoicesBeforeSendingWithDisabledConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING, "false");
- triggerConstantsOnChange();
assertFalse(
mConstants.getEffectiveEditChoicesBeforeSending(
RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
@@ -174,14 +163,12 @@
@Test
public void testShowInHeadsUpEnabled() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, "true");
- triggerConstantsOnChange();
assertTrue(mConstants.getShowInHeadsUp());
}
@Test
public void testShowInHeadsUpDisabled() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, "false");
- triggerConstantsOnChange();
assertFalse(mConstants.getShowInHeadsUp());
}
@@ -194,7 +181,6 @@
@Test
public void testGetMinNumSystemGeneratedRepliesWithValidConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES, "5");
- triggerConstantsOnChange();
assertEquals(5, mConstants.getMinNumSystemGeneratedReplies());
}
@@ -207,7 +193,6 @@
@Test
public void testMaxNumActionsSet() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS, "10");
- triggerConstantsOnChange();
assertEquals(10, mConstants.getMaxNumActions());
}
@@ -219,38 +204,12 @@
@Test
public void testOnClickInitDelaySet() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_ONCLICK_INIT_DELAY, "50");
- triggerConstantsOnChange();
assertEquals(50, mConstants.getOnClickInitDelay());
}
private void overrideSetting(String propertyName, String value) {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
propertyName, value, false /* makeDefault */);
- }
-
- private void triggerConstantsOnChange() {
- mConstants.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
- }
-
- private void resetAllDeviceConfigFlags() {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_ENABLED, null, false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, null, false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS, null,
- false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING, null,
- false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, null, false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES, null,
- false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS, null, false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_ONCLICK_INIT_DELAY, null, false /* makeDefault */);
+ mTestableLooper.processAllMessages();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
index 54cb0b8..31d884c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
@@ -20,11 +20,9 @@
public class FakeProximitySensor extends ProximitySensor {
private boolean mAvailable;
- private boolean mPaused;
public FakeProximitySensor(Resources resources, AsyncSensorManager sensorManager) {
super(resources, sensorManager);
-
mAvailable = true;
}
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 3111ab7..0c37235 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -95,11 +95,7 @@
// TODO (b/148190005): change to module-libs-api-stubs-current once it is ready.
sdk_version: "core_platform",
privileged: true,
- // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies
- // explicitly.
jni_libs: [
- "liblog",
- "libnativehelper_compat_libc++",
"libtetherutilsjni",
],
resource_dirs: [
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 1a3d5b6..5b73dd5 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -41,8 +41,7 @@
java_library {
name: "framework-tethering",
- // TODO (b/146757305): change to module_app_current once available
- sdk_version: "core_platform",
+ sdk_version: "module_current",
srcs: [
"src/android/net/TetheredClient.java",
"src/android/net/TetheringManager.java",
@@ -56,7 +55,6 @@
libs: [
"framework-annotations-lib",
- "android_system_stubs_current",
],
hostdex: true, // for hiddenapi check
diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto
index 789019c..2006fb3 100644
--- a/proto/src/task_snapshot.proto
+++ b/proto/src/task_snapshot.proto
@@ -32,7 +32,12 @@
int32 system_ui_visibility = 8;
bool is_translucent = 9;
string top_activity_component = 10;
- float scale = 11;
+ // deprecated because original width and height are stored now instead of the scale.
+ float legacy_scale = 11 [deprecated=true];
int64 id = 12;
int32 rotation = 13;
+ // The task width when the snapshot was taken
+ int32 task_width = 14;
+ // The task height when the snapshot was taken
+ int32 task_height = 15;
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index a8a2791..ad21075 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -641,8 +641,8 @@
final SuspendDialogInfo dialogInfo =
mPackageManagerInternal.getSuspendedDialogInfo(providerPackage,
suspendingPackage, providerUserId);
- // TODO(b/148035643): Send the original widget intent or ACTION_MAIN as an
- // IntentSender to SuspendedAppActivity.
+ // onUnsuspend is null because we don't want to start any activity on
+ // unsuspending from a suspended widget.
onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
providerPackage, suspendingPackage, dialogInfo, null, null,
providerUserId);
@@ -794,6 +794,8 @@
proto.write(WidgetProto.PROVIDER_PACKAGE, widget.provider.id.componentName.getPackageName());
proto.write(WidgetProto.PROVIDER_CLASS, widget.provider.id.componentName.getClassName());
if (widget.options != null) {
+ proto.write(WidgetProto.RESTORE_COMPLETED,
+ widget.options.getBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED));
proto.write(WidgetProto.MIN_WIDTH,
widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 0));
proto.write(WidgetProto.MIN_HEIGHT,
@@ -2509,7 +2511,8 @@
out.endTag(null, "h");
}
- private static void serializeAppWidget(XmlSerializer out, Widget widget) throws IOException {
+ private static void serializeAppWidget(XmlSerializer out, Widget widget,
+ boolean saveRestoreCompleted) throws IOException {
out.startTag(null, "g");
out.attribute(null, "id", Integer.toHexString(widget.appWidgetId));
out.attribute(null, "rid", Integer.toHexString(widget.restoredId));
@@ -2528,10 +2531,50 @@
out.attribute(null, "max_height", Integer.toHexString((maxHeight > 0) ? maxHeight : 0));
out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt(
AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
+ if (saveRestoreCompleted) {
+ boolean restoreCompleted = widget.options.getBoolean(
+ AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED);
+ out.attribute(null, "restore_completed", Boolean.toString(restoreCompleted));
+ }
}
out.endTag(null, "g");
}
+ private static Bundle parseWidgetIdOptions(XmlPullParser parser) {
+ Bundle options = new Bundle();
+ String restoreCompleted = parser.getAttributeValue(null, "restore_completed");
+ if (restoreCompleted != null) {
+ options.putBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED,
+ Boolean.valueOf(restoreCompleted));
+ }
+ String minWidthString = parser.getAttributeValue(null, "min_width");
+ if (minWidthString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
+ Integer.parseInt(minWidthString, 16));
+ }
+ String minHeightString = parser.getAttributeValue(null, "min_height");
+ if (minHeightString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
+ Integer.parseInt(minHeightString, 16));
+ }
+ String maxWidthString = parser.getAttributeValue(null, "max_width");
+ if (maxWidthString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
+ Integer.parseInt(maxWidthString, 16));
+ }
+ String maxHeightString = parser.getAttributeValue(null, "max_height");
+ if (maxHeightString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
+ Integer.parseInt(maxHeightString, 16));
+ }
+ String categoryString = parser.getAttributeValue(null, "host_category");
+ if (categoryString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+ Integer.parseInt(categoryString, 16));
+ }
+ return options;
+ }
+
@Override
public List<String> getWidgetParticipants(int userId) {
return mBackupRestoreController.getWidgetParticipants(userId);
@@ -3064,7 +3107,7 @@
if (widget.host.getUserId() != userId) {
continue;
}
- serializeAppWidget(out, widget);
+ serializeAppWidget(out, widget, true);
}
Iterator<Pair<Integer, String>> it = mPackagesWithBindWidgetPermission.iterator();
@@ -3203,34 +3246,7 @@
String restoredIdString = parser.getAttributeValue(null, "rid");
widget.restoredId = (restoredIdString == null) ? 0
: Integer.parseInt(restoredIdString, 16);
-
- Bundle options = new Bundle();
- String minWidthString = parser.getAttributeValue(null, "min_width");
- if (minWidthString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
- Integer.parseInt(minWidthString, 16));
- }
- String minHeightString = parser.getAttributeValue(null, "min_height");
- if (minHeightString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
- Integer.parseInt(minHeightString, 16));
- }
- String maxWidthString = parser.getAttributeValue(null, "max_width");
- if (maxWidthString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
- Integer.parseInt(maxWidthString, 16));
- }
- String maxHeightString = parser.getAttributeValue(null, "max_height");
- if (maxHeightString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
- Integer.parseInt(maxHeightString, 16));
- }
- String categoryString = parser.getAttributeValue(null, "host_category");
- if (categoryString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
- Integer.parseInt(categoryString, 16));
- }
- widget.options = options;
+ widget.options = parseWidgetIdOptions(parser);
final int hostTag = Integer.parseInt(parser.getAttributeValue(
null, "h"), 16);
@@ -4382,7 +4398,7 @@
if (widget.host.isInPackageForUser(backedupPackage, userId)
|| (provider != null
&& provider.isInPackageForUser(backedupPackage, userId))) {
- serializeAppWidget(out, widget);
+ serializeAppWidget(out, widget, false);
}
}
@@ -4815,36 +4831,6 @@
|| widget.provider.getUserId() == userId);
}
- private Bundle parseWidgetIdOptions(XmlPullParser parser) {
- Bundle options = new Bundle();
- String minWidthString = parser.getAttributeValue(null, "min_width");
- if (minWidthString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
- Integer.parseInt(minWidthString, 16));
- }
- String minHeightString = parser.getAttributeValue(null, "min_height");
- if (minHeightString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
- Integer.parseInt(minHeightString, 16));
- }
- String maxWidthString = parser.getAttributeValue(null, "max_width");
- if (maxWidthString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
- Integer.parseInt(maxWidthString, 16));
- }
- String maxHeightString = parser.getAttributeValue(null, "max_height");
- if (maxHeightString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
- Integer.parseInt(maxHeightString, 16));
- }
- String categoryString = parser.getAttributeValue(null, "host_category");
- if (categoryString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
- Integer.parseInt(categoryString, 16));
- }
- return options;
- }
-
private int countPendingUpdates(ArrayList<RestoreUpdateRecord> updates) {
int pending = 0;
final int N = updates.size();
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 1eb7692..228c628 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -47,6 +47,7 @@
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.infra.AndroidFuture;
@@ -243,20 +244,27 @@
}
mCallbacks.setLastResponse(sessionId);
+ final InlineSuggestionsResponse inlineSuggestionsResponse =
+ InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
+ request, inlineSuggestionsData, focusedId, mContext,
+ dataset -> {
+ mCallbacks.logAugmentedAutofillSelected(sessionId,
+ dataset.getId());
+ try {
+ client.autofill(sessionId, dataset.getFieldIds(),
+ dataset.getFieldValues());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Encounter exception autofilling the values");
+ }
+ }, onErrorCallback, remoteRenderService);
+
+ if (inlineSuggestionsResponse == null) {
+ Slog.w(TAG, "InlineSuggestionFactory created null response");
+ return;
+ }
+
try {
- inlineSuggestionsCallback.onInlineSuggestionsResponse(
- InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
- request, inlineSuggestionsData, focusedId, mContext,
- dataset -> {
- mCallbacks.logAugmentedAutofillSelected(sessionId,
- dataset.getId());
- try {
- client.autofill(sessionId, dataset.getFieldIds(),
- dataset.getFieldValues());
- } catch (RemoteException e) {
- Slog.w(TAG, "Encounter exception autofilling the values");
- }
- }, onErrorCallback, remoteRenderService));
+ inlineSuggestionsCallback.onInlineSuggestionsResponse(inlineSuggestionsResponse);
} catch (RemoteException e) {
Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
index 5d5af53..7ad5632 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
@@ -86,9 +86,9 @@
*/
public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
@NonNull InlinePresentation presentation, int width, int height,
- @Nullable IBinder hostInputToken) {
- scheduleAsyncRequest(
- (s) -> s.renderSuggestion(callback, presentation, width, height, hostInputToken));
+ @Nullable IBinder hostInputToken, int displayId) {
+ scheduleAsyncRequest((s) -> s.renderSuggestion(callback, presentation, width, height,
+ hostInputToken, displayId));
}
@Nullable
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 317ce4c..960997d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2686,6 +2686,12 @@
requestHideFillUi(mCurrentViewId);
}
}, mService.getRemoteInlineSuggestionRenderServiceLocked());
+
+ if (inlineSuggestionsResponse == null) {
+ Slog.w(TAG, "InlineSuggestionFactory created null response");
+ return false;
+ }
+
try {
imeResponse.getCallback().onInlineSuggestionsResponse(inlineSuggestionsResponse);
} catch (RemoteException e) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 0d8c89b..0d1b6dd 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -134,7 +134,7 @@
if (inlineAuthentication != null) {
InlineSuggestion inlineAuthSuggestion = createInlineAuthSuggestion(inlineAuthentication,
remoteRenderService, onClickFactory, onErrorCallback,
- request.getHostInputToken());
+ request.getHostInputToken(), request.getHostDisplayId());
inlineSuggestions.add(inlineAuthSuggestion);
return new InlineSuggestionsResponse(inlineSuggestions);
@@ -150,21 +150,22 @@
final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
if (fieldIndex < 0) {
Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
- return null;
+ continue;
}
final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(
fieldIndex);
if (inlinePresentation == null) {
Slog.w(TAG, "InlinePresentation not found in dataset");
- return null;
+ continue;
}
if (!includeDataset(dataset, fieldIndex, filterText)) {
continue;
}
InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
- fieldIndex, mergedInlinePresentation(request, datasetIndex, inlinePresentation),
+ datasetIndex,
+ mergedInlinePresentation(request, datasetIndex, inlinePresentation),
onClickFactory, remoteRenderService, onErrorCallback,
- request.getHostInputToken());
+ request.getHostInputToken(), request.getHostDisplayId());
inlineSuggestions.add(inlineSuggestion);
}
@@ -172,7 +173,8 @@
for (InlinePresentation inlinePresentation : inlineActions) {
final InlineSuggestion inlineAction = createInlineAction(isAugmented, context,
mergedInlinePresentation(request, 0, inlinePresentation),
- remoteRenderService, onErrorCallback, request.getHostInputToken());
+ remoteRenderService, onErrorCallback, request.getHostInputToken(),
+ request.getHostDisplayId());
inlineSuggestions.add(inlineAction);
}
}
@@ -215,19 +217,20 @@
@NonNull Context context,
@NonNull InlinePresentation inlinePresentation,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) {
- // TODO(b/146453195): fill in the autofill hint properly.
+ @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken,
+ int displayId) {
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
- : InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
- InlineSuggestionInfo.TYPE_ACTION);
+ : InlineSuggestionInfo.SOURCE_AUTOFILL,
+ inlinePresentation.getAutofillHints(),
+ InlineSuggestionInfo.TYPE_ACTION, inlinePresentation.isPinned());
final Runnable onClickAction = () -> {
Toast.makeText(context, "icon clicked", Toast.LENGTH_SHORT).show();
};
return new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation, onClickAction, onErrorCallback,
- remoteRenderService, hostInputToken));
+ remoteRenderService, hostInputToken, displayId));
}
private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
@@ -235,18 +238,19 @@
@NonNull InlinePresentation inlinePresentation,
@NonNull BiConsumer<Dataset, Integer> onClickFactory,
@NonNull RemoteInlineSuggestionRenderService remoteRenderService,
- @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) {
- // TODO(b/146453195): fill in the autofill hint properly.
+ @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken,
+ int displayId) {
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
- : InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
- InlineSuggestionInfo.TYPE_SUGGESTION);
+ : InlineSuggestionInfo.SOURCE_AUTOFILL,
+ inlinePresentation.getAutofillHints(),
+ InlineSuggestionInfo.TYPE_SUGGESTION, inlinePresentation.isPinned());
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation,
() -> onClickFactory.accept(dataset, datasetIndex), onErrorCallback,
- remoteRenderService, hostInputToken));
+ remoteRenderService, hostInputToken, displayId));
return inlineSuggestion;
}
@@ -255,16 +259,17 @@
@NonNull InlinePresentation inlinePresentation,
@NonNull RemoteInlineSuggestionRenderService remoteRenderService,
@NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull Runnable onErrorCallback,
- @Nullable IBinder hostInputToken) {
+ @Nullable IBinder hostInputToken, int displayId) {
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
- InlineSuggestionInfo.SOURCE_AUTOFILL, null, InlineSuggestionInfo.TYPE_SUGGESTION);
+ InlineSuggestionInfo.SOURCE_AUTOFILL, inlinePresentation.getAutofillHints(),
+ InlineSuggestionInfo.TYPE_SUGGESTION, inlinePresentation.isPinned());
return new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation,
() -> onClickFactory.accept(null,
AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
- onErrorCallback, remoteRenderService, hostInputToken));
+ onErrorCallback, remoteRenderService, hostInputToken, displayId));
}
/**
@@ -291,7 +296,8 @@
@NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction,
@NonNull Runnable onErrorCallback,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- @Nullable IBinder hostInputToken) {
+ @Nullable IBinder hostInputToken,
+ int displayId) {
return new IInlineContentProvider.Stub() {
@Override
public void provideContent(int width, int height, IInlineContentCallback callback) {
@@ -305,7 +311,7 @@
}
remoteRenderService.renderSuggestion(uiCallback, inlinePresentation,
- width, height, hostInputToken);
+ width, height, hostInputToken, displayId);
});
}
};
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4da60b8..84ce34b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -99,6 +99,7 @@
"android.hardware.vibrator-java",
"app-compat-annotations",
"framework-tethering-stubs",
+ "ike-stubs",
],
required: [
@@ -127,6 +128,7 @@
"dnsresolver_aidl_interface-V2-java",
"netd_event_listener_interface-java",
"ike-stubs",
+ "overlayable_policy_aidl-java",
],
plugins: [
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 1230bd7..1b1e06a 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -458,8 +458,8 @@
Bundle verificationBundle, int userId);
/**
- * Grants implicit access based on an interaction between two apps. This grants the target app
- * access to the calling application's package metadata.
+ * Grants implicit access based on an interaction between two apps. This grants access to the
+ * from one application to the other's package metadata.
* <p>
* When an application explicitly tries to interact with another application [via an
* activity, service or provider that is either declared in the caller's
@@ -468,14 +468,22 @@
* metadata about the calling app. If the calling application uses an implicit intent [ie
* action VIEW, category BROWSABLE], it remains hidden from the launched app.
* <p>
+ * If an interaction is not explicit, the {@code direct} argument should be set to false as
+ * visibility should not be granted in some cases. This method handles that logic.
+ * <p>
* @param userId the user
* @param intent the intent that triggered the grant
- * @param callingUid The uid of the calling application
- * @param targetAppId The app ID of the target application
+ * @param recipientAppId The app ID of the application that is being given access to {@code
+ * visibleUid}
+ * @param visibleUid The uid of the application that is becoming accessible to {@code
+ * recipientAppId}
+ * @param direct true if the access is being made due to direct interaction between visibleUid
+ * and recipientAppId.
*/
public abstract void grantImplicitAccess(
- @UserIdInt int userId, Intent intent, int callingUid,
- @AppIdInt int targetAppId);
+ @UserIdInt int userId, Intent intent,
+ @AppIdInt int recipientAppId, int visibleUid,
+ boolean direct);
public abstract boolean isInstantAppInstallerComponent(ComponentName component);
/**
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 93859b3..3148a62 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -345,7 +345,7 @@
@Override
public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
}
- }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, "system");
+ }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, null);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to register uid observer", e);
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index b1cb138..95bfbd7 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4458,8 +4458,11 @@
mVold.fixupAppDir(packageObbDir.getCanonicalPath() + "/", uid);
} catch (IOException e) {
Log.e(TAG, "Failed to get canonical path for " + packageName);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to fixup app dir for " + packageName);
+ } catch (RemoteException | ServiceSpecificException e) {
+ // TODO(b/149975102) there is a known case where this fails, when a new
+ // user is setup and we try to fixup app dirs for some existing apps.
+ // For now catch the exception and don't crash.
+ Log.e(TAG, "Failed to fixup app dir for " + packageName, e);
}
}
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 0f1a652..dd9cc64 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -183,7 +183,8 @@
static native boolean vibratorSupportsAmplitudeControl();
static native void vibratorSetAmplitude(int amplitude);
static native int[] vibratorGetSupportedEffects();
- static native long vibratorPerformEffect(long effect, long strength, Vibration vibration);
+ static native long vibratorPerformEffect(long effect, long strength, Vibration vibration,
+ boolean withCallback);
static native void vibratorPerformComposedEffect(
VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration);
static native boolean vibratorSupportsExternalControl();
@@ -1334,7 +1335,8 @@
// Input devices don't support prebaked effect, so skip trying it with them.
if (!usingInputDeviceVibrators) {
long duration = vibratorPerformEffect(prebaked.getId(),
- prebaked.getEffectStrength(), vib);
+ prebaked.getEffectStrength(), vib,
+ hasCapability(IVibrator.CAP_PERFORM_CALLBACK));
long timeout = duration;
if (hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) {
timeout *= ASYNC_TIMEOUT_MULTIPLIER;
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 6c68c31..e49357b 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -1853,6 +1853,7 @@
public void removeKey(String key) {
if (mKeyMap.containsKey(key)) {
mKeyMap.remove(key);
+ writeKeys(mKeyMap.keySet());
sendPersistKeyStoreMessage();
}
}
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 7ccb284..7aaf9be 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -144,6 +144,18 @@
public File getAdbTempKeysFile() {
return mDebuggingManager.getAdbTempKeysFile();
}
+
+ @Override
+ public void startAdbdForTransport(byte transportType) {
+ FgThread.getHandler().sendMessage(obtainMessage(
+ AdbService::setAdbdEnabledForTransport, AdbService.this, true, transportType));
+ }
+
+ @Override
+ public void stopAdbdForTransport(byte transportType) {
+ FgThread.getHandler().sendMessage(obtainMessage(
+ AdbService::setAdbdEnabledForTransport, AdbService.this, false, transportType));
+ }
}
private void initAdbState() {
@@ -437,6 +449,19 @@
}
}
+ private void setAdbdEnabledForTransport(boolean enable, byte transportType) {
+ if (transportType == AdbTransportType.USB) {
+ mIsAdbUsbEnabled = enable;
+ } else if (transportType == AdbTransportType.WIFI) {
+ mIsAdbWifiEnabled = enable;
+ }
+ if (enable) {
+ startAdbd();
+ } else {
+ stopAdbd();
+ }
+ }
+
private void setAdbEnabled(boolean enable, byte transportType) {
if (DEBUG) {
Slog.d(TAG, "setAdbEnabled(" + enable + "), mIsAdbUsbEnabled=" + mIsAdbUsbEnabled
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7f98c7f..b7b52b1 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
@@ -36,6 +37,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -88,11 +90,13 @@
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.webkit.WebViewZygote;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.ServiceState;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
@@ -177,6 +181,10 @@
/** Temporary list for holding the results of calls to {@link #collectPackageServicesLocked} */
private ArrayList<ServiceRecord> mTmpCollectionResults = null;
+ /** Mapping from uid to their foreground service AppOpCallbacks (if they have one). */
+ @GuardedBy("mAm")
+ private final SparseArray<AppOpCallback> mFgsAppOpCallbacks = new SparseArray<>();
+
/**
* For keeping ActiveForegroundApps retaining state while the screen is off.
*/
@@ -685,8 +693,8 @@
if (!r.mAllowWhileInUsePermissionInFgs) {
r.mAllowWhileInUsePermissionInFgs =
- shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingUid,
- service, r, allowBackgroundActivityStarts);
+ shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid,
+ callingUid, service, r, allowBackgroundActivityStarts);
}
return cmp;
@@ -1455,7 +1463,9 @@
null, true, false, "");
FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
r.appInfo.uid, r.shortInstanceName,
- FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
+ FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
+ r.mAllowWhileInUsePermissionInFgs);
+ registerAppOpCallbackLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
}
r.postNotification();
@@ -1504,9 +1514,11 @@
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
+ unregisterAppOpCallbackLocked(r);
FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
r.appInfo.uid, r.shortInstanceName,
- FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
+ FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
+ r.mAllowWhileInUsePermissionInFgs);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
if (r.app != null) {
mAm.updateLruProcessLocked(r.app, false, null);
@@ -1527,6 +1539,207 @@
}
}
+ /** Registers an AppOpCallback for monitoring special AppOps for this foreground service. */
+ private void registerAppOpCallbackLocked(@NonNull ServiceRecord r) {
+ if (r.app == null) {
+ return;
+ }
+ final int uid = r.appInfo.uid;
+ AppOpCallback callback = mFgsAppOpCallbacks.get(uid);
+ if (callback == null) {
+ callback = new AppOpCallback(r.app, mAm.getAppOpsManager());
+ mFgsAppOpCallbacks.put(uid, callback);
+ }
+ callback.registerLocked();
+ }
+
+ /** Unregisters a foreground service's AppOpCallback. */
+ private void unregisterAppOpCallbackLocked(@NonNull ServiceRecord r) {
+ final int uid = r.appInfo.uid;
+ final AppOpCallback callback = mFgsAppOpCallbacks.get(uid);
+ if (callback != null) {
+ callback.unregisterLocked();
+ if (callback.isObsoleteLocked()) {
+ mFgsAppOpCallbacks.remove(uid);
+ }
+ }
+ }
+
+ /**
+ * For monitoring when {@link #LOGGED_AP_OPS} AppOps occur by an app while it is holding
+ * at least one foreground service and is not also in the TOP state.
+ * Once the uid no longer holds any foreground services, this callback becomes stale
+ * (marked by {@link #isObsoleteLocked()}) and must no longer be used.
+ *
+ * Methods that end in Locked should only be called while the mAm lock is held.
+ */
+ private static final class AppOpCallback {
+ /** AppOps that should be logged if they occur during a foreground service. */
+ private static final int[] LOGGED_AP_OPS = new int[] {
+ AppOpsManager.OP_COARSE_LOCATION,
+ AppOpsManager.OP_FINE_LOCATION,
+ AppOpsManager.OP_RECORD_AUDIO,
+ AppOpsManager.OP_CAMERA
+ };
+
+ private final ProcessRecord mProcessRecord;
+
+ /** Count of acceptances per appop (for LOGGED_AP_OPS) during this fgs session. */
+ @GuardedBy("mCounterLock")
+ private final SparseIntArray mAcceptedOps = new SparseIntArray();
+ /** Count of rejections per appop (for LOGGED_AP_OPS) during this fgs session. */
+ @GuardedBy("mCounterLock")
+ private final SparseIntArray mRejectedOps = new SparseIntArray();
+
+ /** Lock for the purposes of mAcceptedOps and mRejectedOps. */
+ private final Object mCounterLock = new Object();
+
+ /**
+ * AppOp Mode (e.g. {@link AppOpsManager#MODE_ALLOWED} per op.
+ * This currently cannot change without the process being killed, so they are constants.
+ */
+ private final SparseIntArray mAppOpModes = new SparseIntArray();
+
+ /**
+ * Number of foreground services currently associated with this AppOpCallback (i.e.
+ * currently held for this uid).
+ */
+ @GuardedBy("mAm")
+ private int mNumFgs = 0;
+
+ /**
+ * Indicates that this Object is stale and must not be used.
+ * Specifically, when mNumFgs decreases down to 0, the callbacks will be unregistered and
+ * this AppOpCallback is unusable.
+ */
+ @GuardedBy("mAm")
+ private boolean mDestroyed = false;
+
+ private final AppOpsManager mAppOpsManager;
+
+ AppOpCallback(@NonNull ProcessRecord r, @NonNull AppOpsManager appOpsManager) {
+ mProcessRecord = r;
+ mAppOpsManager = appOpsManager;
+ for (int op : LOGGED_AP_OPS) {
+ int mode = appOpsManager.unsafeCheckOpRawNoThrow(op, r.uid, r.info.packageName);
+ mAppOpModes.put(op, mode);
+ }
+ }
+
+ private final AppOpsManager.OnOpNotedListener mOpNotedCallback =
+ new AppOpsManager.OnOpNotedListener() {
+ @Override
+ public void onOpNoted(int op, int uid, String pkgName, int result) {
+ if (uid == mProcessRecord.uid && isNotTop()) {
+ incrementOpCount(op, result == AppOpsManager.MODE_ALLOWED);
+ }
+ }
+ };
+
+ private final AppOpsManager.OnOpActiveChangedInternalListener mOpActiveCallback =
+ new AppOpsManager.OnOpActiveChangedInternalListener() {
+ @Override
+ public void onOpActiveChanged(int op, int uid, String pkgName, boolean active) {
+ if (uid == mProcessRecord.uid && active && isNotTop()) {
+ incrementOpCount(op, true);
+ }
+ }
+ };
+
+ private boolean isNotTop() {
+ return mProcessRecord.getCurProcState() != ActivityManager.PROCESS_STATE_TOP;
+ }
+
+ private void incrementOpCount(int op, boolean allowed) {
+ synchronized (mCounterLock) {
+ final SparseIntArray counter = allowed ? mAcceptedOps : mRejectedOps;
+ final int index = counter.indexOfKey(op);
+ if (index < 0) {
+ counter.put(op, 1);
+ } else {
+ counter.setValueAt(index, counter.valueAt(index) + 1);
+ }
+ }
+ }
+
+ void registerLocked() {
+ if (isObsoleteLocked()) {
+ Slog.wtf(TAG, "Trying to register on a stale AppOpCallback.");
+ return;
+ }
+ mNumFgs++;
+ if (mNumFgs == 1) {
+ mAppOpsManager.startWatchingNoted(LOGGED_AP_OPS, mOpNotedCallback);
+ mAppOpsManager.startWatchingActive(LOGGED_AP_OPS, mOpActiveCallback);
+ }
+ }
+
+ void unregisterLocked() {
+ mNumFgs--;
+ if (mNumFgs <= 0) {
+ mDestroyed = true;
+ logFinalValues();
+ mAppOpsManager.stopWatchingNoted(mOpNotedCallback);
+ mAppOpsManager.stopWatchingActive(mOpActiveCallback);
+ }
+ }
+
+ /**
+ * Indicates that all foreground services for this uid are now over and the callback is
+ * stale and must never be used again.
+ */
+ boolean isObsoleteLocked() {
+ return mDestroyed;
+ }
+
+ private void logFinalValues() {
+ synchronized (mCounterLock) {
+ for (int op : LOGGED_AP_OPS) {
+ final int acceptances = mAcceptedOps.get(op);
+ final int rejections = mRejectedOps.get(op);
+ if (acceptances > 0 || rejections > 0) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED,
+ mProcessRecord.uid, opToEnum(op),
+ modeToEnum(mAppOpModes.get(op)),
+ acceptances, rejections
+ );
+ }
+ }
+ }
+ }
+
+ /** Maps AppOp mode to atoms.proto enum. */
+ private static int modeToEnum(int mode) {
+ switch (mode) {
+ case AppOpsManager.MODE_ALLOWED: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_ALLOWED;
+ case AppOpsManager.MODE_IGNORED: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_IGNORED;
+ case AppOpsManager.MODE_FOREGROUND: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_FOREGROUND;
+ default: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_UNKNOWN;
+ }
+ }
+ }
+
+ /** Maps AppOp op value to atoms.proto enum. */
+ private static int opToEnum(int op) {
+ switch (op) {
+ case AppOpsManager.OP_COARSE_LOCATION: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_COARSE_LOCATION;
+ case AppOpsManager.OP_FINE_LOCATION: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_FINE_LOCATION;
+ case AppOpsManager.OP_CAMERA: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_CAMERA;
+ case AppOpsManager.OP_RECORD_AUDIO: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_RECORD_AUDIO;
+ default: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_NONE;
+ }
+ }
+
private void cancelForegroundNotificationLocked(ServiceRecord r) {
if (r.foregroundId != 0) {
// First check to see if this app has any other active foreground services
@@ -1865,9 +2078,9 @@
}
if (!s.mAllowWhileInUsePermissionInFgs) {
- final int callingUid = Binder.getCallingUid();
s.mAllowWhileInUsePermissionInFgs =
- shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingUid,
+ shouldAllowWhileInUsePermissionInFgsLocked(callingPackage,
+ Binder.getCallingPid(), Binder.getCallingUid(),
service, s, false);
}
@@ -3136,9 +3349,11 @@
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
+ unregisterAppOpCallbackLocked(r);
FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
r.appInfo.uid, r.shortInstanceName,
- FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
+ FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
+ r.mAllowWhileInUsePermissionInFgs);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
}
@@ -3286,8 +3501,11 @@
}
}
- // If unbound while waiting to start, remove the pending service
- mPendingServices.remove(s);
+ // If unbound while waiting to start and there is no connection left in this service,
+ // remove the pending service
+ if (s.getConnections().isEmpty()) {
+ mPendingServices.remove(s);
+ }
if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
boolean hasAutoCreate = s.hasAutoCreateConnections();
@@ -4625,7 +4843,8 @@
* @return true if allow, false otherwise.
*/
private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage,
- int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) {
+ int callingPid, int callingUid, Intent intent, ServiceRecord r,
+ boolean allowBackgroundActivityStarts) {
// Is the background FGS start restriction turned on?
if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
return true;
@@ -4635,13 +4854,6 @@
return true;
}
- // Is the service in a whitelist?
- final boolean hasAllowBackgroundActivityStartsToken = r.app != null
- ? r.app.mAllowBackgroundActivityStartsTokens.contains(r) : false;
- if (hasAllowBackgroundActivityStartsToken) {
- return true;
- }
-
boolean isCallerSystem = false;
final int callingAppId = UserHandle.getAppId(callingUid);
switch (callingAppId) {
@@ -4660,6 +4872,24 @@
return true;
}
+ if (r.app != null) {
+ ActiveInstrumentation instr = r.app.getActiveInstrumentation();
+ if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
+ return true;
+ }
+ }
+
+ final boolean hasAllowBackgroundActivityStartsToken = r.app != null
+ ? !r.app.mAllowBackgroundActivityStartsTokens.isEmpty() : false;
+ if (hasAllowBackgroundActivityStartsToken) {
+ return true;
+ }
+
+ if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
+ == PERMISSION_GRANTED) {
+ return true;
+ }
+
// Is the calling UID at PROCESS_STATE_TOP or above?
final boolean isCallingUidTopApp = appIsTopLocked(callingUid);
if (isCallingUidTopApp) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ca0b03df..b2fb530 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -218,6 +218,7 @@
import android.content.pm.PermissionInfo;
import android.content.pm.ProcessInfo;
import android.content.pm.ProviderInfo;
+import android.content.pm.ProviderInfoList;
import android.content.pm.ResolveInfo;
import android.content.pm.SELinuxUtil;
import android.content.pm.ServiceInfo;
@@ -5151,12 +5152,13 @@
if (mPlatformCompat != null) {
mPlatformCompat.resetReporting(app.info);
}
+ final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
if (app.isolatedEntryPoint != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
} else if (instr2 != null) {
- thread.bindApplication(processName, appInfo, providers,
+ thread.bindApplication(processName, appInfo, providerList,
instr2.mClass,
profilerInfo, instr2.mArguments,
instr2.mWatcher,
@@ -5169,7 +5171,7 @@
buildSerial, autofillOptions, contentCaptureOptions,
app.mDisabledCompatChanges);
} else {
- thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
+ thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
@@ -6285,9 +6287,9 @@
}
@VisibleForTesting
- public void grantImplicitAccess(int userId, Intent intent, int callingUid, int targetAppId) {
+ public void grantImplicitAccess(int userId, Intent intent, int visibleUid, int recipientAppId) {
getPackageManagerInternalLocked().
- grantImplicitAccess(userId, intent, callingUid, targetAppId);
+ grantImplicitAccess(userId, intent, recipientAppId, visibleUid, true /*direct*/);
}
/**
@@ -6513,14 +6515,6 @@
}
@Override
- public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
- Rect tempDockedTaskInsetBounds,
- Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
- mActivityTaskManager.resizeDockedStack(dockedBounds, tempDockedTaskBounds,
- tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds);
- }
-
- @Override
public void positionTaskInStack(int taskId, int stackId, int position) {
mActivityTaskManager.positionTaskInStack(taskId, stackId, position);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index c7f5f63..2941e77 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2570,10 +2570,6 @@
switch (op) {
case "move-task":
return runStackMoveTask(pw);
- case "resize-animated":
- return runStackResizeAnimated(pw);
- case "resize-docked-stack":
- return runStackResizeDocked(pw);
case "positiontask":
return runStackPositionTask(pw);
case "list":
@@ -2648,34 +2644,6 @@
return 0;
}
- int runStackResizeAnimated(PrintWriter pw) throws RemoteException {
- String stackIdStr = getNextArgRequired();
- int stackId = Integer.parseInt(stackIdStr);
- final Rect bounds;
- if ("null".equals(peekNextArg())) {
- bounds = null;
- } else {
- bounds = getBounds();
- if (bounds == null) {
- getErrPrintWriter().println("Error: invalid input bounds");
- return -1;
- }
- }
- mTaskInterface.animateResizePinnedStack(stackId, bounds, -1);
- return 0;
- }
-
- int runStackResizeDocked(PrintWriter pw) throws RemoteException {
- final Rect bounds = getBounds();
- final Rect taskBounds = getBounds();
- if (bounds == null || taskBounds == null) {
- getErrPrintWriter().println("Error: invalid input bounds");
- return -1;
- }
- mTaskInterface.resizeDockedStack(bounds, taskBounds, null, null, null);
- return 0;
- }
-
int runStackPositionTask(PrintWriter pw) throws RemoteException {
String taskIdStr = getNextArgRequired();
int taskId = Integer.parseInt(taskIdStr);
@@ -3285,8 +3253,6 @@
pw.println(" move-task <TASK_ID> <STACK_ID> [true|false]");
pw.println(" Move <TASK_ID> from its current stack to the top (true) or");
pw.println(" bottom (false) of <STACK_ID>.");
- pw.println(" resize-animated <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>");
- pw.println(" Same as resize, but allow animation.");
pw.println(" resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]");
pw.println(" Change docked stack to <LEFT,TOP,RIGHT,BOTTOM>");
pw.println(" and supplying temporary different task bounds indicated by");
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 60aba27..cba6b92 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -708,7 +708,7 @@
void dumpHistoryProcessExitInfo(PrintWriter pw, String packageName) {
pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity exit-info)");
- SimpleDateFormat sdf = new SimpleDateFormat();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
synchronized (mLock) {
pw.println("Last Timestamp of Persistence Into Persistent Storage: "
+ sdf.format(new Date(mLastAppExitInfoPersistTimestamp)));
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 119394f..dbad562 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import android.app.ActivityManager;
-import android.app.job.JobProtoEnums;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.content.ContentResolver;
import android.content.Context;
@@ -468,26 +467,18 @@
}
/** A scheduled job was started. */
- public void noteJobStart(String name, int uid, int standbyBucket, int jobid) {
+ public void noteJobStart(String name, int uid) {
enforceCallingPermission();
synchronized (mStats) {
mStats.noteJobStartLocked(name, uid);
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
- uid, null, name,
- FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
- JobProtoEnums.STOP_REASON_UNKNOWN, standbyBucket, jobid);
}
}
/** A scheduled job was finished. */
- public void noteJobFinish(String name, int uid, int stopReason, int standbyBucket, int jobid) {
+ public void noteJobFinish(String name, int uid, int stopReason) {
enforceCallingPermission();
synchronized (mStats) {
mStats.noteJobFinishLocked(name, uid, stopReason);
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
- uid, null, name,
- FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED, stopReason,
- standbyBucket, jobid);
}
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index eec68dc..be48374 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -911,10 +911,12 @@
pid = proc.pid;
name = proc.processName;
- if (proc.curAdj <= ProcessList.CACHED_APP_MIN_ADJ) {
+ if (proc.curAdj < ProcessList.CACHED_APP_MIN_ADJ
+ || proc.shouldNotFreeze) {
if (DEBUG_FREEZER) {
Slog.d(TAG_AM, "Skipping freeze for process " + pid
- + " " + name + " (not cached)");
+ + " " + name + " curAdj = " + proc.curAdj
+ + ", shouldNotFreeze = " + proc.shouldNotFreeze);
}
return;
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index c239feb1..3fd1b78 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1109,6 +1109,7 @@
app.adjTarget = null;
app.empty = false;
app.setCached(false);
+ app.shouldNotFreeze = false;
final int appUid = app.info.uid;
final int logUid = mService.mCurOomAdjUid;
@@ -1542,23 +1543,24 @@
}
boolean trackedProcState = false;
- if ((cr.flags& Context.BIND_WAIVE_PRIORITY) == 0) {
- ProcessRecord client = cr.binding.client;
- if (computeClients) {
- computeOomAdjLocked(client, cachedAdj, topApp, doingAll, now,
- cycleReEval, true);
- } else {
- client.setCurRawAdj(client.setAdj);
- client.setCurRawProcState(client.setProcState);
- }
+ ProcessRecord client = cr.binding.client;
+ if (computeClients) {
+ computeOomAdjLocked(client, cachedAdj, topApp, doingAll, now,
+ cycleReEval, true);
+ } else {
+ client.setCurRawAdj(client.setAdj);
+ client.setCurRawProcState(client.setProcState);
+ }
+
+ int clientAdj = client.getCurRawAdj();
+ int clientProcState = client.getCurRawProcState();
+
+ if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) {
if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
continue;
}
- int clientAdj = client.getCurRawAdj();
- int clientProcState = client.getCurRawProcState();
-
if (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE) {
procStateFromFGSClient = true;
}
@@ -1762,6 +1764,19 @@
+ ProcessList.makeProcStateString(procState));
}
}
+ } else { // BIND_WAIVE_PRIORITY == true
+ // BIND_WAIVE_PRIORITY bindings are special when it comes to the
+ // freezer. Processes bound via WPRI are expected to be running,
+ // but they are not promoted in the LRU list to keep them out of
+ // cached. As a result, they can freeze based on oom_adj alone.
+ // Normally, bindToDeath would fire when a cached app would die
+ // in the background, but nothing will fire when a running process
+ // pings a frozen process. Accordingly, any cached app that is
+ // bound by an unfrozen app via a WPRI binding has to remain
+ // unfrozen.
+ if (clientAdj < ProcessList.CACHED_APP_MIN_ADJ) {
+ app.shouldNotFreeze = true;
+ }
}
if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
app.treatLikeActivity = true;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c2f03ec..f2ca1da 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -167,6 +167,7 @@
int lastCompactAction; // The most recent compaction action performed for this app.
boolean frozen; // True when the process is frozen.
long freezeUnfreezeTime; // Last time the app was (un)frozen, 0 for never
+ boolean shouldNotFreeze; // True if a process has a WPRI binding from an unfrozen process
private int mCurSchedGroup; // Currently desired scheduling class
int setSchedGroup; // Last set to background scheduling class
int trimMemoryLevel; // Last selected memory trimming level
diff --git a/services/core/java/com/android/server/backup/PeopleBackupHelper.java b/services/core/java/com/android/server/backup/PeopleBackupHelper.java
index e58a051..555e006 100644
--- a/services/core/java/com/android/server/backup/PeopleBackupHelper.java
+++ b/services/core/java/com/android/server/backup/PeopleBackupHelper.java
@@ -50,7 +50,7 @@
if (DEBUG) {
Slog.d(TAG, "Handling backup of " + key);
}
- return ps.backupConversationInfos(mUserId);
+ return ps.getBackupPayload(mUserId);
}
@Override
@@ -63,6 +63,6 @@
if (DEBUG) {
Slog.d(TAG, "Handling restore of " + key);
}
- ps.restoreConversationInfos(mUserId, key, payload);
+ ps.restore(mUserId, payload);
}
}
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 7f7c9c4..441d9d9 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -16,6 +16,7 @@
package com.android.server.compat;
+import android.app.compat.ChangeIdStateCache;
import android.compat.Compatibility.ChangeConfig;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -80,6 +81,7 @@
void addChange(CompatChange change) {
synchronized (mChanges) {
mChanges.put(change.getId(), change);
+ invalidateCache();
}
}
@@ -172,6 +174,7 @@
addChange(c);
}
c.addPackageOverride(packageName, enabled);
+ invalidateCache();
}
return alreadyKnown;
}
@@ -228,6 +231,7 @@
// Should never occur, since validator is in the same process.
throw new RuntimeException("Unable to call override validator!", e);
}
+ invalidateCache();
}
return overrideExists;
}
@@ -250,6 +254,7 @@
addOverride(changeId, packageName, false);
}
+ invalidateCache();
}
}
@@ -279,6 +284,7 @@
throw new RuntimeException("Unable to call override validator!", e);
}
}
+ invalidateCache();
}
}
@@ -377,6 +383,7 @@
config.initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig"));
}
+ config.invalidateCache();
return config;
}
@@ -406,4 +413,8 @@
IOverrideValidator getOverrideValidator() {
return mOverrideValidator;
}
+
+ private void invalidateCache() {
+ ChangeIdStateCache.invalidate();
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 7cb8458..612fd39 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -178,6 +178,8 @@
float maxRefreshRate = Float.POSITIVE_INFINITY;
int lowestConsideredPriority = Vote.MIN_PRIORITY;
while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
+ minRefreshRate = 0f;
+ maxRefreshRate = Float.POSITIVE_INFINITY;
int height = Vote.INVALID_SIZE;
int width = Vote.INVALID_SIZE;
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index fbad8de..5320453 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -16,9 +16,6 @@
package com.android.server.dreams;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -27,10 +24,10 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
import android.os.IRemoteCallback;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.IBinder.DeathRecipient;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
@@ -38,15 +35,14 @@
import android.service.dreams.IDreamService;
import android.util.Slog;
import android.view.IWindowManager;
-import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
import java.io.PrintWriter;
import java.util.NoSuchElementException;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-
/**
* Internal controller for starting and stopping the current dream and managing related state.
*
@@ -86,12 +82,9 @@
}
};
- private final Runnable mStopStubbornDreamRunnable = new Runnable() {
- @Override
- public void run() {
- Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted");
- stopDream(true /*immediate*/);
- }
+ private final Runnable mStopStubbornDreamRunnable = () -> {
+ Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted");
+ stopDream(true /*immediate*/);
};
public DreamController(Context context, Handler handler, Listener listener) {
@@ -140,14 +133,6 @@
MetricsLogger.visible(mContext,
mCurrentDream.mCanDoze ? MetricsEvent.DOZING : MetricsEvent.DREAMING);
- try {
- mIWindowManager.addWindowToken(token, TYPE_DREAM, DEFAULT_DISPLAY);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Unable to add window token for dream.", ex);
- stopDream(true /*immediate*/);
- return;
- }
-
Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
intent.setComponent(name);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
@@ -216,9 +201,6 @@
}
if (oldDream.mService != null) {
- // Tell the dream that it's being stopped so that
- // it can shut down nicely before we yank its window token out from
- // under it.
try {
oldDream.mService.detach();
} catch (RemoteException ex) {
@@ -238,18 +220,7 @@
}
oldDream.releaseWakeLockIfNeeded();
- try {
- mIWindowManager.removeWindowToken(oldDream.mToken, DEFAULT_DISPLAY);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Error removing window token for dream.", ex);
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onDreamStopped(oldDream.mToken);
- }
- });
+ mHandler.post(() -> mListener.onDreamStopped(oldDream.mToken));
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -313,13 +284,10 @@
// May be called on any thread.
@Override
public void binderDied() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mService = null;
- if (mCurrentDream == DreamRecord.this) {
- stopDream(true /*immediate*/);
- }
+ mHandler.post(() -> {
+ mService = null;
+ if (mCurrentDream == DreamRecord.this) {
+ stopDream(true /*immediate*/);
}
});
}
@@ -327,16 +295,13 @@
// May be called on any thread.
@Override
public void onServiceConnected(ComponentName name, final IBinder service) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mConnected = true;
- if (mCurrentDream == DreamRecord.this && mService == null) {
- attach(IDreamService.Stub.asInterface(service));
- // Wake lock will be released once dreaming starts.
- } else {
- releaseWakeLockIfNeeded();
- }
+ mHandler.post(() -> {
+ mConnected = true;
+ if (mCurrentDream == DreamRecord.this && mService == null) {
+ attach(IDreamService.Stub.asInterface(service));
+ // Wake lock will be released once dreaming starts.
+ } else {
+ releaseWakeLockIfNeeded();
}
});
}
@@ -344,13 +309,10 @@
// May be called on any thread.
@Override
public void onServiceDisconnected(ComponentName name) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mService = null;
- if (mCurrentDream == DreamRecord.this) {
- stopDream(true /*immediate*/);
- }
+ mHandler.post(() -> {
+ mService = null;
+ if (mCurrentDream == DreamRecord.this) {
+ stopDream(true /*immediate*/);
}
});
}
@@ -373,4 +335,4 @@
}
};
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 3052e3c..eb0257e 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -268,6 +268,10 @@
}
}
+ private ComponentName getActiveDreamComponentInternal(boolean doze) {
+ return chooseDreamForUser(doze, ActivityManager.getCurrentUser());
+ }
+
private ComponentName chooseDreamForUser(boolean doze, int userId) {
if (doze) {
ComponentName dozeComponent = getDozeComponent(userId);
@@ -501,12 +505,18 @@
@Override // Binder call
public ComponentName[] getDreamComponents() {
- checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+ return getDreamComponentsForUser(UserHandle.getCallingUserId());
+ }
- final int userId = UserHandle.getCallingUserId();
+ @Override // Binder call
+ public ComponentName[] getDreamComponentsForUser(int userId) {
+ checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "getDreamComponents", null);
+
final long ident = Binder.clearCallingIdentity();
try {
- return getDreamComponentsForUser(userId);
+ return DreamManagerService.this.getDreamComponentsForUser(userId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -526,13 +536,28 @@
}
@Override // Binder call
- public ComponentName getDefaultDreamComponent() {
- checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+ public void setDreamComponentsForUser(int userId, ComponentName[] componentNames) {
+ checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "setDreamComponents", null);
- final int userId = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
try {
- return getDefaultDreamComponentForUser(userId);
+ DreamManagerService.this.setDreamComponentsForUser(userId, componentNames);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public ComponentName getDefaultDreamComponentForUser(int userId) {
+ checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "getDefaultDreamComponent", null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return DreamManagerService.this.getDefaultDreamComponentForUser(userId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -563,24 +588,25 @@
}
@Override // Binder call
- public void testDream(ComponentName dream) {
+ public void testDream(int userId, ComponentName dream) {
if (dream == null) {
throw new IllegalArgumentException("dream must not be null");
}
checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "testDream", null);
- final int callingUserId = UserHandle.getCallingUserId();
final int currentUserId = ActivityManager.getCurrentUser();
- if (callingUserId != currentUserId) {
+ if (userId != currentUserId) {
// This check is inherently prone to races but at least it's something.
Slog.w(TAG, "Aborted attempt to start a test dream while a different "
- + " user is active: callingUserId=" + callingUserId
+ + " user is active: userId=" + userId
+ ", currentUserId=" + currentUserId);
return;
}
final long ident = Binder.clearCallingIdentity();
try {
- testDreamInternal(dream, callingUserId);
+ testDreamInternal(dream, userId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -671,6 +697,11 @@
public boolean isDreaming() {
return isDreamingInternal();
}
+
+ @Override
+ public ComponentName getActiveDreamComponent(boolean doze) {
+ return getActiveDreamComponentInternal(doze);
+ }
}
private final Runnable mSystemPropertiesChanged = new Runnable() {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index eac2d24..f24699a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -21,6 +21,7 @@
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodInfo;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.InlineSuggestionsRequestInfo;
import com.android.server.LocalServices;
@@ -51,7 +52,7 @@
/**
* Hides the current input method, if visible.
*/
- public abstract void hideCurrentInputMethod();
+ public abstract void hideCurrentInputMethod(@SoftInputShowHideReason int reason);
/**
* Returns the list of installed input methods for the specified user.
@@ -106,7 +107,7 @@
}
@Override
- public void hideCurrentInputMethod() {
+ public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 87262a8..dcd0a78 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -131,6 +131,7 @@
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
import com.android.internal.inputmethod.UnbindReason;
@@ -767,6 +768,75 @@
@GuardedBy("mMethodMap")
private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
+ private static final class SoftInputShowHideHistory {
+ private Entry[] mEntries = new Entry[16];
+ private int mNextIndex = 0;
+ private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
+
+ // TODO(b/141738570): add requestWindowToken to track who request show / hide softInput.
+ private static final class Entry {
+ ClientState mClientState;
+ String mFocusedWindowString;
+ @SoftInputModeFlags
+ int mFocusedWindowSoftInputMode;
+ @SoftInputShowHideReason
+ int mReason;
+ boolean mRequestShowKeyboard;
+ // The timing of handling MSG_SHOW_SOFT_INPUT or MSG_HIDE_SOFT_INPUT.
+ long mTimestamp;
+ long mWallTime;
+ int mTargetDisplayId;
+
+ Entry(ClientState client, String focusedWindow, @SoftInputModeFlags int softInputMode,
+ @SoftInputShowHideReason int reason, boolean show) {
+ mClientState = client;
+ mFocusedWindowString = focusedWindow;
+ mFocusedWindowSoftInputMode = softInputMode;
+ mReason = reason;
+ mRequestShowKeyboard = show;
+ mTimestamp = SystemClock.uptimeMillis();
+ mWallTime = System.currentTimeMillis();
+ }
+ }
+
+ void addEntry(@NonNull Entry entry) {
+ final int index = mNextIndex;
+ mEntries[index] = entry;
+ mNextIndex = (mNextIndex + 1) % mEntries.length;
+ }
+
+ void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ final SimpleDateFormat dataFormat =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
+
+ for (int i = 0; i < mEntries.length; ++i) {
+ final Entry entry = mEntries[(i + mNextIndex) % mEntries.length];
+ if (entry == null) {
+ continue;
+ }
+ pw.print(prefix);
+ pw.println("SoftInputShowHideHistory #" + sSequenceNumber.getAndIncrement() + ":");
+
+ pw.print(prefix);
+ pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
+ + " (timestamp=" + entry.mTimestamp + ")");
+
+ pw.print(prefix);
+ pw.print(" requestShowKeyboard=" + entry.mRequestShowKeyboard);
+ pw.print(" targetDisplayId=" + entry.mTargetDisplayId);
+ pw.println(" reason=" + entry.mReason);
+
+ pw.print(prefix);
+ pw.print(" requestClient=" + entry.mClientState);
+ pw.println(" focusedWindow=" + entry.mFocusedWindowString);
+
+ pw.print(prefix);
+ pw.println(" focusedWindowSoftInputMode=" + InputMethodDebug.softInputModeToString(
+ entry.mFocusedWindowSoftInputMode));
+ }
+ }
+ }
+
/**
* Map of generated token to windowToken that is requesting
* {@link InputMethodManager#showSoftInput(View, int)}.
@@ -776,6 +846,14 @@
private final WeakHashMap<IBinder, IBinder> mShowRequestWindowMap = new WeakHashMap<>();
/**
+ * Map of generated token to windowToken that is requesting
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}.
+ * This map tracks origin of hideSoftInput requests.
+ */
+ @GuardedBy("mMethodMap")
+ private final WeakHashMap<IBinder, IBinder> mHideRequestWindowMap = new WeakHashMap<>();
+
+ /**
* A ring buffer to store the history of {@link StartInputInfo}.
*/
private static final class StartInputHistory {
@@ -933,6 +1011,11 @@
@NonNull
private final StartInputHistory mStartInputHistory = new StartInputHistory();
+ @GuardedBy("mMethodMap")
+ @NonNull
+ private final SoftInputShowHideHistory mSoftInputShowHideHistory =
+ new SoftInputShowHideHistory();
+
class SettingsObserver extends ContentObserver {
int mUserId;
boolean mRegistered = false;
@@ -989,11 +1072,13 @@
== AccessibilityService.SHOW_MODE_HIDDEN;
if (mAccessibilityRequestingNoSoftKeyboard) {
final boolean showRequested = mShowRequested;
- hideCurrentInputLocked(0, null);
+ hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE);
mShowRequested = showRequested;
} else if (mShowRequested) {
- showCurrentInputLocked(
- mCurFocusedWindow, InputMethodManager.SHOW_IMPLICIT, null);
+ showCurrentInputLocked(mCurFocusedWindow,
+ InputMethodManager.SHOW_IMPLICIT, null,
+ SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE);
}
} else {
boolean enabledChanged = false;
@@ -1618,7 +1703,9 @@
// TODO: Is it really possible that switchUserLocked() happens before system ready?
if (mSystemReady) {
- hideCurrentInputLocked(0, null);
+ hideCurrentInputLocked(
+ mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_SWITCH_USER);
+
resetCurrentMethodAndClient(UnbindReason.SWITCH_USER);
buildInputMethodListLocked(initialUserSwitch);
if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
@@ -1882,7 +1969,7 @@
executeOrSendMessage(mCurMethod,
mCaller.obtainMessageOOO(MSG_INLINE_SUGGESTIONS_REQUEST, mCurMethod,
requestInfo, new InlineSuggestionsRequestCallbackDecorator(callback,
- imi.getPackageName())));
+ imi.getPackageName(), mCurTokenDisplayId)));
} else {
callback.onInlineSuggestionsUnsupported();
}
@@ -1902,11 +1989,14 @@
@NonNull
private final String mImePackageName;
+ private final int mImeDisplayId;
+
InlineSuggestionsRequestCallbackDecorator(
@NonNull IInlineSuggestionsRequestCallback callback,
- @NonNull String imePackageName) {
+ @NonNull String imePackageName, int displayId) {
mCallback = callback;
mImePackageName = imePackageName;
+ mImeDisplayId = displayId;
}
@Override
@@ -1923,6 +2013,7 @@
+ "] doesn't match the IME package name=[" + mImePackageName
+ "].");
}
+ request.setHostDisplayId(mImeDisplayId);
mCallback.onInlineSuggestionsRequest(request, callback);
}
}
@@ -2154,7 +2245,8 @@
startInputToken, session, mCurInputContext, mCurAttribute));
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
- showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null);
+ showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null,
+ SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, (session.channel != null ? session.channel.dup() : null),
@@ -2893,7 +2985,8 @@
}
}
if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
- return showCurrentInputLocked(windowToken, flags, resultReceiver);
+ return showCurrentInputLocked(windowToken, flags, resultReceiver,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2901,7 +2994,8 @@
}
@GuardedBy("mMethodMap")
- boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver) {
+ boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
mShowRequested = true;
if (mAccessibilityRequestingNoSoftKeyboard) {
return false;
@@ -2924,9 +3018,9 @@
// create a dummy token for IMS so that IMS cannot inject windows into client app.
Binder showInputToken = new Binder();
mShowRequestWindowMap.put(showInputToken, windowToken);
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO(
- MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod,
- resultReceiver, showInputToken));
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageIIOOO(
+ MSG_SHOW_SOFT_INPUT, getImeShowFlags(), reason, mCurMethod, resultReceiver,
+ showInputToken));
mInputShown = true;
if (mHaveConnection && !mVisibleBound) {
bindCurrentInputMethodServiceLocked(
@@ -2956,7 +3050,7 @@
}
@Override
- public boolean hideSoftInput(IInputMethodClient client, int flags,
+ public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken, int flags,
ResultReceiver resultReceiver) {
int uid = Binder.getCallingUid();
synchronized (mMethodMap) {
@@ -2984,14 +3078,16 @@
}
if (DEBUG) Slog.v(TAG, "Client requesting input be hidden");
- return hideCurrentInputLocked(flags, resultReceiver);
+ return hideCurrentInputLocked(windowToken, flags, resultReceiver,
+ SoftInputShowHideReason.HIDE_SOFT_INPUT);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
- boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
+ boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
&& (mShowExplicitlyRequested || mShowForced)) {
if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
@@ -3014,12 +3110,14 @@
(mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
boolean res;
if (shouldHideSoftInput) {
+ final Binder hideInputToken = new Binder();
+ mHideRequestWindowMap.put(hideInputToken, windowToken);
// The IME will report its visible state again after the following message finally
// delivered to the IME process as an IPC. Hence the inconsistency between
// IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
// the final state.
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
- MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver));
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO(MSG_HIDE_SOFT_INPUT,
+ reason, mCurMethod, resultReceiver, hideInputToken));
res = true;
} else {
res = false;
@@ -3156,7 +3254,8 @@
Slog.w(TAG, "If you need to impersonate a foreground user/profile from"
+ " a background user, use EditorInfo.targetInputMethodUser with"
+ " INTERACT_ACROSS_USERS_FULL permission.");
- hideCurrentInputLocked(0, null);
+ hideCurrentInputLocked(
+ mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_INVALID_USER);
return InputBindResult.INVALID_USER;
}
@@ -3219,7 +3318,9 @@
// be behind any soft input window, so hide the
// soft input window if it is shown.
if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
- hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null);
+ hideCurrentInputLocked(
+ mCurFocusedWindow, InputMethodManager.HIDE_NOT_ALWAYS, null,
+ SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW);
// If focused display changed, we should unbind current method
// to make app window in previous display relayout after Ime
@@ -3245,7 +3346,8 @@
attribute, startInputFlags, startInputReason);
didStart = true;
}
- showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null);
+ showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+ SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV);
}
break;
case LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
@@ -3254,12 +3356,14 @@
case LayoutParams.SOFT_INPUT_STATE_HIDDEN:
if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward");
- hideCurrentInputLocked(0, null);
+ hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV);
}
break;
case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
if (DEBUG) Slog.v(TAG, "Window asks to hide input");
- hideCurrentInputLocked(0, null);
+ hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
break;
case LayoutParams.SOFT_INPUT_STATE_VISIBLE:
if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
@@ -3271,7 +3375,8 @@
attribute, startInputFlags, startInputReason);
didStart = true;
}
- showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null);
+ showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+ SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV);
} else {
Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because"
+ " there is no focused view that also returns true from"
@@ -3288,7 +3393,8 @@
attribute, startInputFlags, startInputReason);
didStart = true;
}
- showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null);
+ showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+ SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE);
} else {
Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because"
+ " there is no focused view that also returns true from"
@@ -3740,7 +3846,7 @@
// Send it to window manager to hide IME from IME target window.
// TODO(b/139861270): send to mCurClient.client once IMMS is aware of
// actual IME target.
- mWindowManagerInternal.hideIme(mCurClient.selfReportedDisplayId);
+ mWindowManagerInternal.hideIme(mHideRequestWindowMap.get(windowToken));
}
} else {
// Send to window manager to show IME after IME layout finishes.
@@ -3780,7 +3886,10 @@
}
long ident = Binder.clearCallingIdentity();
try {
- hideCurrentInputLocked(flags, null);
+ hideCurrentInputLocked(
+ mLastImeTargetWindow, flags, null,
+ SoftInputShowHideReason.HIDE_MY_SOFT_INPUT);
+
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3795,7 +3904,8 @@
}
long ident = Binder.clearCallingIdentity();
try {
- showCurrentInputLocked(mLastImeTargetWindow, flags, null);
+ showCurrentInputLocked(mLastImeTargetWindow, flags, null,
+ SoftInputShowHideReason.SHOW_MY_SOFT_INPUT);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3876,29 +3986,44 @@
args.recycle();
return true;
case MSG_SHOW_SOFT_INPUT:
- args = (SomeArgs)msg.obj;
+ args = (SomeArgs) msg.obj;
try {
+ final @SoftInputShowHideReason int reason = msg.arg2;
if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
- + msg.arg1 + ", " + args.arg2 + ")");
+ + args.arg3 + ", " + msg.arg1 + ", " + args.arg2 + ") for reason: "
+ + InputMethodDebug.softInputDisplayReasonToString(reason));
((IInputMethod) args.arg1).showSoftInput(
(IBinder) args.arg3, msg.arg1, (ResultReceiver) args.arg2);
+ mSoftInputShowHideHistory.addEntry(
+ new SoftInputShowHideHistory.Entry(mCurClient,
+ InputMethodDebug.objToString(mCurFocusedWindow),
+ mCurFocusedWindowSoftInputMode, reason, true /* show */));
} catch (RemoteException e) {
}
args.recycle();
return true;
case MSG_HIDE_SOFT_INPUT:
- args = (SomeArgs)msg.obj;
+ args = (SomeArgs) msg.obj;
try {
+ final @SoftInputShowHideReason int reason = msg.arg1;
if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
- + args.arg2 + ")");
- ((IInputMethod)args.arg1).hideSoftInput(0, (ResultReceiver)args.arg2);
+ + args.arg3 + ", " + args.arg2 + ") for reason: "
+ + InputMethodDebug.softInputDisplayReasonToString(reason));
+ ((IInputMethod)args.arg1).hideSoftInput(
+ (IBinder) args.arg3, 0, (ResultReceiver)args.arg2);
+ mSoftInputShowHideHistory.addEntry(
+ new SoftInputShowHideHistory.Entry(mCurClient,
+ InputMethodDebug.objToString(mCurFocusedWindow),
+ mCurFocusedWindowSoftInputMode, reason, false /* show */));
} catch (RemoteException e) {
}
args.recycle();
return true;
case MSG_HIDE_CURRENT_INPUT_METHOD:
synchronized (mMethodMap) {
- hideCurrentInputLocked(0, null);
+ final @SoftInputShowHideReason int reason = (int) msg.obj;
+ hideCurrentInputLocked(mCurFocusedWindow, 0, null, reason);
+
}
return true;
case MSG_INITIALIZE_IME:
@@ -4682,9 +4807,9 @@
}
@Override
- public void hideCurrentInputMethod() {
+ public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
- mService.mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD);
+ mService.mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget();
}
@Override
@@ -4841,6 +4966,9 @@
p.println(" mStartInputHistory:");
mStartInputHistory.dump(pw, " ");
+
+ p.println(" mSoftInputShowHideHistory:");
+ mSoftInputShowHideHistory.dump(pw, " ");
}
p.println(" ");
@@ -5300,7 +5428,8 @@
final String nextIme;
final List<InputMethodInfo> nextEnabledImes;
if (userId == mSettings.getCurrentUserId()) {
- hideCurrentInputLocked(0, null);
+ hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+ SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
unbindCurrentMethodLocked();
// Reset the current IME
resetSelectedInputMethodAndSubtypeLocked(null);
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 4904061..e60b910 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -73,6 +73,7 @@
import com.android.internal.inputmethod.IMultiClientInputMethod;
import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.IMultiClientInputMethodSession;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
import com.android.internal.inputmethod.UnbindReason;
@@ -174,7 +175,7 @@
}
@Override
- public void hideCurrentInputMethod() {
+ public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
reportNotSupported();
}
@@ -1499,7 +1500,8 @@
@BinderThread
@Override
public boolean hideSoftInput(
- IInputMethodClient client, int flags, ResultReceiver resultReceiver) {
+ IInputMethodClient client, IBinder windowToken, int flags,
+ ResultReceiver resultReceiver) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int userId = UserHandle.getUserId(callingUid);
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 63054cf..b4ec359 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -40,7 +40,6 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
import android.content.pm.Signature;
@@ -80,8 +79,10 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/** Implementation of {@link AppIntegrityManagerService}. */
public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
@@ -102,12 +103,14 @@
private static final String TAG = "AppIntegrityManagerServiceImpl";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
- private static final String PACKAGE_INSTALLER = "com.google.android.packageinstaller";
private static final String BASE_APK_FILE = "base.apk";
private static final String ALLOWED_INSTALLERS_METADATA_NAME = "allowed-installers";
private static final String ALLOWED_INSTALLER_DELIMITER = ",";
private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|";
+ private static final Set<String> PACKAGE_INSTALLER = new HashSet<>(
+ Arrays.asList("com.google.android.packageinstaller", "com.android.packageinstaller"));
+
// Access to files inside mRulesDir is protected by mRulesLock;
private final Context mContext;
private final Handler mHandler;
@@ -115,8 +118,6 @@
private final RuleEvaluationEngine mEvaluationEngine;
private final IntegrityFileManager mIntegrityFileManager;
- private final boolean mCheckIntegrityForRuleProviders;
-
/** Create an instance of {@link AppIntegrityManagerServiceImpl}. */
public static AppIntegrityManagerServiceImpl create(Context context) {
HandlerThread handlerThread = new HandlerThread("AppIntegrityManagerServiceHandler");
@@ -127,13 +128,7 @@
LocalServices.getService(PackageManagerInternal.class),
RuleEvaluationEngine.getRuleEvaluationEngine(),
IntegrityFileManager.getInstance(),
- handlerThread.getThreadHandler(),
- Settings.Global.getInt(
- context.getContentResolver(),
- Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
- 0)
- == 1
- );
+ handlerThread.getThreadHandler());
}
@VisibleForTesting
@@ -142,14 +137,12 @@
PackageManagerInternal packageManagerInternal,
RuleEvaluationEngine evaluationEngine,
IntegrityFileManager integrityFileManager,
- Handler handler,
- boolean checkIntegrityForRuleProviders) {
+ Handler handler) {
mContext = context;
mPackageManagerInternal = packageManagerInternal;
mEvaluationEngine = evaluationEngine;
mIntegrityFileManager = integrityFileManager;
mHandler = handler;
- mCheckIntegrityForRuleProviders = checkIntegrityForRuleProviders;
IntentFilter integrityVerificationFilter = new IntentFilter();
integrityVerificationFilter.addAction(ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
@@ -202,7 +195,7 @@
intent,
/* onFinished= */ null,
/* handler= */ null);
- } catch (IntentSender.SendIntentException e) {
+ } catch (Exception e) {
Slog.e(TAG, "Error sending status feedback.", e);
}
});
@@ -260,7 +253,7 @@
String installerPackageName = getInstallerPackageName(intent);
// Skip integrity verification if the verifier is doing the install.
- if (!mCheckIntegrityForRuleProviders
+ if (!integrityCheckIncludesRuleProvider()
&& isRuleProvider(installerPackageName)) {
Slog.i(TAG, "Verifier doing the install. Skipping integrity check.");
mPackageManagerInternal.setIntegrityVerificationResult(
@@ -272,8 +265,6 @@
List<String> installerCertificates =
getInstallerCertificateFingerprint(installerPackageName);
- Slog.w(TAG, appCertificates.toString());
-
AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
builder.setPackageName(getPackageNameNormalized(packageName));
@@ -377,7 +368,7 @@
// A common way for apps to install packages is to send an intent to PackageInstaller. In
// that case, the installer will always show up as PackageInstaller which is not what we
// want.
- if (installer.equals(PACKAGE_INSTALLER)) {
+ if (PACKAGE_INSTALLER.contains(installer)) {
int originatingUid = intent.getIntExtra(EXTRA_ORIGINATING_UID, -1);
if (originatingUid < 0) {
Slog.e(TAG, "Installer is package installer but originating UID not found.");
@@ -632,4 +623,12 @@
return getAllowedRuleProviders().stream()
.anyMatch(ruleProvider -> ruleProvider.equals(installerPackageName));
}
+
+ private boolean integrityCheckIncludesRuleProvider() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
+ 0)
+ == 1;
+ }
}
diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java
index b3546dc..a3523f2 100644
--- a/services/core/java/com/android/server/location/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/GnssConfiguration.java
@@ -80,7 +80,7 @@
// Represents an HAL interface version. Instances of this class are created in the JNI layer
// and returned through native methods.
- private static class HalInterfaceVersion {
+ static class HalInterfaceVersion {
final int mMajor;
final int mMinor;
@@ -206,6 +206,10 @@
native_set_satellite_blacklist(constellations, svids);
}
+ HalInterfaceVersion getHalInterfaceVersion() {
+ return native_get_gnss_configuration_version();
+ }
+
interface SetCarrierProperty {
boolean set(int value);
}
@@ -232,8 +236,7 @@
logConfigurations();
- final HalInterfaceVersion gnssConfigurationIfaceVersion =
- native_get_gnss_configuration_version();
+ final HalInterfaceVersion gnssConfigurationIfaceVersion = getHalInterfaceVersion();
if (gnssConfigurationIfaceVersion != null) {
// Set to a range checked value.
if (isConfigEsExtensionSecSupported(gnssConfigurationIfaceVersion)
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 5c2bf26..58e332a 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -807,10 +807,15 @@
locationRequest.setProvider(provider);
- // Ignore location settings if in emergency mode.
- if (isUserEmergency && mNIHandler.getInEmergency()) {
- locationRequest.setLocationSettingsIgnored(true);
- durationMillis *= EMERGENCY_LOCATION_UPDATE_DURATION_MULTIPLIER;
+ // Ignore location settings if in emergency mode. This is only allowed for
+ // isUserEmergency request (introduced in HAL v2.0), or DBH request in HAL v1.1.
+ if (mNIHandler.getInEmergency()) {
+ GnssConfiguration.HalInterfaceVersion halVersion =
+ mGnssConfiguration.getHalInterfaceVersion();
+ if (isUserEmergency || (halVersion.mMajor < 2 && !independentFromGnss)) {
+ locationRequest.setLocationSettingsIgnored(true);
+ durationMillis *= EMERGENCY_LOCATION_UPDATE_DURATION_MULTIPLIER;
+ }
}
Log.i(TAG,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 475f229..ceb1cd4 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -51,6 +51,7 @@
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.BIND_NOT_PERCEPTIBLE;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
@@ -380,7 +381,7 @@
private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
- private static final long DELAY_FOR_ASSISTANT_TIME = 100;
+ private static final long DELAY_FOR_ASSISTANT_TIME = 200;
private static final String ACTION_NOTIFICATION_TIMEOUT =
NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
@@ -3448,16 +3449,10 @@
ArrayList<ConversationChannelWrapper> conversations =
mPreferencesHelper.getConversations(onlyImportant);
for (ConversationChannelWrapper conversation : conversations) {
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
- .setPackage(conversation.getPkg())
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
- .setShortcutIds(Arrays.asList(
- conversation.getNotificationChannel().getConversationId()));
- List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
- query, UserHandle.of(UserHandle.getUserId(conversation.getUid())));
- if (shortcuts != null && !shortcuts.isEmpty()) {
- conversation.setShortcutInfo(shortcuts.get(0));
- }
+ conversation.setShortcutInfo(getShortcutInfo(
+ conversation.getNotificationChannel().getConversationId(),
+ conversation.getPkg(),
+ UserHandle.of(UserHandle.getUserId(conversation.getUid()))));
}
return new ParceledListSlice<>(conversations);
}
@@ -3477,16 +3472,10 @@
ArrayList<ConversationChannelWrapper> conversations =
mPreferencesHelper.getConversations(pkg, uid);
for (ConversationChannelWrapper conversation : conversations) {
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
- .setPackage(pkg)
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
- .setShortcutIds(Arrays.asList(
- conversation.getNotificationChannel().getConversationId()));
- List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
- query, UserHandle.of(UserHandle.getUserId(uid)));
- if (shortcuts != null && !shortcuts.isEmpty()) {
- conversation.setShortcutInfo(shortcuts.get(0));
- }
+ conversation.setShortcutInfo(getShortcutInfo(
+ conversation.getNotificationChannel().getConversationId(),
+ pkg,
+ UserHandle.of(UserHandle.getUserId(uid))));
}
return new ParceledListSlice<>(conversations);
}
@@ -5646,6 +5635,8 @@
}
}
+ r.setShortcutInfo(getShortcutInfo(notification.getShortcutId(), pkg, user));
+
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
r.getSbn().getOverrideGroupKey() != null)) {
return;
@@ -5959,20 +5950,33 @@
return false;
}
+ private ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (shortcutId == null || packageName == null || user == null) {
+ return null;
+ }
+ LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
+ if (packageName != null) {
+ query.setPackage(packageName);
+ }
+ if (shortcutId != null) {
+ query.setShortcutIds(Arrays.asList(shortcutId));
+ }
+ query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED);
+ List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
+ ShortcutInfo shortcutInfo = shortcuts != null && shortcuts.size() > 0
+ ? shortcuts.get(0)
+ : null;
+ return shortcutInfo;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean hasValidShortcutInfo(String shortcutId, String packageName, UserHandle user) {
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
- if (packageName != null) {
- query.setPackage(packageName);
- }
- if (shortcutId != null) {
- query.setShortcutIds(Arrays.asList(shortcutId));
- }
- query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED);
- List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
- ShortcutInfo shortcutInfo = shortcuts != null && shortcuts.size() > 0
- ? shortcuts.get(0)
- : null;
- return shortcutInfo != null;
+ ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user);
+ return shortcutInfo != null && shortcutInfo.isLongLived();
}
private void logBubbleError(String key, String failureMessage) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f92e1fc..9d243e4 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -35,6 +35,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.media.AudioAttributes;
import android.media.AudioSystem;
@@ -166,6 +167,7 @@
private boolean mAllowBubble;
private Light mLight;
private boolean mIsNotConversationOverride;
+ private ShortcutInfo mShortcutInfo;
/**
* This list contains system generated smart actions from NAS, app-generated smart actions are
* stored in Notification.actions with isContextual() set to true.
@@ -1338,14 +1340,20 @@
return hasCustomRemoteView && !hasDecoratedStyle;
}
- /** Whether this notification is a conversation notification. */
+ public void setShortcutInfo(ShortcutInfo shortcutInfo) {
+ mShortcutInfo = shortcutInfo;
+ }
+
+ /**
+ * Whether this notification is a conversation notification.
+ */
public boolean isConversation() {
Notification notification = getNotification();
if (mChannel.isDemoted()
|| !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
return false;
}
- if (notification.getShortcutId() == null
+ if (mShortcutInfo == null
&& !FeatureFlagUtils.isEnabled(
mContext, FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ)) {
return false;
@@ -1353,7 +1361,6 @@
if (mIsNotConversationOverride) {
return false;
}
- // STOPSHIP b/137397357: Check shortcut to make a further decision
return true;
}
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index f3c9128..7c9be2d 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -21,17 +21,17 @@
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
+import android.content.om.OverlayableInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.Build.VERSION_CODES;
-import android.os.IIdmap2;
+import android.os.OverlayablePolicy;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Slog;
-import com.android.server.om.OverlayManagerServiceImpl.PackageManagerHelper;
-
import java.io.File;
+import java.io.IOException;
/**
* Handle the creation and deletion of idmap files.
@@ -55,11 +55,11 @@
VENDOR_IS_Q_OR_LATER = isQOrLater;
}
- private final PackageManagerHelper mPackageManager;
+ private final OverlayableInfoCallback mOverlayableCallback;
private final IdmapDaemon mIdmapDaemon;
- IdmapManager(final PackageManagerHelper packageManager) {
- mPackageManager = packageManager;
+ IdmapManager(final OverlayableInfoCallback verifyCallback) {
+ mOverlayableCallback = verifyCallback;
mIdmapDaemon = IdmapDaemon.getInstance();
}
@@ -148,40 +148,67 @@
private int calculateFulfilledPolicies(@NonNull final PackageInfo targetPackage,
@NonNull final PackageInfo overlayPackage, int userId) {
final ApplicationInfo ai = overlayPackage.applicationInfo;
- int fulfilledPolicies = IIdmap2.POLICY_PUBLIC;
+ int fulfilledPolicies = OverlayablePolicy.PUBLIC;
// Overlay matches target signature
- if (mPackageManager.signaturesMatching(targetPackage.packageName,
+ if (mOverlayableCallback.signaturesMatching(targetPackage.packageName,
overlayPackage.packageName, userId)) {
- fulfilledPolicies |= IIdmap2.POLICY_SIGNATURE;
+ fulfilledPolicies |= OverlayablePolicy.SIGNATURE;
+ }
+
+ // Overlay matches actor signature
+ if (matchesActorSignature(targetPackage, overlayPackage, userId)) {
+ fulfilledPolicies |= OverlayablePolicy.ACTOR_SIGNATURE;
}
// Vendor partition (/vendor)
if (ai.isVendor()) {
- return fulfilledPolicies | IIdmap2.POLICY_VENDOR_PARTITION;
+ return fulfilledPolicies | OverlayablePolicy.VENDOR_PARTITION;
}
// Product partition (/product)
if (ai.isProduct()) {
- return fulfilledPolicies | IIdmap2.POLICY_PRODUCT_PARTITION;
+ return fulfilledPolicies | OverlayablePolicy.PRODUCT_PARTITION;
}
// Odm partition (/odm)
if (ai.isOdm()) {
- return fulfilledPolicies | IIdmap2.POLICY_ODM_PARTITION;
+ return fulfilledPolicies | OverlayablePolicy.ODM_PARTITION;
}
// Oem partition (/oem)
if (ai.isOem()) {
- return fulfilledPolicies | IIdmap2.POLICY_OEM_PARTITION;
+ return fulfilledPolicies | OverlayablePolicy.OEM_PARTITION;
}
// System_ext partition (/system_ext) is considered as system
// Check this last since every partition except for data is scanned as system in the PMS.
if (ai.isSystemApp() || ai.isSystemExt()) {
- return fulfilledPolicies | IIdmap2.POLICY_SYSTEM_PARTITION;
+ return fulfilledPolicies | OverlayablePolicy.SYSTEM_PARTITION;
}
return fulfilledPolicies;
}
+
+ private boolean matchesActorSignature(@NonNull PackageInfo targetPackage,
+ @NonNull PackageInfo overlayPackage, int userId) {
+ String targetOverlayableName = overlayPackage.targetOverlayableName;
+ if (targetOverlayableName != null) {
+ try {
+ OverlayableInfo overlayableInfo = mOverlayableCallback.getOverlayableForTarget(
+ targetPackage.packageName, targetOverlayableName, userId);
+ if (overlayableInfo != null) {
+ String actorPackageName = OverlayActorEnforcer.getPackageNameForActor(
+ overlayableInfo.actor, mOverlayableCallback.getNamedActors()).first;
+ if (mOverlayableCallback.signaturesMatching(actorPackageName,
+ overlayPackage.packageName, userId)) {
+ return true;
+ }
+ }
+ } catch (IOException ignored) {
+ }
+ }
+
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index 4c85603..40efb7c 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -17,7 +17,6 @@
package com.android.server.om;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.om.OverlayInfo;
import android.content.om.OverlayableInfo;
import android.content.pm.ApplicationInfo;
@@ -46,7 +45,7 @@
// By default, the reason is not logged to prevent leaks of why it failed
private static final boolean DEBUG_REASON = false;
- private final VerifyCallback mVerifyCallback;
+ private final OverlayableInfoCallback mOverlayableCallback;
/**
* @return nullable actor result with {@link ActorState} failure status
@@ -80,8 +79,8 @@
return Pair.create(packageName, ActorState.ALLOWED);
}
- public OverlayActorEnforcer(@NonNull VerifyCallback verifyCallback) {
- mVerifyCallback = verifyCallback;
+ public OverlayActorEnforcer(@NonNull OverlayableInfoCallback overlayableCallback) {
+ mOverlayableCallback = overlayableCallback;
}
void enforceActor(@NonNull OverlayInfo overlayInfo, @NonNull String methodName,
@@ -117,7 +116,7 @@
return ActorState.ALLOWED;
}
- String[] callingPackageNames = mVerifyCallback.getPackagesForUid(callingUid);
+ String[] callingPackageNames = mOverlayableCallback.getPackagesForUid(callingUid);
if (ArrayUtils.isEmpty(callingPackageNames)) {
return ActorState.NO_PACKAGES_FOR_UID;
}
@@ -132,12 +131,12 @@
if (TextUtils.isEmpty(targetOverlayableName)) {
try {
- if (mVerifyCallback.doesTargetDefineOverlayable(targetPackageName, userId)) {
+ if (mOverlayableCallback.doesTargetDefineOverlayable(targetPackageName, userId)) {
return ActorState.MISSING_TARGET_OVERLAYABLE_NAME;
} else {
// If there's no overlayable defined, fallback to the legacy permission check
try {
- mVerifyCallback.enforcePermission(
+ mOverlayableCallback.enforcePermission(
android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, methodName);
// If the previous method didn't throw, check passed
@@ -153,7 +152,7 @@
OverlayableInfo targetOverlayable;
try {
- targetOverlayable = mVerifyCallback.getOverlayableForTarget(targetPackageName,
+ targetOverlayable = mOverlayableCallback.getOverlayableForTarget(targetPackageName,
targetOverlayableName, userId);
} catch (IOException e) {
return ActorState.UNABLE_TO_GET_TARGET;
@@ -167,7 +166,7 @@
if (TextUtils.isEmpty(actor)) {
// If there's no actor defined, fallback to the legacy permission check
try {
- mVerifyCallback.enforcePermission(
+ mOverlayableCallback.enforcePermission(
android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, methodName);
// If the previous method didn't throw, check passed
@@ -177,7 +176,7 @@
}
}
- Map<String, Map<String, String>> namedActors = mVerifyCallback.getNamedActors();
+ Map<String, Map<String, String>> namedActors = mOverlayableCallback.getNamedActors();
Pair<String, ActorState> actorUriPair = getPackageNameForActor(actor, namedActors);
ActorState actorUriState = actorUriPair.second;
if (actorUriState != ActorState.ALLOWED) {
@@ -185,7 +184,7 @@
}
String packageName = actorUriPair.first;
- PackageInfo packageInfo = mVerifyCallback.getPackageInfo(packageName, userId);
+ PackageInfo packageInfo = mOverlayableCallback.getPackageInfo(packageName, userId);
if (packageInfo == null) {
return ActorState.MISSING_APP_INFO;
}
@@ -211,7 +210,7 @@
* For easier logging/debugging, a set of all possible failure/success states when running
* enforcement.
*/
- enum ActorState {
+ public enum ActorState {
ALLOWED,
INVALID_ACTOR,
MISSING_NAMESPACE,
@@ -228,53 +227,4 @@
UNABLE_TO_GET_TARGET,
MISSING_LEGACY_PERMISSION
}
-
- /**
- * Delegate to the system for querying information about packages.
- */
- public interface VerifyCallback {
-
- /**
- * Read from the APK and AndroidManifest of a package to return the overlayable defined for
- * a given name.
- *
- * @throws IOException if the target can't be read
- */
- @Nullable
- OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
- @Nullable String targetOverlayableName, int userId)
- throws IOException;
-
- /**
- * @see android.content.pm.PackageManager#getPackagesForUid(int)
- */
- @Nullable
- String[] getPackagesForUid(int uid);
-
- /**
- * @param userId user to filter package visibility by
- * @see android.content.pm.PackageManager#getPackageInfo(String, int)
- */
- @Nullable
- PackageInfo getPackageInfo(@NonNull String packageName, int userId);
-
- /**
- * @return map of system pre-defined, uniquely named actors; keys are namespace,
- * value maps actor name to package name
- */
- @NonNull
- Map<String, Map<String, String>> getNamedActors();
-
- /**
- * @return true if the target package has declared an overlayable
- */
- boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
- throws RemoteException, IOException;
-
- /**
- * @throws SecurityException containing message if the caller doesn't have the given
- * permission
- */
- void enforcePermission(String permission, String message) throws SecurityException;
- }
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f221285..c81f7cd 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -226,7 +226,7 @@
private final AtomicFile mSettingsFile;
- private final PackageManagerHelper mPackageManager;
+ private final PackageManagerHelperImpl mPackageManager;
private final UserManagerService mUserManager;
@@ -244,7 +244,7 @@
traceBegin(TRACE_TAG_RRO, "OMS#OverlayManagerService");
mSettingsFile = new AtomicFile(
new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
- mPackageManager = new PackageManagerHelper(context);
+ mPackageManager = new PackageManagerHelperImpl(context);
mUserManager = UserManagerService.getInstance();
IdmapManager im = new IdmapManager(mPackageManager);
mSettings = new OverlayManagerSettings();
@@ -1053,14 +1053,8 @@
}
}
- /**
- * Delegate for {@link android.content.pm.PackageManager} and {@link PackageManagerInternal}
- * functionality, separated for easy testing.
- *
- * @hide
- */
- public static final class PackageManagerHelper implements
- OverlayManagerServiceImpl.PackageManagerHelper, OverlayActorEnforcer.VerifyCallback {
+ private static final class PackageManagerHelperImpl implements PackageManagerHelper,
+ OverlayableInfoCallback {
private final Context mContext;
private final IPackageManager mPackageManager;
@@ -1073,7 +1067,7 @@
// behind until all pending intents have been processed.
private final SparseArray<HashMap<String, PackageInfo>> mCache = new SparseArray<>();
- PackageManagerHelper(Context context) {
+ PackageManagerHelperImpl(Context context) {
mContext = context;
mPackageManager = getPackageManager();
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
@@ -1132,7 +1126,7 @@
@Nullable
@Override
public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
- @Nullable String targetOverlayableName, int userId)
+ @NonNull String targetOverlayableName, int userId)
throws IOException {
PackageInfo packageInfo = getPackageInfo(packageName, userId);
if (packageInfo == null) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 2493057..5734271 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -18,7 +18,6 @@
import static android.content.om.OverlayInfo.STATE_DISABLED;
import static android.content.om.OverlayInfo.STATE_ENABLED;
-import static android.content.om.OverlayInfo.STATE_ENABLED_IMMUTABLE;
import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
import static android.content.om.OverlayInfo.STATE_OVERLAY_IS_BEING_REPLACED;
@@ -806,11 +805,4 @@
**/
void onOverlaysChanged(@NonNull String targetPackage, int userId);
}
-
- interface PackageManagerHelper {
- PackageInfo getPackageInfo(@NonNull String packageName, int userId);
- boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2,
- int userId);
- List<PackageInfo> getOverlayPackages(int userId);
- }
}
diff --git a/services/core/java/com/android/server/om/OverlayableInfoCallback.java b/services/core/java/com/android/server/om/OverlayableInfoCallback.java
new file mode 100644
index 0000000..6b81884
--- /dev/null
+++ b/services/core/java/com/android/server/om/OverlayableInfoCallback.java
@@ -0,0 +1,85 @@
+/*
+ * 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.om;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.om.OverlayableInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+
+import com.android.server.pm.PackageManagerServiceUtils;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Delegate to the system for querying information about overlayables and packages.
+ */
+public interface OverlayableInfoCallback {
+
+ /**
+ * Read from the APK and AndroidManifest of a package to return the overlayable defined for
+ * a given name.
+ *
+ * @throws IOException if the target can't be read
+ */
+ @Nullable
+ OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
+ @NonNull String targetOverlayableName, int userId)
+ throws IOException;
+
+ /**
+ * @see PackageManager#getPackagesForUid(int)
+ */
+ @Nullable
+ String[] getPackagesForUid(int uid);
+
+ /**
+ * @param userId user to filter package visibility by
+ * @see PackageManager#getPackageInfo(String, int)
+ */
+ @Nullable
+ PackageInfo getPackageInfo(@NonNull String packageName, int userId);
+
+ /**
+ * @return map of system pre-defined, uniquely named actors; keys are namespace,
+ * value maps actor name to package name
+ */
+ @NonNull
+ Map<String, Map<String, String>> getNamedActors();
+
+ /**
+ * @return true if the target package has declared an overlayable
+ */
+ boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
+ throws RemoteException, IOException;
+
+ /**
+ * @throws SecurityException containing message if the caller doesn't have the given
+ * permission
+ */
+ void enforcePermission(String permission, String message) throws SecurityException;
+
+ /**
+ * @return true if {@link PackageManagerServiceUtils#compareSignatures} run on both packages
+ * in the system returns {@link PackageManager#SIGNATURE_MATCH}
+ */
+ boolean signaturesMatching(@NonNull String pkgName1, @NonNull String pkgName2, int userId);
+}
diff --git a/services/core/java/com/android/server/om/PackageManagerHelper.java b/services/core/java/com/android/server/om/PackageManagerHelper.java
new file mode 100644
index 0000000..ec9c5e6
--- /dev/null
+++ b/services/core/java/com/android/server/om/PackageManagerHelper.java
@@ -0,0 +1,36 @@
+/*
+ * 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.om;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+
+import java.util.List;
+
+/**
+ * Delegate for {@link PackageManager} and {@link PackageManagerInternal} functionality,
+ * separated for easy testing.
+ *
+ * @hide
+ */
+interface PackageManagerHelper {
+ PackageInfo getPackageInfo(@NonNull String packageName, int userId);
+ boolean signaturesMatching(@NonNull String pkgName1, @NonNull String pkgName2, int userId);
+ List<PackageInfo> getOverlayPackages(int userId);
+}
diff --git a/services/core/java/com/android/server/people/PeopleServiceInternal.java b/services/core/java/com/android/server/people/PeopleServiceInternal.java
index 31fa4d1..8b4740b 100644
--- a/services/core/java/com/android/server/people/PeopleServiceInternal.java
+++ b/services/core/java/com/android/server/people/PeopleServiceInternal.java
@@ -17,6 +17,7 @@
package com.android.server.people;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.CancellationSignal;
import android.service.appprediction.IPredictionService;
@@ -34,16 +35,17 @@
@NonNull CancellationSignal signal);
/**
- * The number conversation infos will be dynamic, based on the currently installed apps on the
- * device. All of which should be combined into a single blob to be backed up.
+ * Returns a backup payload that contains conversation infos. The number conversation infos will
+ * be dynamic, based on the currently installed apps on the device. All of which should be
+ * combined into a single blob to be backed up.
*/
- public abstract byte[] backupConversationInfos(@UserIdInt int userId);
+ @Nullable
+ public abstract byte[] getBackupPayload(@UserIdInt int userId);
/**
- * Multiple conversation infos may exist in the restore payload, child classes are required to
- * manage the restoration based on how individual conversation infos were originally combined
- * during backup.
+ * Restores conversation infos stored in payload blob. Multiple conversation infos may exist in
+ * the restore payload, child classes are required to manage the restoration based on how
+ * individual conversation infos were originally combined during backup.
*/
- public abstract void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
- @NonNull byte[] payload);
+ public abstract void restore(@UserIdInt int userId, @NonNull byte[] payload);
}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index ae6e058..0ad0b23 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -354,14 +354,13 @@
* Grants access based on an interaction between a calling and target package, granting
* visibility of the caller from the target.
*
- * @param callingUid the uid initiating the interaction
- * @param targetUid the uid being interacted with and thus gaining visibility of the
- * initiating uid.
+ * @param recipientUid the uid gaining visibility of the {@code visibleUid}.
+ * @param visibleUid the uid becoming visible to the {@recipientUid}
*/
- public void grantImplicitAccess(int callingUid, int targetUid) {
- if (targetUid != callingUid
- && mImplicitlyQueryable.add(targetUid, callingUid) && DEBUG_LOGGING) {
- Slog.wtf(TAG, "implicit access granted: " + targetUid + " -> " + callingUid);
+ public void grantImplicitAccess(int recipientUid, int visibleUid) {
+ if (recipientUid != visibleUid
+ && mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) {
+ Slog.wtf(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
}
}
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 85810e3..f497f11 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.InstantAppResolveInfo;
import android.content.pm.PackageManager;
@@ -56,6 +57,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.IntentResolver;
import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.PackageInfoUtils.CachedApplicationInfoGenerator;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.PrintWriter;
@@ -272,6 +274,7 @@
return null;
}
List<ProviderInfo> providerList = null;
+ CachedApplicationInfoGenerator appInfoGenerator = null;
synchronized (mLock) {
for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) {
final ParsedProvider p = mProviders.mProviders.valueAt(i);
@@ -300,8 +303,18 @@
&& (p.getMetaData() == null || !p.getMetaData().containsKey(metaDataKey))) {
continue;
}
+ if (appInfoGenerator == null) {
+ appInfoGenerator = new CachedApplicationInfoGenerator();
+ }
+ final PackageUserState state = ps.readUserState(userId);
+ final ApplicationInfo appInfo =
+ appInfoGenerator.generate(pkg, flags, state, userId, ps);
+ if (appInfo == null) {
+ continue;
+ }
+
final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
- pkg, p, flags, ps.readUserState(userId), userId, ps);
+ pkg, p, flags, state, appInfo, userId, ps);
if (info == null) {
continue;
}
@@ -330,14 +343,20 @@
if (pkg == null) {
return null;
}
- return PackageInfoUtils.generateProviderInfo(pkg, p, flags,
- ps.readUserState(userId), userId, ps);
+ final PackageUserState state = ps.readUserState(userId);
+ ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, flags, state, userId, ps);
+ if (appInfo == null) {
+ return null;
+ }
+ return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, appInfo, userId, ps);
}
}
void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo, boolean safeMode,
int userId) {
synchronized (mLock) {
+ CachedApplicationInfoGenerator appInfoGenerator = null;
for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) {
final ParsedProvider p = mProvidersByAuthority.valueAt(i);
if (!p.isSyncable()) {
@@ -359,9 +378,18 @@
if (safeMode && !pkg.isSystem()) {
continue;
}
- final ProviderInfo info =
- PackageInfoUtils.generateProviderInfo(pkg, p, 0,
- ps.readUserState(userId), userId, ps);
+ if (appInfoGenerator == null) {
+ appInfoGenerator = new CachedApplicationInfoGenerator();
+ }
+ final PackageUserState state = ps.readUserState(userId);
+ final ApplicationInfo appInfo =
+ appInfoGenerator.generate(pkg, 0, state, userId, ps);
+ if (appInfo == null) {
+ continue;
+ }
+
+ final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
+ pkg, p, 0, state, appInfo, userId, ps);
if (info == null) {
continue;
}
@@ -1706,8 +1734,13 @@
if (userState.instantApp && ps.isUpdateAvailable()) {
return null;
}
+ final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, mFlags, userState, userId, ps);
+ if (appInfo == null) {
+ return null;
+ }
ProviderInfo pi = PackageInfoUtils.generateProviderInfo(pkg, provider, mFlags,
- userState, userId, ps);
+ userState, appInfo, userId, ps);
if (pi == null) {
return null;
}
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index cf85b0f..0eaac41 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -403,7 +403,7 @@
@GuardedBy("mService.mLock")
public void grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
- int instantAppId, int targetAppId) {
+ int recipientUid, int instantAppId) {
if (mInstalledInstantAppUids == null) {
return; // no instant apps installed; no need to grant
}
@@ -411,7 +411,7 @@
if (instantAppList == null || !instantAppList.get(instantAppId)) {
return; // instant app id isn't installed; no need to grant
}
- if (instantAppList.get(targetAppId)) {
+ if (instantAppList.get(recipientUid)) {
return; // target app id is an instant app; no need to grant
}
if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
@@ -428,10 +428,10 @@
targetAppList = new SparseArray<>();
mInstantGrants.put(userId, targetAppList);
}
- SparseBooleanArray instantGrantList = targetAppList.get(targetAppId);
+ SparseBooleanArray instantGrantList = targetAppList.get(recipientUid);
if (instantGrantList == null) {
instantGrantList = new SparseBooleanArray();
- targetAppList.put(targetAppId, instantGrantList);
+ targetAppList.put(recipientUid, instantGrantList);
}
instantGrantList.put(instantAppId, true /*granted*/);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 077fc6f..74cb93d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5459,7 +5459,13 @@
return null;
}
PackageUserState state = ps.readUserState(userId);
- return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, userId, ps);
+ final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, flags, state, userId, ps);
+ if (appInfo == null) {
+ return null;
+ }
+ return PackageInfoUtils.generateProviderInfo(
+ pkg, p, flags, state, appInfo, userId, ps);
}
}
return null;
@@ -10969,10 +10975,10 @@
// to null here, only to reset them at a later point.
Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(),
- AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
- AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
- PackageInfoWithoutStateUtils.appInfoFlags(parsedPackage),
- PackageInfoWithoutStateUtils.appInfoPrivateFlags(parsedPackage),
+ AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
+ PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
UserManagerService.getInstance(),
usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
parsedPackage.getMimeGroups());
@@ -11164,6 +11170,8 @@
// TODO(b/135203078): Remove, move to constructor
pkgSetting.pkg = parsedPackage;
pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);
+ pkgSetting.pkgPrivateFlags =
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);
if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {
pkgSetting.versionCode = parsedPackage.getLongVersionCode();
}
@@ -11772,14 +11780,22 @@
final String pkgName = pkg.getPackageName();
if (mCustomResolverComponentName != null &&
mCustomResolverComponentName.getPackageName().equals(pkg.getPackageName())) {
- setUpCustomResolverActivity(pkg);
+ setUpCustomResolverActivity(pkg, pkgSetting);
}
if (pkg.getPackageName().equals("android")) {
synchronized (mLock) {
// Set up information for our fall-back user intent resolution activity.
mPlatformPackage = pkg;
+
+ // The instance stored in PackageManagerService is special cased to be non-user
+ // specific, so initialize all the needed fields here.
mAndroidApplication = pkg.toAppInfoWithoutState();
+ mAndroidApplication.flags = PackageInfoUtils.appInfoFlags(pkg, pkgSetting);
+ mAndroidApplication.privateFlags =
+ PackageInfoUtils.appInfoPrivateFlags(pkg, pkgSetting);
+ mAndroidApplication.initForUser(UserHandle.USER_SYSTEM);
+
if (!mResolverReplaced) {
mResolveActivity.applicationInfo = mAndroidApplication;
mResolveActivity.name = ResolverActivity.class.getName();
@@ -11941,11 +11957,20 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- private void setUpCustomResolverActivity(AndroidPackage pkg) {
+ private void setUpCustomResolverActivity(AndroidPackage pkg, PackageSetting pkgSetting) {
synchronized (mLock) {
mResolverReplaced = true;
+
+ // The instance created in PackageManagerService is special cased to be non-user
+ // specific, so initialize all the needed fields here.
+ ApplicationInfo appInfo = pkg.toAppInfoWithoutState();
+ appInfo.flags = PackageInfoUtils.appInfoFlags(pkg, pkgSetting);
+ appInfo.privateFlags =
+ PackageInfoUtils.appInfoPrivateFlags(pkg, pkgSetting);
+ appInfo.initForUser(UserHandle.USER_SYSTEM);
+
// Set up information for custom user intent resolution activity.
- mResolveActivity.applicationInfo = pkg.toAppInfoWithoutState();
+ mResolveActivity.applicationInfo = appInfo;
mResolveActivity.name = mCustomResolverComponentName.getClassName();
mResolveActivity.packageName = pkg.getPackageName();
mResolveActivity.processName = pkg.getProcessName();
@@ -16343,7 +16368,27 @@
REASON_INSTALL,
DexoptOptions.DEXOPT_BOOT_COMPLETE
| DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
- mPackageDexOptimizer.performDexOpt(pkg, reconciledPkg.pkgSetting,
+ ScanResult result = reconciledPkg.scanResult;
+
+ // This mirrors logic from commitReconciledScanResultLocked, where the library files
+ // needed for dexopt are assigned.
+ // TODO: Fix this to have 1 mutable PackageSetting for scan/install. If the previous
+ // setting needs to be passed to have a comparison, hide it behind an immutable
+ // interface. There's no good reason to have 3 different ways to access the real
+ // PackageSetting object, only one of which is actually correct.
+ PackageSetting realPkgSetting = result.existingSettingCopied
+ ? result.request.pkgSetting : result.pkgSetting;
+ if (realPkgSetting == null) {
+ realPkgSetting = reconciledPkg.pkgSetting;
+ }
+
+ // Unfortunately, the updated system app flag is only tracked on this PackageSetting
+ boolean isUpdatedSystemApp = reconciledPkg.pkgSetting.getPkgState()
+ .isUpdatedSystemApp();
+
+ realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
+
+ mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
mDexManager.getPackageUseInfoOrDefault(packageName),
@@ -16954,6 +16999,7 @@
final boolean vendor = oldPackage.isVendor();
final boolean product = oldPackage.isProduct();
final boolean odm = oldPackage.isOdm();
+ final boolean systemExt = oldPackage.isSystemExt();
final @ParseFlags int systemParseFlags = parseFlags;
final @ScanFlags int systemScanFlags = scanFlags
| SCAN_AS_SYSTEM
@@ -16961,14 +17007,14 @@
| (oem ? SCAN_AS_OEM : 0)
| (vendor ? SCAN_AS_VENDOR : 0)
| (product ? SCAN_AS_PRODUCT : 0)
- | (odm ? SCAN_AS_ODM : 0);
+ | (odm ? SCAN_AS_ODM : 0)
+ | (systemExt ? SCAN_AS_SYSTEM_EXT : 0);
if (DEBUG_INSTALL) {
Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage
+ ", old=" + oldPackage);
}
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- ps.getPkgState().setUpdatedSystemApp(true);
targetParseFlags = systemParseFlags;
targetScanFlags = systemScanFlags;
} else { // non system replace
@@ -23566,22 +23612,27 @@
@Override
public void grantImplicitAccess(int userId, Intent intent,
- int callingUid, int targetAppId) {
+ int recipientAppId, int visibleUid, boolean direct) {
synchronized (mLock) {
- final AndroidPackage callingPackage = getPackage(callingUid);
- final int targetUid = UserHandle.getUid(userId, targetAppId);
- final AndroidPackage targetPackage = getPackage(targetUid);
- if (callingPackage == null || targetPackage == null) {
+ final AndroidPackage visiblePackage = getPackage(visibleUid);
+ final int recipientUid = UserHandle.getUid(userId, recipientAppId);
+ if (visiblePackage == null || getPackage(recipientUid) == null) {
return;
}
- final boolean instantApp = isInstantAppInternal(callingPackage.getPackageName(),
- userId, callingUid);
+ final boolean instantApp =
+ isInstantAppInternal(visiblePackage.getPackageName(), userId, visibleUid);
if (instantApp) {
+ if (!direct) {
+ // if the interaction that lead to this granting access to an instant app
+ // was indirect (i.e.: URI permission grant), do not actually execute the
+ // grant.
+ return;
+ }
mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
- UserHandle.getAppId(callingUid), targetAppId);
+ recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/);
} else {
- mAppsFilter.grantImplicitAccess(callingUid, targetUid);
+ mAppsFilter.grantImplicitAccess(recipientUid, visibleUid);
}
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index af5c536..fa1da27 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -33,6 +33,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.compat.ChangeIdStateCache;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
@@ -441,6 +442,7 @@
private static void invalidatePackageCache() {
PackageManager.invalidatePackageInfoCache();
+ ChangeIdStateCache.invalidate();
}
PackageSetting getPackageLPr(String pkgName) {
@@ -521,6 +523,9 @@
p.secondaryCpuAbiString, p.cpuAbiOverrideString,
p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups);
+ if (ret != null) {
+ ret.getPkgState().setUpdatedSystemApp(false);
+ }
mDisabledSysPackages.remove(name);
return ret;
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index fe99229..7dd2e55 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -1246,7 +1246,7 @@
try {
IStorageManager storageManager = PackageHelper.getStorageManager();
if (storageManager.supportsCheckpoint()) {
- storageManager.startCheckpoint(1);
+ storageManager.startCheckpoint(2);
}
} catch (Exception e) {
// Failed to get hold of StorageManager
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 23bdf5f..4ab1f396 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -48,7 +48,7 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Pair;
+import android.util.Slog;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.PackageSetting;
@@ -61,6 +61,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
@@ -72,6 +73,7 @@
* @hide
**/
public class PackageInfoUtils {
+ private static final String TAG = PackageParser2.TAG;
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
@@ -315,26 +317,19 @@
*/
@Nullable
public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId,
- @Nullable PackageSetting pkgSetting) {
- return generateProviderInfo(pkg, p, flags, state, null, userId, pkgSetting);
- }
-
- /**
- * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
- */
- @Nullable
- private static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
@PackageManager.ComponentInfoFlags int flags, PackageUserState state,
- @Nullable ApplicationInfo applicationInfo, int userId,
+ @NonNull ApplicationInfo applicationInfo, int userId,
@Nullable PackageSetting pkgSetting) {
if (p == null) return null;
+ if (applicationInfo == null || !pkg.getPackageName().equals(applicationInfo.packageName)) {
+ Slog.wtf(TAG, "AppInfo's package name is different. Expected=" + pkg.getPackageName()
+ + " actual=" + (applicationInfo == null ? "(null AppInfo)"
+ : applicationInfo.packageName));
+ applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
+ }
if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
return null;
}
- if (applicationInfo == null) {
- applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
- }
ProviderInfo info = PackageInfoWithoutStateUtils.generateProviderInfo(pkg, p, flags, state,
applicationInfo, userId);
if (info == null) {
@@ -480,4 +475,29 @@
| flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY);
// @formatter:on
}
+
+ /**
+ * Wraps {@link PackageInfoUtils#generateApplicationInfo} with a cache.
+ */
+ public static class CachedApplicationInfoGenerator {
+ // Map from a package name to the corresponding app info.
+ private ArrayMap<String, ApplicationInfo> mCache = new ArrayMap<>();
+
+ /**
+ * {@link PackageInfoUtils#generateApplicationInfo} with a cache.
+ */
+ @Nullable
+ public ApplicationInfo generate(AndroidPackage pkg,
+ @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId,
+ @Nullable PackageSetting pkgSetting) {
+ ApplicationInfo appInfo = mCache.get(pkg.getPackageName());
+ if (appInfo != null) {
+ return appInfo;
+ }
+ appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, flags, state, userId, pkgSetting);
+ mCache.put(pkg.getPackageName(), appInfo);
+ return appInfo;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index f99791a..d561b9c 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -42,7 +42,7 @@
*/
public class PackageParser2 {
- private static final String TAG = "PackageParser2";
+ static final String TAG = "PackageParser2";
private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 48dd9e6..2feddb6 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -733,7 +733,7 @@
if (!TextUtils.isEmpty(contentCapturePackageName)) {
grantPermissionsToSystemPackage(contentCapturePackageName, userId,
PHONE_PERMISSIONS, SMS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
- CONTACTS_PERMISSIONS);
+ CONTACTS_PERMISSIONS, STORAGE_PERMISSIONS);
}
// Atthention Service
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 840c865d..4a85027 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2794,7 +2794,7 @@
}
if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() &&
- !ps.isSystem() || !ps.getPkgState().isUpdatedSystemApp()) {
+ !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
// This is the first that we have heard about this package, so the
// permissions we have now selected are fixed until explicitly
// changed.
@@ -4471,6 +4471,9 @@
@Override
public void setCheckPermissionDelegate(CheckPermissionDelegate delegate) {
synchronized (mLock) {
+ if (delegate != null || mCheckPermissionDelegate != null) {
+ PackageManager.invalidatePackageInfoCache();
+ }
mCheckPermissionDelegate = delegate;
}
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
index c008d93..e27bf48 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -35,6 +35,9 @@
*
* It is assumed that anything inside the package was not cached or written to disk, so none of
* these fields are either. They must be set on every boot from other state on the device.
+ *
+ * These fields are also not copied into any cloned PackageSetting, to preserve the old behavior
+ * where they would be lost implicitly by re-generating the package object.
*/
@DataClass(genSetters = true, genConstructor = false, genBuilder = false)
public class PackageStateUnserialized {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 19e6062..1b5cc6a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -186,6 +186,7 @@
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.RoSystemProperties;
@@ -482,6 +483,7 @@
MetricsLogger mLogger;
boolean mWakeOnDpadKeyPress;
boolean mWakeOnAssistKeyPress;
+ boolean mWakeOnBackKeyPress;
private boolean mHandleVolumeKeysInWM;
@@ -1105,7 +1107,8 @@
LocalServices.getService(InputMethodManagerInternal.class);
}
if (mInputMethodManagerInternal != null) {
- mInputMethodManagerInternal.hideCurrentInputMethod();
+ mInputMethodManagerInternal.hideCurrentInputMethod(
+ SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME);
}
} else {
shortPressPowerGoHome();
@@ -1736,6 +1739,8 @@
res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);
mWakeOnAssistKeyPress =
res.getBoolean(com.android.internal.R.bool.config_wakeOnAssistKeyPress);
+ mWakeOnBackKeyPress =
+ res.getBoolean(com.android.internal.R.bool.config_wakeOnBackKeyPress);
// Init display burn-in protection
boolean burnInProtectionEnabled = context.getResources().getBoolean(
@@ -3085,7 +3090,7 @@
event.getAction(), fallbackAction.keyCode,
event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
- flags, event.getSource(), event.getDisplayId(), null /* hmac */, null);
+ flags, event.getSource(), event.getDisplayId(), null);
if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
@@ -4107,6 +4112,9 @@
case KeyEvent.KEYCODE_ASSIST:
return mWakeOnAssistKeyPress;
+
+ case KeyEvent.KEYCODE_BACK:
+ return mWakeOnBackKeyPress;
}
return true;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8483c77..e6c23b6 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -4213,15 +4213,15 @@
.SCREEN_BRIGHTNESS_SETTING_LIMITS);
proto.write(
PowerServiceSettingsAndConfigurationDumpProto.ScreenBrightnessSettingLimitsProto
- .SETTING_MINIMUM,
+ .SETTING_MINIMUM_FLOAT,
mScreenBrightnessSettingMinimum);
proto.write(
PowerServiceSettingsAndConfigurationDumpProto.ScreenBrightnessSettingLimitsProto
- .SETTING_MAXIMUM,
+ .SETTING_MAXIMUM_FLOAT,
mScreenBrightnessSettingMaximum);
proto.write(
PowerServiceSettingsAndConfigurationDumpProto.ScreenBrightnessSettingLimitsProto
- .SETTING_DEFAULT,
+ .SETTING_DEFAULT_FLOAT,
mScreenBrightnessSettingDefault);
proto.end(screenBrightnessSettingLimitsToken);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 233417d..059861b 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -16,8 +16,13 @@
package com.android.server.power.batterysaver;
import android.annotation.IntDef;
+import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatterySaverPolicyConfig;
@@ -183,18 +188,15 @@
private String mEventLogKeys;
/**
- * Whether vibration should *really* be disabled -- i.e. {@link Policy#disableVibration}
- * is true *and* {@link #mAccessibilityEnabled} is false.
- */
- @GuardedBy("mLock")
- private boolean mDisableVibrationEffective;
-
- /**
* Whether accessibility is currently enabled or not.
*/
@GuardedBy("mLock")
private boolean mAccessibilityEnabled;
+ /** Whether the phone is projecting in car mode or not. */
+ @GuardedBy("mLock")
+ private boolean mCarModeEnabled;
+
/** The current default adaptive policy. */
@GuardedBy("mLock")
private Policy mDefaultAdaptivePolicy = DEFAULT_ADAPTIVE_POLICY;
@@ -207,6 +209,13 @@
@GuardedBy("mLock")
private Policy mFullPolicy = DEFAULT_FULL_POLICY;
+ /**
+ * The current effective policy. This is based on the current policy level's policy, with any
+ * required adjustments.
+ */
+ @GuardedBy("mLock")
+ private Policy mEffectivePolicy = OFF_POLICY;
+
@IntDef(prefix = {"POLICY_LEVEL_"}, value = {
POLICY_LEVEL_OFF,
POLICY_LEVEL_ADAPTIVE,
@@ -230,6 +239,20 @@
private final ContentResolver mContentResolver;
private final BatterySavingStats mBatterySavingStats;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED:
+ setCarModeEnabled(true);
+ break;
+ case UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED:
+ setCarModeEnabled(false);
+ break;
+ }
+ }
+ };
+
@GuardedBy("mLock")
private final List<BatterySaverPolicyListener> mListeners = new ArrayList<>();
@@ -263,16 +286,25 @@
final AccessibilityManager acm = mContext.getSystemService(AccessibilityManager.class);
- acm.addAccessibilityStateChangeListener((enabled) -> {
- synchronized (mLock) {
- mAccessibilityEnabled = enabled;
- }
- refreshSettings();
- });
- final boolean enabled = acm.isEnabled();
+ acm.addAccessibilityStateChangeListener((enabled) -> setAccessibilityEnabled(enabled));
+ final boolean accessibilityEnabled = acm.isEnabled();
synchronized (mLock) {
- mAccessibilityEnabled = enabled;
+ mAccessibilityEnabled = accessibilityEnabled;
}
+
+ final IntentFilter filter = new IntentFilter(
+ UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
+ filter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED);
+ // The ENTER/EXIT_CAR_MODE_PRIORITIZED intents are sent to UserHandle.ALL, so no need to
+ // register as all users here.
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ final boolean carModeEnabled =
+ mContext.getSystemService(UiModeManager.class).getCurrentModeType()
+ == Configuration.UI_MODE_TYPE_CAR;
+ synchronized (mLock) {
+ mCarModeEnabled = carModeEnabled;
+ }
+
onChange(true, null);
}
@@ -299,13 +331,34 @@
PowerManager.invalidatePowerSaveModeCaches();
}
+ /**
+ * Notifies listeners of a policy change on the handler thread only if the current policy level
+ * is not {@link POLICY_LEVEL_OFF}.
+ */
+ private void maybeNotifyListenersOfPolicyChange() {
+ final BatterySaverPolicyListener[] listeners;
+ synchronized (mLock) {
+ if (getPolicyLevelLocked() == POLICY_LEVEL_OFF) {
+ // Current policy is OFF, so there's no change to notify listeners of.
+ return;
+ }
+ // Don't call out to listeners with the lock held.
+ listeners = mListeners.toArray(new BatterySaverPolicyListener[mListeners.size()]);
+ }
+
+ mHandler.post(() -> {
+ for (BatterySaverPolicyListener listener : listeners) {
+ listener.onBatterySaverPolicyChanged(this);
+ }
+ });
+ }
+
@Override
public void onChange(boolean selfChange, Uri uri) {
refreshSettings();
}
private void refreshSettings() {
- final BatterySaverPolicyListener[] listeners;
synchronized (mLock) {
// Load the non-device-specific setting.
final String setting = getGlobalSetting(Settings.Global.BATTERY_SAVER_CONSTANTS);
@@ -334,16 +387,9 @@
// Nothing of note changed.
return;
}
-
- listeners = mListeners.toArray(new BatterySaverPolicyListener[0]);
}
- // Notify the listeners.
- mHandler.post(() -> {
- for (BatterySaverPolicyListener listener : listeners) {
- listener.onBatterySaverPolicyChanged(this);
- }
- });
+ maybeNotifyListenersOfPolicyChange();
}
@GuardedBy("mLock")
@@ -404,31 +450,63 @@
@GuardedBy("mLock")
private void updatePolicyDependenciesLocked() {
- final Policy currPolicy = getCurrentPolicyLocked();
- // Update the effective vibration policy.
- mDisableVibrationEffective = currPolicy.disableVibration
- && !mAccessibilityEnabled; // Don't disable vibration when accessibility is on.
+ final Policy rawPolicy = getCurrentRawPolicyLocked();
+
+ final int locationMode;
+ if (mCarModeEnabled
+ && rawPolicy.locationMode != PowerManager.LOCATION_MODE_NO_CHANGE
+ && rawPolicy.locationMode != PowerManager.LOCATION_MODE_FOREGROUND_ONLY) {
+ // If car projection is enabled, ensure that navigation works.
+ locationMode = PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
+ } else {
+ locationMode = rawPolicy.locationMode;
+ }
+ mEffectivePolicy = new Policy(
+ rawPolicy.adjustBrightnessFactor,
+ rawPolicy.advertiseIsEnabled,
+ rawPolicy.deferFullBackup,
+ rawPolicy.deferKeyValueBackup,
+ rawPolicy.disableAnimation,
+ rawPolicy.disableAod,
+ rawPolicy.disableLaunchBoost,
+ rawPolicy.disableOptionalSensors,
+ rawPolicy.disableSoundTrigger,
+ // Don't disable vibration when accessibility is on.
+ rawPolicy.disableVibration && !mAccessibilityEnabled,
+ rawPolicy.enableAdjustBrightness,
+ rawPolicy.enableDataSaver,
+ rawPolicy.enableFirewall,
+ // Don't force night mode when car projection is enabled.
+ rawPolicy.enableNightMode && !mCarModeEnabled,
+ rawPolicy.enableQuickDoze,
+ rawPolicy.filesForInteractive,
+ rawPolicy.filesForNoninteractive,
+ rawPolicy.forceAllAppsStandby,
+ rawPolicy.forceBackgroundCheck,
+ locationMode
+ );
+
final StringBuilder sb = new StringBuilder();
- if (currPolicy.forceAllAppsStandby) sb.append("A");
- if (currPolicy.forceBackgroundCheck) sb.append("B");
+ if (mEffectivePolicy.forceAllAppsStandby) sb.append("A");
+ if (mEffectivePolicy.forceBackgroundCheck) sb.append("B");
- if (mDisableVibrationEffective) sb.append("v");
- if (currPolicy.disableAnimation) sb.append("a");
- if (currPolicy.disableSoundTrigger) sb.append("s");
- if (currPolicy.deferFullBackup) sb.append("F");
- if (currPolicy.deferKeyValueBackup) sb.append("K");
- if (currPolicy.enableFirewall) sb.append("f");
- if (currPolicy.enableDataSaver) sb.append("d");
- if (currPolicy.enableAdjustBrightness) sb.append("b");
+ if (mEffectivePolicy.disableVibration) sb.append("v");
+ if (mEffectivePolicy.disableAnimation) sb.append("a");
+ if (mEffectivePolicy.disableSoundTrigger) sb.append("s");
+ if (mEffectivePolicy.deferFullBackup) sb.append("F");
+ if (mEffectivePolicy.deferKeyValueBackup) sb.append("K");
+ if (mEffectivePolicy.enableFirewall) sb.append("f");
+ if (mEffectivePolicy.enableDataSaver) sb.append("d");
+ if (mEffectivePolicy.enableAdjustBrightness) sb.append("b");
- if (currPolicy.disableLaunchBoost) sb.append("l");
- if (currPolicy.disableOptionalSensors) sb.append("S");
- if (currPolicy.disableAod) sb.append("o");
- if (currPolicy.enableQuickDoze) sb.append("q");
+ if (mEffectivePolicy.disableLaunchBoost) sb.append("l");
+ if (mEffectivePolicy.disableOptionalSensors) sb.append("S");
+ if (mEffectivePolicy.disableAod) sb.append("o");
+ if (mEffectivePolicy.enableQuickDoze) sb.append("q");
- sb.append(currPolicy.locationMode);
+ sb.append(mEffectivePolicy.locationMode);
mEventLogKeys = sb.toString();
}
@@ -857,7 +935,7 @@
return builder.setBatterySaverEnabled(currPolicy.disableSoundTrigger)
.build();
case ServiceType.VIBRATION:
- return builder.setBatterySaverEnabled(mDisableVibrationEffective)
+ return builder.setBatterySaverEnabled(currPolicy.disableVibration)
.build();
case ServiceType.FORCE_ALL_APPS_STANDBY:
return builder.setBatterySaverEnabled(currPolicy.forceAllAppsStandby)
@@ -933,6 +1011,10 @@
}
private Policy getCurrentPolicyLocked() {
+ return mEffectivePolicy;
+ }
+
+ private Policy getCurrentRawPolicyLocked() {
switch (getPolicyLevelLocked()) {
case POLICY_LEVEL_FULL:
return mFullPolicy;
@@ -994,11 +1076,13 @@
pw.println(" value: " + mAdaptiveDeviceSpecificSettings);
pw.println(" mAccessibilityEnabled=" + mAccessibilityEnabled);
+ pw.println(" mCarModeEnabled=" + mCarModeEnabled);
pw.println(" mPolicyLevel=" + getPolicyLevelLocked());
dumpPolicyLocked(pw, " ", "full", mFullPolicy);
dumpPolicyLocked(pw, " ", "default adaptive", mDefaultAdaptivePolicy);
dumpPolicyLocked(pw, " ", "current adaptive", mAdaptivePolicy);
+ dumpPolicyLocked(pw, " ", "effective", mEffectivePolicy);
}
}
@@ -1009,11 +1093,7 @@
pw.print(indent);
pw.println(" " + KEY_ADVERTISE_IS_ENABLED + "=" + p.advertiseIsEnabled);
pw.print(indent);
- pw.println(" " + KEY_VIBRATION_DISABLED + ":config=" + p.disableVibration);
- // mDisableVibrationEffective is based on the currently selected policy
- pw.print(indent);
- pw.println(" " + KEY_VIBRATION_DISABLED + ":effective=" + (p.disableVibration
- && !mAccessibilityEnabled));
+ pw.println(" " + KEY_VIBRATION_DISABLED + "=" + p.disableVibration);
pw.print(indent);
pw.println(" " + KEY_ANIMATION_DISABLED + "=" + p.disableAnimation);
pw.print(indent);
@@ -1070,10 +1150,24 @@
}
@VisibleForTesting
- public void setAccessibilityEnabledForTest(boolean enabled) {
+ void setAccessibilityEnabled(boolean enabled) {
synchronized (mLock) {
- mAccessibilityEnabled = enabled;
- updatePolicyDependenciesLocked();
+ if (mAccessibilityEnabled != enabled) {
+ mAccessibilityEnabled = enabled;
+ updatePolicyDependenciesLocked();
+ maybeNotifyListenersOfPolicyChange();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void setCarModeEnabled(boolean enabled) {
+ synchronized (mLock) {
+ if (mCarModeEnabled != enabled) {
+ mCarModeEnabled = enabled;
+ updatePolicyDependenciesLocked();
+ maybeNotifyListenersOfPolicyChange();
+ }
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/Dumpable.java b/services/core/java/com/android/server/soundtrigger_middleware/Dumpable.java
new file mode 100644
index 0000000..f9aa009
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/Dumpable.java
@@ -0,0 +1,32 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+
+import java.io.PrintWriter;
+
+/**
+ * An interface of an object that can generate a dump.
+ */
+interface Dumpable {
+ /**
+ * Generate a human-readable dump into the given writer.
+ * @param pw The writer.
+ */
+ void dump(@NonNull PrintWriter pw);
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java b/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
new file mode 100644
index 0000000..7f047f8
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
@@ -0,0 +1,249 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * A collection of pretty-print utilities for data objects.
+ */
+class ObjectPrinter {
+ /** Default maximum elements to print in a collection. */
+ static public final int kDefaultMaxCollectionLength = 16;
+
+ /**
+ * Simple version of {@link #print(Object, boolean, int)} that prints an object, without
+ * recursing into sub-objects.
+ *
+ * @param obj The object to print.
+ * @return A string representing the object.
+ */
+ static String print(@Nullable Object obj) {
+ return print(obj, false, kDefaultMaxCollectionLength);
+ }
+
+ /**
+ * Pretty-prints an object.
+ *
+ * @param obj The object to print.
+ * @param deep Whether to pretty-print sub-objects (if false, just prints them
+ * with {@link Object#toString()}).
+ * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
+ * print.
+ * @return A string representing the object.
+ */
+ static String print(@Nullable Object obj, boolean deep, int maxCollectionLength) {
+ StringBuilder builder = new StringBuilder();
+ print(builder, obj, deep, maxCollectionLength);
+ return builder.toString();
+ }
+
+ /**
+ * This version is suitable for use inside a toString() override of an object, e.g.:
+ * <pre><code>
+ * class MyObject {
+ * ...
+ * @Override
+ * String toString() {
+ * return ObjectPrinter.printPublicFields(this, ...);
+ * }
+ * }
+ * </code></pre>
+ *
+ * @param obj The object to print.
+ * @param deep Whether to pretty-print sub-objects (if false, just prints them
+ * with {@link Object#toString()}).
+ * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
+ * print.
+ */
+ static String printPublicFields(@Nullable Object obj, boolean deep, int maxCollectionLength) {
+ StringBuilder builder = new StringBuilder();
+ printPublicFields(builder, obj, deep, maxCollectionLength);
+ return builder.toString();
+ }
+
+ /**
+ * A version of {@link #print(Object, boolean, int)} that uses a {@link StringBuilder}.
+ *
+ * @param builder StringBuilder to print into.
+ * @param obj The object to print.
+ * @param deep Whether to pretty-print sub-objects (if false, just prints them
+ * with {@link Object#toString()}).
+ * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
+ * print.
+ */
+ static void print(@NonNull StringBuilder builder, @Nullable Object obj, boolean deep,
+ int maxCollectionLength) {
+ try {
+ if (obj == null) {
+ builder.append("null");
+ return;
+ }
+ if (obj instanceof Boolean) {
+ builder.append(obj.toString());
+ return;
+ }
+ if (obj instanceof Number) {
+ builder.append(obj.toString());
+ return;
+ }
+ if (obj instanceof Character) {
+ builder.append('\'');
+ builder.append(obj.toString());
+ builder.append('\'');
+ return;
+ }
+ if (obj instanceof String) {
+ builder.append('"');
+ builder.append(obj.toString());
+ builder.append('"');
+ return;
+ }
+
+ Class cls = obj.getClass();
+
+ if (Collection.class.isAssignableFrom(cls)) {
+ Collection collection = (Collection) obj;
+ builder.append("[ ");
+ int length = collection.size();
+ boolean isLong = false;
+ int i = 0;
+ for (Object child : collection) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ if (i >= maxCollectionLength) {
+ isLong = true;
+ break;
+ }
+ print(builder, child, deep, maxCollectionLength);
+ ++i;
+ }
+ if (isLong) {
+ builder.append("... (+");
+ builder.append(length - maxCollectionLength);
+ builder.append(" entries)");
+ }
+ builder.append(" ]");
+ return;
+ }
+
+ if (Map.class.isAssignableFrom(cls)) {
+ Map<?, ?> map = (Map<?, ?>) obj;
+ builder.append("< ");
+ int length = map.size();
+ boolean isLong = false;
+ int i = 0;
+ for (Map.Entry<?, ?> child : map.entrySet()) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ if (i >= maxCollectionLength) {
+ isLong = true;
+ break;
+ }
+ print(builder, child.getKey(), deep, maxCollectionLength);
+ builder.append(": ");
+ print(builder, child.getValue(), deep, maxCollectionLength);
+ ++i;
+ }
+ if (isLong) {
+ builder.append("... (+");
+ builder.append(length - maxCollectionLength);
+ builder.append(" entries)");
+ }
+ builder.append(" >");
+ return;
+ }
+
+ if (cls.isArray()) {
+ builder.append("[ ");
+ int length = Array.getLength(obj);
+ boolean isLong = false;
+ for (int i = 0; i < length; ++i) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ if (i >= maxCollectionLength) {
+ isLong = true;
+ break;
+ }
+ print(builder, Array.get(obj, i), deep, maxCollectionLength);
+ }
+ if (isLong) {
+ builder.append("... (+");
+ builder.append(length - maxCollectionLength);
+ builder.append(" entries)");
+ }
+ builder.append(" ]");
+ return;
+ }
+
+ if (!deep) {
+ builder.append(obj.toString());
+ return;
+ }
+ printPublicFields(builder, obj, deep, maxCollectionLength);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * A version of {@link #printPublicFields(Object, boolean, int)} that uses a {@link
+ * StringBuilder}.
+ *
+ * @param obj The object to print.
+ * @param deep Whether to pretty-print sub-objects (if false, just prints them
+ * with {@link Object#toString()}).
+ * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
+ * print.
+ */
+ static void printPublicFields(@NonNull StringBuilder builder, @Nullable Object obj,
+ boolean deep,
+ int maxCollectionLength) {
+ try {
+ Class cls = obj.getClass();
+ builder.append("{ ");
+
+ boolean first = true;
+ for (Field fld : cls.getDeclaredFields()) {
+ int mod = fld.getModifiers();
+ if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.STATIC) == 0) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(", ");
+ }
+ builder.append(fld.getName());
+ builder.append(": ");
+ print(builder, fld.get(obj), deep, maxCollectionLength);
+ }
+ }
+ builder.append(" }");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
new file mode 100644
index 0000000..fa78cb0
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -0,0 +1,454 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.LinkedList;
+
+/**
+ * An ISoundTriggerMiddlewareService decorator, which adds logging of all API calls (and
+ * callbacks).
+ *
+ * All API methods should follow this structure:
+ * <pre><code>
+ * @Override
+ * public @NonNull ReturnType someMethod(ArgType1 arg1, ArgType2 arg2) throws ExceptionType {
+ * try {
+ * ReturnType result = mDelegate.someMethod(arg1, arg2);
+ * logReturn("someMethod", result, arg1, arg2);
+ * return result;
+ * } catch (Exception e) {
+ * logException("someMethod", e, arg1, arg2);
+ * throw e;
+ * }
+ * }
+ * </code></pre>
+ * The actual handling of these events is then done inside of {@link #logReturnWithObject(Object,
+ * String, Object, Object[])}, {@link #logVoidReturnWithObject(Object, String, Object[])} and {@link
+ * #logExceptionWithObject(Object, String, Exception, Object[])}.
+ */
+public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareService, Dumpable {
+ private static final String TAG = "SoundTriggerMiddlewareLogging";
+ private final @NonNull ISoundTriggerMiddlewareService mDelegate;
+
+ public SoundTriggerMiddlewareLogging(@NonNull ISoundTriggerMiddlewareService delegate) {
+ mDelegate = delegate;
+ }
+
+ @Override
+ public @NonNull SoundTriggerModuleDescriptor[] listModules() throws RemoteException {
+ try {
+ SoundTriggerModuleDescriptor[] result = mDelegate.listModules();
+ logReturn("listModules", result);
+ return result;
+ } catch (Exception e) {
+ logException("listModules", e);
+ throw e;
+ }
+ }
+
+ @Override
+ public @NonNull ISoundTriggerModule attach(int handle, ISoundTriggerCallback callback)
+ throws RemoteException {
+ try {
+ ISoundTriggerModule result = mDelegate.attach(handle, new CallbackLogging(callback));
+ logReturn("attach", result, handle, callback);
+ return new ModuleLogging(result);
+ } catch (Exception e) {
+ logException("attach", e, handle, callback);
+ throw e;
+ }
+ }
+
+ @Override
+ public void setExternalCaptureState(boolean active) throws RemoteException {
+ try {
+ mDelegate.setExternalCaptureState(active);
+ logVoidReturn("setExternalCaptureState", active);
+ } catch (Exception e) {
+ logException("setExternalCaptureState", e, active);
+ throw e;
+ }
+ }
+
+ @Override public IBinder asBinder() {
+ throw new UnsupportedOperationException(
+ "This implementation is not inteded to be used directly with Binder.");
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+
+ private void logException(String methodName, Exception ex, Object... args) {
+ logExceptionWithObject(this, methodName, ex, args);
+ }
+
+ private void logReturn(String methodName, Object retVal, Object... args) {
+ logReturnWithObject(this, methodName, retVal, args);
+ }
+
+ private void logVoidReturn(String methodName, Object... args) {
+ logVoidReturnWithObject(this, methodName, args);
+ }
+
+ private class CallbackLogging implements ISoundTriggerCallback {
+ private final ISoundTriggerCallback mDelegate;
+
+ private CallbackLogging(ISoundTriggerCallback delegate) {
+ mDelegate = delegate;
+ }
+
+ @Override
+ public void onRecognition(int modelHandle, RecognitionEvent event) throws RemoteException {
+ try {
+ mDelegate.onRecognition(modelHandle, event);
+ logVoidReturn("onRecognition", modelHandle, event);
+ } catch (Exception e) {
+ logException("onRecognition", e, modelHandle, event);
+ throw e;
+ }
+ }
+
+ @Override
+ public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
+ throws RemoteException {
+ try {
+ mDelegate.onPhraseRecognition(modelHandle, event);
+ logVoidReturn("onPhraseRecognition", modelHandle, event);
+ } catch (Exception e) {
+ logException("onPhraseRecognition", e, modelHandle, event);
+ throw e;
+ }
+ }
+
+ @Override
+ public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
+ try {
+ mDelegate.onRecognitionAvailabilityChange(available);
+ logVoidReturn("onRecognitionAvailabilityChange", available);
+ } catch (Exception e) {
+ logException("onRecognitionAvailabilityChange", e, available);
+ throw e;
+ }
+ }
+
+ @Override
+ public void onModuleDied() throws RemoteException {
+ try {
+ mDelegate.onModuleDied();
+ logVoidReturn("onModuleDied");
+ } catch (Exception e) {
+ logException("onModuleDied", e);
+ throw e;
+ }
+ }
+
+ private void logException(String methodName, Exception ex, Object... args) {
+ logExceptionWithObject(this, methodName, ex, args);
+ }
+
+ private void logVoidReturn(String methodName, Object... args) {
+ logVoidReturnWithObject(this, methodName, args);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mDelegate.asBinder();
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+ }
+
+ private class ModuleLogging implements ISoundTriggerModule {
+ private final ISoundTriggerModule mDelegate;
+
+ private ModuleLogging(ISoundTriggerModule delegate) {
+ mDelegate = delegate;
+ }
+
+ @Override
+ public int loadModel(SoundModel model) throws RemoteException {
+ try {
+ int result = mDelegate.loadModel(model);
+ logReturn("loadModel", result, model);
+ return result;
+ } catch (Exception e) {
+ logException("loadModel", e, model);
+ throw e;
+ }
+ }
+
+ @Override
+ public int loadPhraseModel(PhraseSoundModel model) throws RemoteException {
+ try {
+ int result = mDelegate.loadPhraseModel(model);
+ logReturn("loadPhraseModel", result, model);
+ return result;
+ } catch (Exception e) {
+ logException("loadPhraseModel", e, model);
+ throw e;
+ }
+ }
+
+ @Override
+ public void unloadModel(int modelHandle) throws RemoteException {
+ try {
+ mDelegate.unloadModel(modelHandle);
+ logVoidReturn("unloadModel", modelHandle);
+ } catch (Exception e) {
+ logException("unloadModel", e, modelHandle);
+ throw e;
+ }
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, RecognitionConfig config)
+ throws RemoteException {
+ try {
+ mDelegate.startRecognition(modelHandle, config);
+ logVoidReturn("startRecognition", modelHandle, config);
+ } catch (Exception e) {
+ logException("startRecognition", e, modelHandle, config);
+ throw e;
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) throws RemoteException {
+ try {
+ mDelegate.stopRecognition(modelHandle);
+ logVoidReturn("stopRecognition", modelHandle);
+ } catch (Exception e) {
+ logException("stopRecognition", e, modelHandle);
+ throw e;
+ }
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) throws RemoteException {
+ try {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ logVoidReturn("forceRecognitionEvent", modelHandle);
+ } catch (Exception e) {
+ logException("forceRecognitionEvent", e, modelHandle);
+ throw e;
+ }
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int modelParam, int value)
+ throws RemoteException {
+ try {
+ mDelegate.setModelParameter(modelHandle, modelParam, value);
+ logVoidReturn("setModelParameter", modelHandle, modelParam, value);
+ } catch (Exception e) {
+ logException("setModelParameter", e, modelHandle, modelParam, value);
+ throw e;
+ }
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int modelParam) throws RemoteException {
+ try {
+ int result = mDelegate.getModelParameter(modelHandle, modelParam);
+ logReturn("getModelParameter", result, modelHandle, modelParam);
+ return result;
+ } catch (Exception e) {
+ logException("getModelParameter", e, modelHandle, modelParam);
+ throw e;
+ }
+ }
+
+ @Override
+ public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam)
+ throws RemoteException {
+ try {
+ ModelParameterRange result = mDelegate.queryModelParameterSupport(modelHandle,
+ modelParam);
+ logReturn("queryModelParameterSupport", result, modelHandle, modelParam);
+ return result;
+ } catch (Exception e) {
+ logException("queryModelParameterSupport", e, modelHandle, modelParam);
+ throw e;
+ }
+ }
+
+ @Override
+ public void detach() throws RemoteException {
+ try {
+ mDelegate.detach();
+ logVoidReturn("detach");
+ } catch (Exception e) {
+ logException("detach", e);
+ throw e;
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mDelegate.asBinder();
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+
+ private void logException(String methodName, Exception ex, Object... args) {
+ logExceptionWithObject(this, methodName, ex, args);
+ }
+
+ private void logReturn(String methodName, Object retVal, Object... args) {
+ logReturnWithObject(this, methodName, retVal, args);
+ }
+
+ private void logVoidReturn(String methodName, Object... args) {
+ logVoidReturnWithObject(this, methodName, args);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // Actual logging logic below.
+ private static final int NUM_EVENTS_TO_DUMP = 64;
+ private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
+ private final @NonNull LinkedList<Event> mLastEvents = new LinkedList<>();
+
+ static private class Event {
+ public final long timestamp = System.currentTimeMillis();
+ public final String message;
+
+ private Event(String message) {
+ this.message = message;
+ }
+ }
+
+ private static String printArgs(@NonNull Object[] args) {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < args.length; ++i) {
+ if (i > 0) {
+ result.append(", ");
+ }
+ printObject(result, args[i]);
+ }
+ return result.toString();
+ }
+
+ private static void printObject(@NonNull StringBuilder builder, @Nullable Object obj) {
+ if (obj instanceof Parcelable) {
+ ObjectPrinter.print(builder, obj, true, 16);
+ } else {
+ builder.append(obj.toString());
+ }
+ }
+
+ private static String printObject(@Nullable Object obj) {
+ StringBuilder builder = new StringBuilder();
+ printObject(builder, obj);
+ return builder.toString();
+ }
+
+ private void logReturnWithObject(@NonNull Object object, String methodName,
+ @Nullable Object retVal,
+ @NonNull Object[] args) {
+ final String message = String.format("%s[this=%s, caller=%d/%d](%s) -> %s", methodName,
+ object,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ printArgs(args),
+ printObject(retVal));
+ Log.i(TAG, message);
+ appendMessage(message);
+ }
+
+ private void logVoidReturnWithObject(@NonNull Object object, @NonNull String methodName,
+ @NonNull Object[] args) {
+ final String message = String.format("%s[this=%s, caller=%d/%d](%s)", methodName,
+ object,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ printArgs(args));
+ Log.i(TAG, message);
+ appendMessage(message);
+ }
+
+ private void logExceptionWithObject(@NonNull Object object, @NonNull String methodName,
+ @NonNull Exception ex,
+ Object[] args) {
+ final String message = String.format("%s[this=%s, caller=%d/%d](%s) threw", methodName,
+ object,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ printArgs(args));
+ Log.e(TAG, message, ex);
+ appendMessage(message + " " + ex.toString());
+ }
+
+ private void appendMessage(@NonNull String message) {
+ Event event = new Event(message);
+ synchronized (mLastEvents) {
+ if (mLastEvents.size() > NUM_EVENTS_TO_DUMP) {
+ mLastEvents.remove();
+ }
+ mLastEvents.add(event);
+ }
+ }
+
+ @Override public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println("=========================================");
+ pw.println("Last events");
+ pw.println("=========================================");
+ synchronized (mLastEvents) {
+ for (Event event : mLastEvents) {
+ pw.print(DATE_FORMAT.format(new Date(event.timestamp)));
+ pw.print('\t');
+ pw.println(event.message);
+ }
+ }
+ pw.println();
+
+ if (mDelegate instanceof Dumpable) {
+ ((Dumpable) mDelegate).dump(pw);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index 1ed97be..0d8fc76 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -16,91 +16,40 @@
package com.android.server.soundtrigger_middleware;
-import android.Manifest;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
-import android.content.PermissionChecker;
import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
import android.media.soundtrigger_middleware.PhraseSoundModel;
import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionStatus;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.Status;
import android.os.RemoteException;
-import android.os.ServiceSpecificException;
import android.util.Log;
-import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.Objects;
-import java.util.Set;
/**
* This is a wrapper around an {@link ISoundTriggerMiddlewareService} implementation, which exposes
- * it as a Binder service and enforces permissions and correct usage by the client, as well as makes
- * sure that exceptions representing a server malfunction do not get sent to the client.
+ * it as a Binder service.
* <p>
- * This is intended to extract the non-business logic out of the underlying implementation and thus
- * make it easier to maintain each one of those separate aspects. A design trade-off is being made
- * here, in that this class would need to essentially eavesdrop on all the client-server
- * communication and retain all state known to the client, while the client doesn't necessarily care
- * about all of it, and while the server has its own representation of this information. However,
- * in this case, this is a small amount of data, and the benefits in code elegance seem worth it.
- * There is also some additional cost in employing a simplistic locking mechanism here, but
- * following the same line of reasoning, the benefits in code simplicity outweigh it.
+ * This is intended to facilitate a pattern of decorating the core implementation (business logic)
+ * of the interface with every decorator implementing a different aspect of the service, such as
+ * validation and logging. This class acts as the top-level decorator, which also adds the binder-
+ * related functionality (hence, it extends ISoundTriggerMiddlewareService.Stub as rather than
+ * implements ISoundTriggerMiddlewareService), and does the same thing for child interfaces
+ * returned.
* <p>
- * Every public method in this class, overriding an interface method, must follow the following
- * pattern:
- * <code><pre>
- * @Override public T method(S arg) {
- * // Permission check.
- * checkPermissions();
- * // Input validation.
- * ValidationUtil.validateS(arg);
- * synchronized (this) {
- * // State validation.
- * if (...state is not valid for this call...) {
- * throw new IllegalStateException("State is invalid because...");
- * }
- * // From here on, every exception isn't client's fault.
- * try {
- * T result = mDelegate.method(arg);
- * // Update state.;
- * ...
- * return result;
- * } catch (Exception e) {
- * throw handleException(e);
- * }
- * }
- * }
- * </pre></code>
- * Following this patterns ensures a consistent and rigorous handling of all aspects associated
- * with client-server separation.
- * <p>
- * <b>Exception handling approach:</b><br>
- * We make sure all client faults (permissions, argument and state validation) happen first, and
- * would throw {@link SecurityException}, {@link IllegalArgumentException}/
- * {@link NullPointerException} or {@link
- * IllegalStateException}, respectively. All those exceptions are treated specially by Binder and
- * will get sent back to the client.<br>
- * Once this is done, any subsequent fault is considered a server fault. Only {@link
- * RecoverableException}s thrown by the implementation are special-cased: they would get sent back
- * to the caller as a {@link ServiceSpecificException}, which is the behavior of Binder. Any other
- * exception gets wrapped with a {@link InternalServerError}, which is specifically chosen as a type
- * that <b>does NOT</b> get forwarded by binder. Those exceptions would be handled by a high-level
- * exception handler on the server side, typically resulting in rebooting the server.
+ * The inner class {@link Lifecycle} acts as both a factory, composing the various aspect-decorators
+ * to create a full-featured implementation, as well as as an entry-point for presenting this
+ * implementation as a system service.
* <p>
* <b>Exposing this service as a System Service:</b><br>
* Insert this line into {@link com.android.server.SystemServer}:
@@ -113,224 +62,100 @@
public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareService.Stub {
static private final String TAG = "SoundTriggerMiddlewareService";
+ @NonNull
private final ISoundTriggerMiddlewareService mDelegate;
- private final Context mContext;
- private Set<Integer> mModuleHandles;
/**
* Constructor for internal use only. Could be exposed for testing purposes in the future.
* Users should access this class via {@link Lifecycle}.
*/
- private SoundTriggerMiddlewareService(
- @NonNull ISoundTriggerMiddlewareService delegate, @NonNull Context context) {
- mDelegate = delegate;
- mContext = context;
- }
-
- /**
- * Generic exception handling for exceptions thrown by the underlying implementation.
- *
- * Would throw any {@link RecoverableException} as a {@link ServiceSpecificException} (passed
- * by Binder to the caller) and <i>any other</i> exception as {@link InternalServerError}
- * (<b>not</b> passed by Binder to the caller).
- * <p>
- * Typical usage:
- * <code><pre>
- * try {
- * ... Do server operations ...
- * } catch (Exception e) {
- * throw handleException(e);
- * }
- * </pre></code>
- */
- private static @NonNull
- RuntimeException handleException(@NonNull Exception e) {
- if (e instanceof RecoverableException) {
- throw new ServiceSpecificException(((RecoverableException) e).errorCode,
- e.getMessage());
- }
- throw new InternalServerError(e);
+ private SoundTriggerMiddlewareService(@NonNull ISoundTriggerMiddlewareService delegate) {
+ mDelegate = Objects.requireNonNull(delegate);
}
@Override
public @NonNull
- SoundTriggerModuleDescriptor[] listModules() {
- // Permission check.
- checkPermissions();
- // Input validation (always valid).
-
- synchronized (this) {
- // State validation (always valid).
-
- // From here on, every exception isn't client's fault.
- try {
- SoundTriggerModuleDescriptor[] result = mDelegate.listModules();
- mModuleHandles = new HashSet<>(result.length);
- for (SoundTriggerModuleDescriptor desc : result) {
- mModuleHandles.add(desc.handle);
- }
- return result;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
+ SoundTriggerModuleDescriptor[] listModules() throws RemoteException {
+ return mDelegate.listModules();
}
@Override
public @NonNull
- ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback) {
- // Permission check.
- checkPermissions();
- // Input validation.
- Objects.requireNonNull(callback);
- Objects.requireNonNull(callback.asBinder());
-
- synchronized (this) {
- // State validation.
- if (mModuleHandles == null) {
- throw new IllegalStateException(
- "Client must call listModules() prior to attaching.");
- }
- if (!mModuleHandles.contains(handle)) {
- throw new IllegalArgumentException("Invalid handle: " + handle);
- }
-
- // From here on, every exception isn't client's fault.
- try {
- ModuleService moduleService = new ModuleService(callback);
- moduleService.attach(mDelegate.attach(handle, moduleService));
- return moduleService;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
+ ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback)
+ throws RemoteException {
+ return new ModuleService(mDelegate.attach(handle, callback));
}
@Override
- public void setExternalCaptureState(boolean active) {
- // Permission check.
- checkPreemptPermissions();
- // Input validation (always valid).
+ public void setExternalCaptureState(boolean active) throws RemoteException {
+ mDelegate.setExternalCaptureState(active);
+ }
- synchronized (this) {
- // State validation (always valid).
-
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.setExternalCaptureState(active);
- } catch (Exception e) {
- throw handleException(e);
- }
+ @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ if (mDelegate instanceof Dumpable) {
+ ((Dumpable) mDelegate).dump(fout);
}
}
- /**
- * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
- * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
- * caller temporarily doesn't have the right permissions to use this service.
- */
- private void checkPermissions() {
- enforcePermission(Manifest.permission.RECORD_AUDIO);
- enforcePermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD);
- }
+ private final static class ModuleService extends ISoundTriggerModule.Stub {
+ private final ISoundTriggerModule mDelegate;
- /**
- * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
- * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
- * caller temporarily doesn't have the right permissions to preempt active sound trigger
- * sessions.
- */
- private void checkPreemptPermissions() {
- enforcePermission(Manifest.permission.PREEMPT_SOUND_TRIGGER);
- }
-
- /**
- * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
- * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
- * caller temporarily doesn't have the given permission.
- *
- * @param permission The permission to check.
- */
- private void enforcePermission(String permission) {
- final int status = PermissionChecker.checkCallingOrSelfPermissionForPreflight(mContext,
- permission);
- switch (status) {
- case PermissionChecker.PERMISSION_GRANTED:
- return;
- case PermissionChecker.PERMISSION_HARD_DENIED:
- throw new SecurityException(
- String.format("Caller must have the %s permission.", permission));
- case PermissionChecker.PERMISSION_SOFT_DENIED:
- throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED,
- String.format("Caller must have the %s permission.", permission));
- default:
- throw new InternalServerError(
- new RuntimeException("Unexpected perimission check result."));
- }
- }
-
- /** State of a sound model. */
- static class ModelState {
- /** Activity state of a sound model. */
- enum Activity {
- /** Model is loaded, recognition is inactive. */
- LOADED,
- /** Model is loaded, recognition is active. */
- ACTIVE
+ private ModuleService(ISoundTriggerModule delegate) {
+ mDelegate = delegate;
}
- /** Activity state. */
- Activity activityState = Activity.LOADED;
-
- /**
- * A map of known parameter support. A missing key means we don't know yet whether the
- * parameter is supported. A null value means it is known to not be supported. A non-null
- * value indicates the valid value range.
- */
- private Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
-
- /**
- * Check that the given parameter is known to be supported for this model.
- *
- * @param modelParam The parameter key.
- */
- void checkSupported(int modelParam) {
- if (!parameterSupport.containsKey(modelParam)) {
- throw new IllegalStateException("Parameter has not been checked for support.");
- }
- ModelParameterRange range = parameterSupport.get(modelParam);
- if (range == null) {
- throw new IllegalArgumentException("Paramater is not supported.");
- }
+ @Override
+ public int loadModel(SoundModel model) throws RemoteException {
+ return mDelegate.loadModel(model);
}
- /**
- * Check that the given parameter is known to be supported for this model and that the given
- * value is a valid value for it.
- *
- * @param modelParam The parameter key.
- * @param value The value.
- */
- void checkSupported(int modelParam, int value) {
- if (!parameterSupport.containsKey(modelParam)) {
- throw new IllegalStateException("Parameter has not been checked for support.");
- }
- ModelParameterRange range = parameterSupport.get(modelParam);
- if (range == null) {
- throw new IllegalArgumentException("Paramater is not supported.");
- }
- Preconditions.checkArgumentInRange(value, range.minInclusive, range.maxInclusive,
- "value");
+ @Override
+ public int loadPhraseModel(PhraseSoundModel model) throws RemoteException {
+ return mDelegate.loadPhraseModel(model);
}
- /**
- * Update support state for the given parameter for this model.
- *
- * @param modelParam The parameter key.
- * @param range The parameter value range, or null if not supported.
- */
- void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
- parameterSupport.put(modelParam, range);
+ @Override
+ public void unloadModel(int modelHandle) throws RemoteException {
+ mDelegate.unloadModel(modelHandle);
+ ;
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, RecognitionConfig config)
+ throws RemoteException {
+ mDelegate.startRecognition(modelHandle, config);
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) throws RemoteException {
+ mDelegate.stopRecognition(modelHandle);
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) throws RemoteException {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int modelParam, int value)
+ throws RemoteException {
+ mDelegate.setModelParameter(modelHandle, modelParam, value);
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int modelParam) throws RemoteException {
+ return mDelegate.getModelParameter(modelHandle, modelParam);
+ }
+
+ @Override
+ public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam)
+ throws RemoteException {
+ return mDelegate.queryModelParameterSupport(modelHandle, modelParam);
+ }
+
+ @Override
+ public void detach() throws RemoteException {
+ mDelegate.detach();
}
}
@@ -355,395 +180,11 @@
publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE,
new SoundTriggerMiddlewareService(
- new SoundTriggerMiddlewareImpl(factories,
- new AudioSessionProviderImpl()),
- getContext()));
- }
- }
-
- /**
- * A wrapper around an {@link ISoundTriggerModule} implementation, to address the same aspects
- * mentioned in {@link SoundTriggerModule} above. This class follows the same conventions.
- */
- private class ModuleService extends ISoundTriggerModule.Stub implements ISoundTriggerCallback,
- DeathRecipient {
- private final ISoundTriggerCallback mCallback;
- private ISoundTriggerModule mDelegate;
- private @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
-
- ModuleService(@NonNull ISoundTriggerCallback callback) {
- mCallback = callback;
- try {
- mCallback.asBinder().linkToDeath(this, 0);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- void attach(@NonNull ISoundTriggerModule delegate) {
- mDelegate = delegate;
- }
-
- @Override
- public int loadModel(@NonNull SoundModel model) {
- // Permission check.
- checkPermissions();
- // Input validation.
- ValidationUtil.validateGenericModel(model);
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
-
- // From here on, every exception isn't client's fault.
- try {
- int handle = mDelegate.loadModel(model);
- mLoadedModels.put(handle, new ModelState());
- return handle;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public int loadPhraseModel(@NonNull PhraseSoundModel model) {
- // Permission check.
- checkPermissions();
- // Input validation.
- ValidationUtil.validatePhraseModel(model);
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
-
- // From here on, every exception isn't client's fault.
- try {
- int handle = mDelegate.loadPhraseModel(model);
- mLoadedModels.put(handle, new ModelState());
- return handle;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public void unloadModel(int modelHandle) {
- // Permission check.
- checkPermissions();
- // Input validation (always valid).
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
- if (modelState.activityState != ModelState.Activity.LOADED) {
- throw new IllegalStateException("Model with handle: " + modelHandle
- + " has invalid state for unloading: " + modelState.activityState);
- }
-
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.unloadModel(modelHandle);
- mLoadedModels.remove(modelHandle);
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
- // Permission check.
- checkPermissions();
- // Input validation.
- ValidationUtil.validateRecognitionConfig(config);
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
- if (modelState.activityState != ModelState.Activity.LOADED) {
- throw new IllegalStateException("Model with handle: " + modelHandle
- + " has invalid state for starting recognition: "
- + modelState.activityState);
- }
-
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.startRecognition(modelHandle, config);
- modelState.activityState = ModelState.Activity.ACTIVE;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public void stopRecognition(int modelHandle) {
- // Permission check.
- checkPermissions();
- // Input validation (always valid).
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
- // stopRecognition is idempotent - no need to check model state.
-
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.stopRecognition(modelHandle);
- modelState.activityState = ModelState.Activity.LOADED;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public void forceRecognitionEvent(int modelHandle) {
- // Permission check.
- checkPermissions();
- // Input validation (always valid).
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
- // forceRecognitionEvent is idempotent - no need to check model state.
-
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.forceRecognitionEvent(modelHandle);
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public void setModelParameter(int modelHandle, int modelParam, int value) {
- // Permission check.
- checkPermissions();
- // Input validation.
- ValidationUtil.validateModelParameter(modelParam);
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
- modelState.checkSupported(modelParam, value);
-
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.setModelParameter(modelHandle, modelParam, value);
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public int getModelParameter(int modelHandle, int modelParam) {
- // Permission check.
- checkPermissions();
- // Input validation.
- ValidationUtil.validateModelParameter(modelParam);
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
- modelState.checkSupported(modelParam);
-
- // From here on, every exception isn't client's fault.
- try {
- return mDelegate.getModelParameter(modelHandle, modelParam);
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- @Nullable
- public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam) {
- // Permission check.
- checkPermissions();
- // Input validation.
- ValidationUtil.validateModelParameter(modelParam);
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
-
- // From here on, every exception isn't client's fault.
- try {
- ModelParameterRange result = mDelegate.queryModelParameterSupport(modelHandle,
- modelParam);
- modelState.updateParameterSupport(modelParam, result);
- return result;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public void detach() {
- // Permission check.
- checkPermissions();
- // Input validation (always valid).
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has already been detached.");
- }
- if (!mLoadedModels.isEmpty()) {
- throw new IllegalStateException("Cannot detach while models are loaded.");
- }
-
- // From here on, every exception isn't client's fault.
- try {
- detachInternal();
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- private void detachInternal() {
- try {
- mDelegate.detach();
- mDelegate = null;
- mCallback.asBinder().unlinkToDeath(this, 0);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////
- // Callbacks
-
- @Override
- public void onRecognition(int modelHandle, @NonNull RecognitionEvent event) {
- synchronized (this) {
- if (event.status != RecognitionStatus.FORCED) {
- mLoadedModels.get(modelHandle).activityState = ModelState.Activity.LOADED;
- }
- try {
- mCallback.onRecognition(modelHandle, event);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
- }
- }
-
- @Override
- public void onPhraseRecognition(int modelHandle, @NonNull PhraseRecognitionEvent event) {
- synchronized (this) {
- if (event.common.status != RecognitionStatus.FORCED) {
- mLoadedModels.get(modelHandle).activityState = ModelState.Activity.LOADED;
- }
- try {
- mCallback.onPhraseRecognition(modelHandle, event);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
- }
- }
-
- @Override
- public void onRecognitionAvailabilityChange(boolean available) {
- synchronized (this) {
- try {
- mCallback.onRecognitionAvailabilityChange(available);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
- }
- }
-
- @Override
- public void onModuleDied() {
- synchronized (this) {
- try {
- mCallback.onModuleDied();
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
- }
- }
-
- @Override
- public void binderDied() {
- // This is called whenever our client process dies.
- synchronized (this) {
- try {
- // Gracefully stop all active recognitions and unload the models.
- for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
- if (entry.getValue().activityState == ModelState.Activity.ACTIVE) {
- mDelegate.stopRecognition(entry.getKey());
- }
- mDelegate.unloadModel(entry.getKey());
- }
- // Detach.
- detachInternal();
- } catch (Exception e) {
- throw handleException(e);
- }
- }
+ new SoundTriggerMiddlewareLogging(
+ new SoundTriggerMiddlewareValidation(
+ new SoundTriggerMiddlewareImpl(factories,
+ new AudioSessionProviderImpl()),
+ getContext()))));
}
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
new file mode 100644
index 0000000..c45f37d
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -0,0 +1,784 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.PermissionChecker;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger_middleware.RecognitionStatus;
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.media.soundtrigger_middleware.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This is a decorator of an {@link ISoundTriggerMiddlewareService}, which enforces permissions and
+ * correct usage by the client, as well as makes sure that exceptions representing a server
+ * malfunction do not get sent to the client.
+ * <p>
+ * This is intended to extract the non-business logic out of the underlying implementation and thus
+ * make it easier to maintain each one of those separate aspects. A design trade-off is being made
+ * here, in that this class would need to essentially eavesdrop on all the client-server
+ * communication and retain all state known to the client, while the client doesn't necessarily care
+ * about all of it, and while the server has its own representation of this information. However,
+ * in this case, this is a small amount of data, and the benefits in code elegance seem worth it.
+ * There is also some additional cost in employing a simplistic locking mechanism here, but
+ * following the same line of reasoning, the benefits in code simplicity outweigh it.
+ * <p>
+ * Every public method in this class, overriding an interface method, must follow the following
+ * pattern:
+ * <code><pre>
+ * @Override public T method(S arg) {
+ * // Permission check.
+ * checkPermissions();
+ * // Input validation.
+ * ValidationUtil.validateS(arg);
+ * synchronized (this) {
+ * // State validation.
+ * if (...state is not valid for this call...) {
+ * throw new IllegalStateException("State is invalid because...");
+ * }
+ * // From here on, every exception isn't client's fault.
+ * try {
+ * T result = mDelegate.method(arg);
+ * // Update state.;
+ * ...
+ * return result;
+ * } catch (Exception e) {
+ * throw handleException(e);
+ * }
+ * }
+ * }
+ * </pre></code>
+ * Following this patterns ensures a consistent and rigorous handling of all aspects associated
+ * with client-server separation.
+ * <p>
+ * <b>Exception handling approach:</b><br>
+ * We make sure all client faults (permissions, argument and state validation) happen first, and
+ * would throw {@link SecurityException}, {@link IllegalArgumentException}/
+ * {@link NullPointerException} or {@link
+ * IllegalStateException}, respectively. All those exceptions are treated specially by Binder and
+ * will get sent back to the client.<br>
+ * Once this is done, any subsequent fault is considered a server fault. Only {@link
+ * RecoverableException}s thrown by the implementation are special-cased: they would get sent back
+ * to the caller as a {@link ServiceSpecificException}, which is the behavior of Binder. Any other
+ * exception gets wrapped with a {@link InternalServerError}, which is specifically chosen as a type
+ * that <b>does NOT</b> get forwarded by binder. Those exceptions would be handled by a high-level
+ * exception handler on the server side, typically resulting in rebooting the server.
+ *
+ * {@hide}
+ */
+public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddlewareService, Dumpable {
+ private static final String TAG = "SoundTriggerMiddlewareValidation";
+
+ private final @NonNull ISoundTriggerMiddlewareService mDelegate;
+ private final @NonNull Context mContext;
+ private Map<Integer, Set<ModuleService>> mModules;
+
+ public SoundTriggerMiddlewareValidation(
+ @NonNull ISoundTriggerMiddlewareService delegate, @NonNull Context context) {
+ mDelegate = delegate;
+ mContext = context;
+ }
+
+ /**
+ * Generic exception handling for exceptions thrown by the underlying implementation.
+ *
+ * Would throw any {@link RecoverableException} as a {@link ServiceSpecificException} (passed
+ * by Binder to the caller) and <i>any other</i> exception as {@link InternalServerError}
+ * (<b>not</b> passed by Binder to the caller).
+ * <p>
+ * Typical usage:
+ * <code><pre>
+ * try {
+ * ... Do server operations ...
+ * } catch (Exception e) {
+ * throw handleException(e);
+ * }
+ * </pre></code>
+ */
+ static @NonNull
+ RuntimeException handleException(@NonNull Exception e) {
+ if (e instanceof RecoverableException) {
+ throw new ServiceSpecificException(((RecoverableException) e).errorCode,
+ e.getMessage());
+ }
+ throw new InternalServerError(e);
+ }
+
+ @Override
+ public @NonNull
+ SoundTriggerModuleDescriptor[] listModules() {
+ // Permission check.
+ checkPermissions();
+ // Input validation (always valid).
+
+ synchronized (this) {
+ // State validation (always valid).
+
+ // From here on, every exception isn't client's fault.
+ try {
+ SoundTriggerModuleDescriptor[] result = mDelegate.listModules();
+ mModules = new HashMap<>(result.length);
+ for (SoundTriggerModuleDescriptor desc : result) {
+ mModules.put(desc.handle, new HashSet<>());
+ }
+ return result;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public @NonNull ISoundTriggerModule attach(int handle,
+ @NonNull ISoundTriggerCallback callback) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ Objects.requireNonNull(callback);
+ Objects.requireNonNull(callback.asBinder());
+
+ synchronized (this) {
+ // State validation.
+ if (mModules == null) {
+ throw new IllegalStateException(
+ "Client must call listModules() prior to attaching.");
+ }
+ if (!mModules.containsKey(handle)) {
+ throw new IllegalArgumentException("Invalid handle: " + handle);
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ ModuleService moduleService =
+ new ModuleService(handle, callback);
+ moduleService.attach(mDelegate.attach(handle, moduleService));
+ return moduleService;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void setExternalCaptureState(boolean active) {
+ // Permission check.
+ checkPreemptPermissions();
+ // Input validation (always valid).
+
+ synchronized (this) {
+ // State validation (always valid).
+
+ // From here on, every exception isn't client's fault.
+ try {
+ mDelegate.setExternalCaptureState(active);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+
+ /**
+ * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * caller temporarily doesn't have the right permissions to use this service.
+ */
+ void checkPermissions() {
+ enforcePermission(Manifest.permission.RECORD_AUDIO);
+ enforcePermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD);
+ }
+
+ /**
+ * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * caller temporarily doesn't have the right permissions to preempt active sound trigger
+ * sessions.
+ */
+ void checkPreemptPermissions() {
+ enforcePermission(Manifest.permission.PREEMPT_SOUND_TRIGGER);
+ }
+
+ /**
+ * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * caller temporarily doesn't have the given permission.
+ *
+ * @param permission The permission to check.
+ */
+ void enforcePermission(String permission) {
+ final int status = PermissionChecker.checkCallingOrSelfPermissionForPreflight(mContext,
+ permission);
+ switch (status) {
+ case PermissionChecker.PERMISSION_GRANTED:
+ return;
+ case PermissionChecker.PERMISSION_HARD_DENIED:
+ throw new SecurityException(
+ String.format("Caller must have the %s permission.", permission));
+ case PermissionChecker.PERMISSION_SOFT_DENIED:
+ throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED,
+ String.format("Caller must have the %s permission.", permission));
+ default:
+ throw new InternalServerError(
+ new RuntimeException("Unexpected perimission check result."));
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ throw new UnsupportedOperationException(
+ "This implementation is not inteded to be used directly with Binder.");
+ }
+
+ @Override public void dump(PrintWriter pw) {
+ synchronized (this) {
+ if (mModules != null) {
+ for (int handle : mModules.keySet()) {
+ pw.println("=========================================");
+ pw.printf("Active sessions for module %d", handle);
+ pw.println();
+ pw.println("=========================================");
+ for (ModuleService session : mModules.get(handle)) {
+ session.dump(pw);
+ }
+ }
+ }
+ }
+ pw.println();
+
+ if (mDelegate instanceof Dumpable) {
+ ((Dumpable) mDelegate).dump(pw);
+ }
+
+ }
+
+ /** State of a sound model. */
+ static class ModelState {
+ /** Activity state of a sound model. */
+ enum Activity {
+ /** Model is loaded, recognition is inactive. */
+ LOADED,
+ /** Model is loaded, recognition is active. */
+ ACTIVE
+ }
+
+ /** Activity state. */
+ Activity activityState = Activity.LOADED;
+
+ /**
+ * A map of known parameter support. A missing key means we don't know yet whether the
+ * parameter is supported. A null value means it is known to not be supported. A non-null
+ * value indicates the valid value range.
+ */
+ private Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
+
+ /**
+ * Check that the given parameter is known to be supported for this model.
+ *
+ * @param modelParam The parameter key.
+ */
+ void checkSupported(int modelParam) {
+ if (!parameterSupport.containsKey(modelParam)) {
+ throw new IllegalStateException("Parameter has not been checked for support.");
+ }
+ ModelParameterRange range = parameterSupport.get(modelParam);
+ if (range == null) {
+ throw new IllegalArgumentException("Paramater is not supported.");
+ }
+ }
+
+ /**
+ * Check that the given parameter is known to be supported for this model and that the given
+ * value is a valid value for it.
+ *
+ * @param modelParam The parameter key.
+ * @param value The value.
+ */
+ void checkSupported(int modelParam, int value) {
+ if (!parameterSupport.containsKey(modelParam)) {
+ throw new IllegalStateException("Parameter has not been checked for support.");
+ }
+ ModelParameterRange range = parameterSupport.get(modelParam);
+ if (range == null) {
+ throw new IllegalArgumentException("Paramater is not supported.");
+ }
+ Preconditions.checkArgumentInRange(value, range.minInclusive, range.maxInclusive,
+ "value");
+ }
+
+ /**
+ * Update support state for the given parameter for this model.
+ *
+ * @param modelParam The parameter key.
+ * @param range The parameter value range, or null if not supported.
+ */
+ void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
+ parameterSupport.put(modelParam, range);
+ }
+ }
+
+ /**
+ * A wrapper around an {@link ISoundTriggerModule} implementation, to address the same aspects
+ * mentioned in {@link SoundTriggerModule} above. This class follows the same conventions.
+ */
+ private class ModuleService extends ISoundTriggerModule.Stub implements ISoundTriggerCallback,
+ IBinder.DeathRecipient {
+ private final ISoundTriggerCallback mCallback;
+ private ISoundTriggerModule mDelegate;
+ private @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
+ private final int mHandle;
+
+ ModuleService(int handle, @NonNull ISoundTriggerCallback callback) {
+ mCallback = callback;
+ mHandle = handle;
+ try {
+ mCallback.asBinder().linkToDeath(null, 0);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ void attach(@NonNull ISoundTriggerModule delegate) {
+ mDelegate = delegate;
+ mModules.get(mHandle).add(this);
+ }
+
+ @Override
+ public int loadModel(@NonNull SoundModel model) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ ValidationUtil.validateGenericModel(model);
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ int handle = mDelegate.loadModel(model);
+ mLoadedModels.put(handle, new ModelState());
+ return handle;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public int loadPhraseModel(@NonNull PhraseSoundModel model) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ ValidationUtil.validatePhraseModel(model);
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ int handle = mDelegate.loadPhraseModel(model);
+ mLoadedModels.put(handle, new ModelState());
+ return handle;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void unloadModel(int modelHandle) {
+ // Permission check.
+ checkPermissions();
+ // Input validation (always valid).
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+ if (modelState.activityState
+ != ModelState.Activity.LOADED) {
+ throw new IllegalStateException("Model with handle: " + modelHandle
+ + " has invalid state for unloading: " + modelState.activityState);
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ mDelegate.unloadModel(modelHandle);
+ mLoadedModels.remove(modelHandle);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ ValidationUtil.validateRecognitionConfig(config);
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+ if (modelState.activityState
+ != ModelState.Activity.LOADED) {
+ throw new IllegalStateException("Model with handle: " + modelHandle
+ + " has invalid state for starting recognition: "
+ + modelState.activityState);
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ mDelegate.startRecognition(modelHandle, config);
+ modelState.activityState =
+ ModelState.Activity.ACTIVE;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ // Permission check.
+ checkPermissions();
+ // Input validation (always valid).
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+ // stopRecognition is idempotent - no need to check model state.
+
+ // From here on, every exception isn't client's fault.
+ try {
+ mDelegate.stopRecognition(modelHandle);
+ modelState.activityState =
+ ModelState.Activity.LOADED;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ // Permission check.
+ checkPermissions();
+ // Input validation (always valid).
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+ // forceRecognitionEvent is idempotent - no need to check model state.
+
+ // From here on, every exception isn't client's fault.
+ try {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int modelParam, int value) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ ValidationUtil.validateModelParameter(modelParam);
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+ modelState.checkSupported(modelParam, value);
+
+ // From here on, every exception isn't client's fault.
+ try {
+ mDelegate.setModelParameter(modelHandle, modelParam, value);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int modelParam) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ ValidationUtil.validateModelParameter(modelParam);
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+ modelState.checkSupported(modelParam);
+
+ // From here on, every exception isn't client's fault.
+ try {
+ return mDelegate.getModelParameter(modelHandle, modelParam);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ @Nullable
+ public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ ValidationUtil.validateModelParameter(modelParam);
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ ModelParameterRange result = mDelegate.queryModelParameterSupport(modelHandle,
+ modelParam);
+ modelState.updateParameterSupport(modelParam, result);
+ return result;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void detach() {
+ // Permission check.
+ checkPermissions();
+ // Input validation (always valid).
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has already been detached.");
+ }
+ if (!mLoadedModels.isEmpty()) {
+ throw new IllegalStateException("Cannot detach while models are loaded.");
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ detachInternal();
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+
+ private void detachInternal() {
+ try {
+ mDelegate.detach();
+ mDelegate = null;
+ mCallback.asBinder().unlinkToDeath(null, 0);
+ mModules.get(mHandle).remove(this);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ void dump(PrintWriter pw) {
+ pw.printf("Loaded models for session %s (handle, active)", toString());
+ pw.println();
+ pw.println("-------------------------------");
+ for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
+ pw.print(entry.getKey());
+ pw.print('\t');
+ pw.print(entry.getValue().activityState.name());
+ pw.println();
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Callbacks
+
+ @Override
+ public void onRecognition(int modelHandle, @NonNull RecognitionEvent event) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ if (event.status != RecognitionStatus.FORCED) {
+ mLoadedModels.get(modelHandle).activityState =
+ ModelState.Activity.LOADED;
+ }
+ try {
+ mCallback.onRecognition(modelHandle, event);
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
+ }
+ }
+
+ @Override
+ public void onPhraseRecognition(int modelHandle, @NonNull PhraseRecognitionEvent event) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ if (event.common.status != RecognitionStatus.FORCED) {
+ mLoadedModels.get(modelHandle).activityState =
+ ModelState.Activity.LOADED;
+ }
+ try {
+ mCallback.onPhraseRecognition(modelHandle, event);
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
+ }
+ }
+
+ @Override
+ public void onRecognitionAvailabilityChange(boolean available) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ try {
+ mCallback.onRecognitionAvailabilityChange(available);
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
+ }
+ }
+
+ @Override
+ public void onModuleDied() {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ try {
+ mCallback.onModuleDied();
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ // This is called whenever our client process dies.
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ try {
+ // Gracefully stop all active recognitions and unload the models.
+ for (Map.Entry<Integer, ModelState> entry :
+ mLoadedModels.entrySet()) {
+ if (entry.getValue().activityState
+ == ModelState.Activity.ACTIVE) {
+ mDelegate.stopRecognition(entry.getKey());
+ }
+ mDelegate.unloadModel(entry.getKey());
+ }
+ // Detach.
+ detachInternal();
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index aa1558e..d6390184 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -123,7 +123,6 @@
*/
synchronized @NonNull
ISoundTriggerModule attach(@NonNull ISoundTriggerCallback callback) {
- Log.d(TAG, "attach()");
Session session = new Session(callback);
mActiveSessions.add(session);
return session;
@@ -149,8 +148,6 @@
* @param active true iff external capture is active.
*/
synchronized void setExternalCaptureState(boolean active) {
- Log.d(TAG, String.format("setExternalCaptureState(active=%b)", active));
-
if (mProperties.concurrentCapture) {
// If we support concurrent capture, we don't care about any of this.
return;
@@ -235,7 +232,6 @@
@Override
public void detach() {
- Log.d(TAG, "detach()");
synchronized (SoundTriggerModule.this) {
if (mCallback == null) {
return;
@@ -247,8 +243,6 @@
@Override
public int loadModel(@NonNull SoundModel model) {
- Log.d(TAG, String.format("loadModel(model=%s)", model));
-
// We must do this outside the lock, to avoid possible deadlocks with the remote process
// that provides the audio sessions, which may also be calling into us.
SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
@@ -276,8 +270,6 @@
@Override
public int loadPhraseModel(@NonNull PhraseSoundModel model) {
- Log.d(TAG, String.format("loadPhraseModel(model=%s)", model));
-
// We must do this outside the lock, to avoid possible deadlocks with the remote process
// that provides the audio sessions, which may also be calling into us.
SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
@@ -306,10 +298,7 @@
@Override
public void unloadModel(int modelHandle) {
- Log.d(TAG, String.format("unloadModel(handle=%d)", modelHandle));
-
int sessionId;
-
synchronized (SoundTriggerModule.this) {
checkValid();
sessionId = mLoadedModels.get(modelHandle).unload();
@@ -323,8 +312,6 @@
@Override
public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
- Log.d(TAG,
- String.format("startRecognition(handle=%d, config=%s)", modelHandle, config));
synchronized (SoundTriggerModule.this) {
checkValid();
mLoadedModels.get(modelHandle).startRecognition(config);
@@ -333,7 +320,6 @@
@Override
public void stopRecognition(int modelHandle) {
- Log.d(TAG, String.format("stopRecognition(handle=%d)", modelHandle));
synchronized (SoundTriggerModule.this) {
mLoadedModels.get(modelHandle).stopRecognition();
}
@@ -341,7 +327,6 @@
@Override
public void forceRecognitionEvent(int modelHandle) {
- Log.d(TAG, String.format("forceRecognitionEvent(handle=%d)", modelHandle));
synchronized (SoundTriggerModule.this) {
checkValid();
mLoadedModels.get(modelHandle).forceRecognitionEvent();
@@ -350,9 +335,6 @@
@Override
public void setModelParameter(int modelHandle, int modelParam, int value) {
- Log.d(TAG,
- String.format("setModelParameter(handle=%d, param=%d, value=%d)", modelHandle,
- modelParam, value));
synchronized (SoundTriggerModule.this) {
checkValid();
mLoadedModels.get(modelHandle).setParameter(modelParam, value);
@@ -361,8 +343,6 @@
@Override
public int getModelParameter(int modelHandle, int modelParam) {
- Log.d(TAG, String.format("getModelParameter(handle=%d, param=%d)", modelHandle,
- modelParam));
synchronized (SoundTriggerModule.this) {
checkValid();
return mLoadedModels.get(modelHandle).getParameter(modelParam);
@@ -372,8 +352,6 @@
@Override
@Nullable
public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam) {
- Log.d(TAG, String.format("queryModelParameterSupport(handle=%d, param=%d)", modelHandle,
- modelParam));
synchronized (SoundTriggerModule.this) {
checkValid();
return mLoadedModels.get(modelHandle).queryModelParameterSupport(modelParam);
@@ -584,8 +562,6 @@
public void recognitionCallback(
@NonNull ISoundTriggerHwCallback.RecognitionEvent recognitionEvent,
int cookie) {
- Log.d(TAG, String.format("recognitionCallback_2_1(event=%s, cookie=%d)",
- recognitionEvent, cookie));
synchronized (SoundTriggerModule.this) {
android.media.soundtrigger_middleware.RecognitionEvent aidlEvent =
ConversionUtil.hidl2aidlRecognitionEvent(recognitionEvent);
@@ -608,8 +584,6 @@
public void phraseRecognitionCallback(
@NonNull ISoundTriggerHwCallback.PhraseRecognitionEvent phraseRecognitionEvent,
int cookie) {
- Log.d(TAG, String.format("phraseRecognitionCallback_2_1(event=%s, cookie=%d)",
- phraseRecognitionEvent, cookie));
synchronized (SoundTriggerModule.this) {
android.media.soundtrigger_middleware.PhraseRecognitionEvent aidlEvent =
ConversionUtil.hidl2aidlPhraseRecognitionEvent(phraseRecognitionEvent);
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 3c8ef6c..3f8f6bf 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -188,6 +188,7 @@
private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8;
private static final int OP_FLAGS_PULLED = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED;
+ private static final String COMMON_PERMISSION_PREFIX = "android.permission.";
private final Object mNetworkStatsLock = new Object();
@GuardedBy("mNetworkStatsLock")
@@ -2627,6 +2628,10 @@
continue;
}
+ if (permName.startsWith(COMMON_PERMISSION_PREFIX)) {
+ permName = permName.substring(COMMON_PERMISSION_PREFIX.length());
+ }
+
StatsEvent.Builder e = StatsEvent.newBuilder();
e.setAtomId(atomTag);
e.writeString(permName);
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index bad2b78..4eff954f 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.server.tv.tunerresourcemanager;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* A client profile object used by the Tuner Resource Manager to record the registered clients'
* information.
@@ -23,12 +25,14 @@
* @hide
*/
public final class ClientProfile {
+
public static final int INVALID_GROUP_ID = -1;
+
/**
* Client id sent to the client when registering with
* {@link #registerClientProfile(ResourceClientProfile, TunerResourceManagerCallback, int[])}
*/
- private final int mClientId;
+ private final int mId;
/**
* see {@link ResourceClientProfile}
@@ -41,7 +45,7 @@
private final int mUseCase;
/**
- * Process id queried from {@link TvInputManager#}
+ * Process id queried from {@link TvInputManager#getPid(String)}.
*/
private final int mProcessId;
@@ -59,6 +63,11 @@
private int mNiceValue;
/**
+ * List of the frontend ids that are used by the current client.
+ */
+ private List<Integer> mUsingFrontendIds = new ArrayList<>();
+
+ /**
* Optional arbitrary priority value given by the client.
*
* <p>This value can override the default priorotiy calculated from
@@ -66,18 +75,15 @@
*/
private int mPriority;
- private ClientProfile(ClientProfileBuilder builder) {
- this.mClientId = builder.mClientId;
+ private ClientProfile(Builder builder) {
+ this.mId = builder.mId;
this.mTvInputSessionId = builder.mTvInputSessionId;
this.mUseCase = builder.mUseCase;
this.mProcessId = builder.mProcessId;
- this.mGroupId = builder.mGroupId;
- this.mNiceValue = builder.mNiceValue;
- this.mPriority = builder.mPriority;
}
- public int getClientId() {
- return mClientId;
+ public int getId() {
+ return mId;
}
public String getTvInputSessionId() {
@@ -116,26 +122,47 @@
mNiceValue = niceValue;
}
+ /**
+ * Set when the client starts to use a frontend.
+ *
+ * @param frontendId being used.
+ */
+ public void useFrontend(int frontendId) {
+ mUsingFrontendIds.add(frontendId);
+ }
+
+ public List<Integer> getInUseFrontendIds() {
+ return mUsingFrontendIds;
+ }
+
+ /**
+ * Called when the client released a frontend.
+ *
+ * <p>This could happen when client resource reclaimed.
+ *
+ * @param frontendId being released.
+ */
+ public void releaseFrontend(int frontendId) {
+ mUsingFrontendIds.remove(frontendId);
+ }
+
@Override
public String toString() {
- return "ClientProfile: " + this.mClientId + ", " + this.mTvInputSessionId + ", "
- + this.mUseCase + ", " + this.mProcessId;
+ return "ClientProfile[id=" + this.mId + ", tvInputSessionId=" + this.mTvInputSessionId
+ + ", useCase=" + this.mUseCase + ", processId=" + this.mProcessId + "]";
}
/**
* Builder class for {@link ClientProfile}.
*/
- public static class ClientProfileBuilder {
- private final int mClientId;
+ public static class Builder {
+ private final int mId;
private String mTvInputSessionId;
private int mUseCase;
private int mProcessId;
- private int mGroupId;
- private int mNiceValue;
- private int mPriority;
- ClientProfileBuilder(int clientId) {
- this.mClientId = clientId;
+ Builder(int id) {
+ this.mId = id;
}
/**
@@ -143,7 +170,7 @@
*
* @param useCase the useCase of the client.
*/
- public ClientProfileBuilder useCase(int useCase) {
+ public Builder useCase(int useCase) {
this.mUseCase = useCase;
return this;
}
@@ -153,7 +180,7 @@
*
* @param tvInputSessionId the id of the tv input session.
*/
- public ClientProfileBuilder tvInputSessionId(String tvInputSessionId) {
+ public Builder tvInputSessionId(String tvInputSessionId) {
this.mTvInputSessionId = tvInputSessionId;
return this;
}
@@ -163,42 +190,11 @@
*
* @param processId the id of process.
*/
- public ClientProfileBuilder processId(int processId) {
+ public Builder processId(int processId) {
this.mProcessId = processId;
return this;
}
-
- /**
- * Builder for {@link ClientProfile}.
- *
- * @param groupId the id of the group that shares the same resource.
- */
- public ClientProfileBuilder groupId(int groupId) {
- this.mGroupId = groupId;
- return this;
- }
-
- /**
- * Builder for {@link ClientProfile}.
- *
- * @param niceValue the nice value of the client.
- */
- public ClientProfileBuilder niceValue(int niceValue) {
- this.mNiceValue = niceValue;
- return this;
- }
-
- /**
- * Builder for {@link ClientProfile}.
- *
- * @param priority the priority value of the client.
- */
- public ClientProfileBuilder priority(int priority) {
- this.mPriority = priority;
- return this;
- }
-
/**
* Build a {@link ClientProfile}.
*
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
new file mode 100644
index 0000000..a109265
--- /dev/null
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 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.tv.tunerresourcemanager;
+
+import android.annotation.Nullable;
+import android.media.tv.tuner.frontend.FrontendSettings.Type;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A frontend resource object used by the Tuner Resource Manager to record the tuner frontend
+ * information.
+ *
+ * @hide
+ */
+public final class FrontendResource {
+ public static final int INVALID_OWNER_ID = -1;
+
+ /**
+ * Id of the current frontend. Should not be changed and should be aligned with the driver level
+ * implementation.
+ */
+ private final int mId;
+
+ /**
+ * see {@link android.media.tv.tuner.frontend.FrontendSettings.Type}
+ */
+ @Type private final int mType;
+
+ /**
+ * The exclusive group id of the FE. FEs under the same id can't be used at the same time.
+ */
+ private final int mExclusiveGroupId;
+
+ /**
+ * An array to save all the FE ids under the same exclisive group.
+ */
+ private List<Integer> mExclusiveGroupMemberFeIds = new ArrayList<>();
+
+ /**
+ * If the current resource is in use. Once resources under the same exclusive group id is in use
+ * all other resources in the same group would be considered in use.
+ */
+ private boolean mIsInUse;
+
+ /**
+ * The owner client's id if this resource is occupied. Owner of the resource under the same
+ * exclusive group id would be considered as the whole group's owner.
+ */
+ private int mOwnerClientId = INVALID_OWNER_ID;
+
+ private FrontendResource(Builder builder) {
+ this.mId = builder.mId;
+ this.mType = builder.mType;
+ this.mExclusiveGroupId = builder.mExclusiveGroupId;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public int getExclusiveGroupId() {
+ return mExclusiveGroupId;
+ }
+
+ public List<Integer> getExclusiveGroupMemberFeIds() {
+ return mExclusiveGroupMemberFeIds;
+ }
+
+ /**
+ * Add one id into the exclusive group member id list.
+ *
+ * @param id the id to be added.
+ */
+ public void addExclusiveGroupMemberFeId(int id) {
+ mExclusiveGroupMemberFeIds.add(id);
+ }
+
+ /**
+ * Add one id list to the exclusive group member id list.
+ *
+ * @param ids the id list to be added.
+ */
+ public void addExclusiveGroupMemberFeId(List<Integer> ids) {
+ mExclusiveGroupMemberFeIds.addAll(ids);
+ }
+
+ /**
+ * Remove one id from the exclusive group member id list.
+ *
+ * @param id the id to be removed.
+ */
+ public void removeExclusiveGroupMemberFeId(int id) {
+ mExclusiveGroupMemberFeIds.remove(new Integer(id));
+ }
+
+ public boolean isInUse() {
+ return mIsInUse;
+ }
+
+ public int getOwnerClientId() {
+ return mOwnerClientId;
+ }
+
+ /**
+ * Set an owner client on the resource.
+ *
+ * @param ownerClientId the id of the owner client.
+ */
+ public void setOwner(int ownerClientId) {
+ mIsInUse = true;
+ mOwnerClientId = ownerClientId;
+ }
+
+ /**
+ * Remove an owner client from the resource.
+ */
+ public void removeOwner() {
+ mIsInUse = false;
+ mOwnerClientId = INVALID_OWNER_ID;
+ }
+
+ @Override
+ public String toString() {
+ return "FrontendResource[id=" + this.mId + ", type=" + this.mType
+ + ", exclusiveGId=" + this.mExclusiveGroupId + ", exclusiveGMemeberIds="
+ + Arrays.toString(this.mExclusiveGroupMemberFeIds.toArray())
+ + ", isInUse=" + this.mIsInUse + ", ownerClientId=" + this.mOwnerClientId + "]";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o instanceof FrontendResource) {
+ FrontendResource fe = (FrontendResource) o;
+ return mId == fe.getId() && mType == fe.getType()
+ && mExclusiveGroupId == fe.getExclusiveGroupId()
+ && mExclusiveGroupMemberFeIds.equals(fe.getExclusiveGroupMemberFeIds())
+ && mIsInUse == fe.isInUse() && mOwnerClientId == fe.getOwnerClientId();
+ }
+ return false;
+ }
+
+ /**
+ * Builder class for {@link FrontendResource}.
+ */
+ public static class Builder {
+ private final int mId;
+ @Type private int mType;
+ private int mExclusiveGroupId;
+
+ Builder(int id) {
+ this.mId = id;
+ }
+
+ /**
+ * Builder for {@link FrontendResource}.
+ *
+ * @param type the type of the frontend. See {@link Type}
+ */
+ public Builder type(@Type int type) {
+ this.mType = type;
+ return this;
+ }
+
+ /**
+ * Builder for {@link FrontendResource}.
+ *
+ * @param exclusiveGroupId the id of exclusive group.
+ */
+ public Builder exclusiveGroupId(int exclusiveGroupId) {
+ this.mExclusiveGroupId = exclusiveGroupId;
+ return this;
+ }
+
+ /**
+ * Build a {@link FrontendResource}.
+ *
+ * @return {@link FrontendResource}.
+ */
+ public FrontendResource build() {
+ FrontendResource frontendResource = new FrontendResource(this);
+ return frontendResource;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 49a7045..cb31a50 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -28,32 +28,48 @@
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.os.Binder;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemService;
import java.util.ArrayList;
import java.util.List;
/**
- * This class provides a system service that manages the TV tuner resources.
- *
- * @hide
- */
+ * This class provides a system service that manages the TV tuner resources.
+ *
+ * @hide
+ */
public class TunerResourceManagerService extends SystemService {
private static final String TAG = "TunerResourceManagerService";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private SparseArray<ClientProfile> mClientProfiles = new SparseArray<>();
- private SparseArray<IResourcesReclaimListener> mListeners = new SparseArray<>();
- private int mNextUnusedFrontendId = 0;
- private List<Integer> mReleasedClientId = new ArrayList<Integer>();
+ public static final int INVALID_CLIENT_ID = -1;
+ private static final int MAX_CLIENT_PRIORITY = 1000;
+
+ // Array of the registered client profiles
+ @VisibleForTesting private SparseArray<ClientProfile> mClientProfiles = new SparseArray<>();
+ private int mNextUnusedClientId = 0;
+ private List<Integer> mRegisteredClientIds = new ArrayList<Integer>();
+
+ // Array of the current available frontend resources
+ @VisibleForTesting
+ private SparseArray<FrontendResource> mFrontendResources = new SparseArray<>();
+ // Array of the current available frontend ids
private List<Integer> mAvailableFrontendIds = new ArrayList<Integer>();
+ private SparseArray<IResourcesReclaimListener> mListeners = new SparseArray<>();
+
private TvInputManager mManager;
+ private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints();
+
+ // Used to synchronize the access to the service.
+ private final Object mLock = new Object();
public TunerResourceManagerService(@Nullable Context context) {
super(context);
@@ -61,97 +77,78 @@
@Override
public void onStart() {
- publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService());
- mManager = (TvInputManager) getContext()
- .getSystemService(Context.TV_INPUT_SERVICE);
+ onStart(false /*isForTesting*/);
+ }
+
+ @VisibleForTesting
+ protected void onStart(boolean isForTesting) {
+ if (!isForTesting) {
+ publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService());
+ }
+ mManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
+ mPriorityCongfig.parse();
}
private final class BinderService extends ITunerResourceManager.Stub {
@Override
public void registerClientProfile(@NonNull ResourceClientProfile profile,
- @NonNull IResourcesReclaimListener listener,
- @NonNull int[] clientId) {
- if (DEBUG) {
- Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")");
+ @NonNull IResourcesReclaimListener listener, @NonNull int[] clientId)
+ throws RemoteException {
+ enforceAccessPermission();
+ if (profile == null) {
+ throw new RemoteException("ResourceClientProfile can't be null");
}
- // TODO tell if the client already exists
- if (mReleasedClientId.isEmpty()) {
- clientId[0] = mNextUnusedFrontendId++;
- } else {
- clientId[0] = mReleasedClientId.get(0);
- mReleasedClientId.remove(0);
+ if (clientId == null) {
+ throw new RemoteException("clientId can't be null!");
}
- if (mManager == null) {
- Slog.e(TAG, "TvInputManager is null. Can't register client profile.");
- return;
+ if (!mPriorityCongfig.isDefinedUseCase(profile.getUseCase())) {
+ throw new RemoteException("Use undefined client use case:" + profile.getUseCase());
}
- int callingPid = mManager.getClientPid(profile.getTvInputSessionId());
-
- ClientProfile clientProfile = new ClientProfile.ClientProfileBuilder(
- clientId[0])
- .tvInputSessionId(profile.getTvInputSessionId())
- .useCase(profile.getUseCase())
- .processId(callingPid)
- .build();
- mClientProfiles.append(clientId[0], clientProfile);
- mListeners.append(clientId[0], listener);
+ synchronized (mLock) {
+ registerClientProfileInternal(profile, listener, clientId);
+ }
}
@Override
- public void unregisterClientProfile(int clientId) {
- if (DEBUG) {
- Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
+ public void unregisterClientProfile(int clientId) throws RemoteException {
+ enforceAccessPermission();
+ synchronized (mLock) {
+ if (!checkClientExists(clientId)) {
+ Slog.e(TAG, "Unregistering non exists client:" + clientId);
+ return;
+ }
+ unregisterClientProfileInternal(clientId);
}
-
- mClientProfiles.remove(clientId);
- mListeners.remove(clientId);
- mReleasedClientId.add(clientId);
}
@Override
public boolean updateClientPriority(int clientId, int priority, int niceValue) {
- if (DEBUG) {
- Slog.d(TAG, "updateClientPriority(clientId=" + clientId
- + ", priority=" + priority + ", niceValue=" + niceValue + ")");
+ enforceAccessPermission();
+ synchronized (mLock) {
+ return updateClientPriorityInternal(clientId, priority, niceValue);
}
-
- ClientProfile profile = mClientProfiles.get(clientId);
- if (profile == null) {
- Slog.e(TAG, "Can not find client profile with id " + clientId
- + " when trying to update the client priority.");
- return false;
- }
-
- profile.setPriority(priority);
- profile.setNiceValue(niceValue);
-
- return true;
}
@Override
- public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos)
- throws RemoteException {
- if (infos == null || infos.length == 0) {
- Slog.d(TAG, "Can't update with empty frontend info");
- return;
+ public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) throws RemoteException {
+ enforceAccessPermission();
+ if (infos == null) {
+ throw new RemoteException("TunerFrontendInfo can't be null");
}
-
- if (DEBUG) {
- Slog.d(TAG, "updateFrontendInfo:");
- for (int i = 0; i < infos.length; i++) {
- Slog.d(TAG, infos[i].toString());
- }
+ synchronized (mLock) {
+ setFrontendInfoListInternal(infos);
}
}
@Override
public void updateCasInfo(int casSystemId, int maxSessionNum) {
if (DEBUG) {
- Slog.d(TAG, "updateCasInfo(casSystemId="
- + casSystemId + ", maxSessionNum=" + maxSessionNum + ")");
+ Slog.d(TAG,
+ "updateCasInfo(casSystemId=" + casSystemId
+ + ", maxSessionNum=" + maxSessionNum + ")");
}
}
@@ -166,49 +163,30 @@
@Override
public boolean requestFrontend(@NonNull TunerFrontendRequest request,
- @NonNull int[] frontendId) throws RemoteException {
- if (DEBUG) {
- Slog.d(TAG, "requestFrontend(request=" + request + ")");
+ @NonNull int[] frontendId) throws RemoteException {
+ enforceAccessPermission();
+ if (frontendId == null) {
+ throw new RemoteException("frontendId can't be null");
}
-
- frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID;
-
- if (getContext() == null) {
- Slog.e(TAG, "Can not find context when requesting frontend");
- return false;
+ synchronized (mLock) {
+ try {
+ return requestFrontendInternal(request, frontendId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
-
- if (mClientProfiles.get(request.getClientId()) == null) {
- Slog.e(TAG, "Request from unregistered client. Id: "
- + request.getClientId());
- return false;
- }
-
- String sessionId = mClientProfiles.get(request.getClientId())
- .getTvInputSessionId();
-
- if (DEBUG) {
- Slog.d(TAG, "session Id:" + sessionId + ")");
- }
-
- if (DEBUG) {
- Slog.d(TAG, "No available Frontend found.");
- }
-
- return false;
}
@Override
public void shareFrontend(int selfClientId, int targetClientId) {
if (DEBUG) {
- Slog.d(TAG, "shareFrontend from "
- + selfClientId + " with " + targetClientId);
+ Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId);
}
}
@Override
- public boolean requestCasSession(@NonNull CasSessionRequest request,
- @NonNull int[] sessionResourceId) {
+ public boolean requestCasSession(
+ @NonNull CasSessionRequest request, @NonNull int[] sessionResourceId) {
if (DEBUG) {
Slog.d(TAG, "requestCasSession(request=" + request + ")");
}
@@ -246,13 +224,284 @@
}
@Override
- public boolean isHigherPriority(ResourceClientProfile challengerProfile,
- ResourceClientProfile holderProfile) {
+ public boolean isHigherPriority(
+ ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) {
if (DEBUG) {
- Slog.d(TAG, "isHigherPriority(challengerProfile=" + challengerProfile
- + ", holderProfile=" + challengerProfile + ")");
+ Slog.d(TAG,
+ "isHigherPriority(challengerProfile=" + challengerProfile
+ + ", holderProfile=" + challengerProfile + ")");
}
return true;
}
}
+
+ @VisibleForTesting
+ protected void registerClientProfileInternal(ResourceClientProfile profile,
+ IResourcesReclaimListener listener, int[] clientId) {
+ if (DEBUG) {
+ Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")");
+ }
+
+ clientId[0] = INVALID_CLIENT_ID;
+ if (mManager == null) {
+ Slog.e(TAG, "TvInputManager is null. Can't register client profile.");
+ return;
+ }
+ // TODO tell if the client already exists
+ clientId[0] = mNextUnusedClientId++;
+
+ int pid = profile.getTvInputSessionId() == null
+ ? Binder.getCallingPid() /*callingPid*/
+ : mManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/
+
+ ClientProfile clientProfile = new ClientProfile.Builder(clientId[0])
+ .tvInputSessionId(profile.getTvInputSessionId())
+ .useCase(profile.getUseCase())
+ .processId(pid)
+ .build();
+ clientProfile.setPriority(getClientPriority(profile.getUseCase(), pid));
+
+ mClientProfiles.append(clientId[0], clientProfile);
+ mListeners.append(clientId[0], listener);
+ mRegisteredClientIds.add(clientId[0]);
+ }
+
+ @VisibleForTesting
+ protected void unregisterClientProfileInternal(int clientId) {
+ if (DEBUG) {
+ Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
+ }
+ for (int id : getClientProfile(clientId).getInUseFrontendIds()) {
+ getFrontendResource(id).removeOwner();
+ for (int groupMemberId : getFrontendResource(id).getExclusiveGroupMemberFeIds()) {
+ getFrontendResource(groupMemberId).removeOwner();
+ }
+ }
+ mClientProfiles.remove(clientId);
+ mListeners.remove(clientId);
+ mRegisteredClientIds.remove(clientId);
+ }
+
+ @VisibleForTesting
+ protected boolean updateClientPriorityInternal(int clientId, int priority, int niceValue) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "updateClientPriority(clientId=" + clientId + ", priority=" + priority
+ + ", niceValue=" + niceValue + ")");
+ }
+
+ ClientProfile profile = getClientProfile(clientId);
+ if (profile == null) {
+ Slog.e(TAG,
+ "Can not find client profile with id " + clientId
+ + " when trying to update the client priority.");
+ return false;
+ }
+
+ profile.setPriority(priority);
+ profile.setNiceValue(niceValue);
+
+ return true;
+ }
+
+ @VisibleForTesting
+ protected void setFrontendInfoListInternal(TunerFrontendInfo[] infos) {
+ if (DEBUG) {
+ Slog.d(TAG, "updateFrontendInfo:");
+ for (int i = 0; i < infos.length; i++) {
+ Slog.d(TAG, infos[i].toString());
+ }
+ }
+
+ // An arrayList to record the frontends pending on updating. Ids will be removed
+ // from this list once its updating finished. Any frontend left in this list when all
+ // the updates are done will be removed from mAvailableFrontendIds and
+ // mFrontendResources.
+ List<Integer> updatingFrontendIds = new ArrayList<>(mAvailableFrontendIds);
+
+ // Update frontendResources sparse array and other mappings accordingly
+ for (int i = 0; i < infos.length; i++) {
+ if (getFrontendResource(infos[i].getId()) != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Frontend id=" + infos[i].getId() + "exists.");
+ }
+ updatingFrontendIds.remove(new Integer(infos[i].getId()));
+ } else {
+ // Add a new fe resource
+ FrontendResource newFe = new FrontendResource.Builder(infos[i].getId())
+ .type(infos[i].getFrontendType())
+ .exclusiveGroupId(infos[i].getExclusiveGroupId())
+ .build();
+ // Update the exclusive group member list in all the existing Frontend resource
+ for (Integer feId : mAvailableFrontendIds) {
+ FrontendResource fe = getFrontendResource(feId.intValue());
+ if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) {
+ newFe.addExclusiveGroupMemberFeId(fe.getId());
+ newFe.addExclusiveGroupMemberFeId(fe.getExclusiveGroupMemberFeIds());
+ for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) {
+ getFrontendResource(excGroupmemberFeId.intValue())
+ .addExclusiveGroupMemberFeId(newFe.getId());
+ }
+ fe.addExclusiveGroupMemberFeId(newFe.getId());
+ break;
+ }
+ }
+ // Update resource list and available id list
+ mFrontendResources.append(newFe.getId(), newFe);
+ mAvailableFrontendIds.add(newFe.getId());
+ }
+ }
+
+ // TODO check if the removing resource is in use or not. Handle the conflict.
+ for (Integer removingId : updatingFrontendIds) {
+ // update the exclusive group id memver list
+ FrontendResource fe = getFrontendResource(removingId.intValue());
+ fe.removeExclusiveGroupMemberFeId(new Integer(fe.getId()));
+ for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) {
+ getFrontendResource(excGroupmemberFeId.intValue())
+ .removeExclusiveGroupMemberFeId(new Integer(fe.getId()));
+ }
+ mFrontendResources.remove(removingId.intValue());
+ mAvailableFrontendIds.remove(removingId);
+ }
+ }
+
+ @VisibleForTesting
+ protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendId)
+ throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "requestFrontend(request=" + request + ")");
+ }
+
+ frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID;
+ if (!checkClientExists(request.getClientId())) {
+ Slog.e(TAG, "Request frontend from unregistered client:" + request.getClientId());
+ return false;
+ }
+ ClientProfile requestClient = getClientProfile(request.getClientId());
+ int grantingFrontendId = -1;
+ int inUseLowestPriorityFrId = -1;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ for (int id : mAvailableFrontendIds) {
+ FrontendResource fr = getFrontendResource(id);
+ if (fr.getType() == request.getFrontendType()) {
+ if (!fr.isInUse()) {
+ // Grant unused frontend with no exclusive group members first.
+ if (fr.getExclusiveGroupMemberFeIds().size() == 0) {
+ grantingFrontendId = id;
+ break;
+ } else if (grantingFrontendId < 0) {
+ // Grant the unused frontend with lower id first if all the unused
+ // frontends have exclusive group members.
+ grantingFrontendId = id;
+ }
+ } else if (grantingFrontendId < 0) {
+ // Record the frontend id with the lowest client priority among all the
+ // in use frontends when no available frontend has been found.
+ int priority = getOwnerClientPriority(id);
+ if (currentLowestPriority > priority) {
+ inUseLowestPriorityFrId = id;
+ currentLowestPriority = priority;
+ }
+ }
+ }
+ }
+
+ // Grant frontend when there is unused resource.
+ if (grantingFrontendId > -1) {
+ frontendId[0] = grantingFrontendId;
+ updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId());
+ return true;
+ }
+
+ // When all the resources are occupied, grant the lowest priority resource if the
+ // request client has higher priority.
+ if (inUseLowestPriorityFrId > -1 && (requestClient.getPriority() > currentLowestPriority)) {
+ frontendId[0] = inUseLowestPriorityFrId;
+ reclaimFrontendResource(getFrontendResource(frontendId[0]).getOwnerClientId());
+ updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId());
+ return true;
+ }
+
+ return false;
+ }
+
+ @VisibleForTesting
+ protected int getClientPriority(int useCase, int pid) {
+ if (DEBUG) {
+ Slog.d(TAG, "getClientPriority useCase=" + useCase
+ + ", pid=" + pid + ")");
+ }
+
+ if (isForeground(pid)) {
+ return mPriorityCongfig.getForegroundPriority(useCase);
+ }
+ return mPriorityCongfig.getBackgroundPriority(useCase);
+ }
+
+ @VisibleForTesting
+ protected boolean isForeground(int pid) {
+ // TODO: how to get fg/bg information from pid
+ return true;
+ }
+
+ @VisibleForTesting
+ protected void reclaimFrontendResource(int reclaimingId) throws RemoteException {
+ if (mListeners.get(reclaimingId) != null) {
+ try {
+ mListeners.get(reclaimingId).onReclaimResources();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private void updateFrontendClientMappingOnNewGrant(int grantingId, int ownerClientId) {
+ FrontendResource grantingFrontend = getFrontendResource(grantingId);
+ ClientProfile ownerProfile = getClientProfile(ownerClientId);
+ grantingFrontend.setOwner(ownerClientId);
+ ownerProfile.useFrontend(grantingId);
+ for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeIds()) {
+ getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId);
+ ownerProfile.useFrontend(exclusiveGroupMember);
+ }
+ }
+
+ /**
+ * Get the owner client's priority from the frontend id.
+ *
+ * @param frontendId an in use frontend id.
+ * @return the priority of the owner client of the frontend.
+ */
+ private int getOwnerClientPriority(int frontendId) {
+ return getClientProfile(getFrontendResource(frontendId).getOwnerClientId()).getPriority();
+ }
+
+ private ClientProfile getClientProfile(int clientId) {
+ return mClientProfiles.get(clientId);
+ }
+
+ protected FrontendResource getFrontendResource(int frontendId) {
+ return mFrontendResources.get(frontendId);
+ }
+
+ @VisibleForTesting
+ protected SparseArray<ClientProfile> getClientProfiles() {
+ return mClientProfiles;
+ }
+
+ @VisibleForTesting
+ protected SparseArray<FrontendResource> getFrontendResources() {
+ return mFrontendResources;
+ }
+
+ private boolean checkClientExists(int clientId) {
+ return mRegisteredClientIds.contains(clientId);
+ }
+
+ private void enforceAccessPermission() {
+ getContext().enforceCallingOrSelfPermission(
+ "android.permission.TUNER_RESOURCE_ACCESS", TAG);
+ }
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
new file mode 100644
index 0000000..8c2de47
--- /dev/null
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 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.tv.tunerresourcemanager;
+
+import android.media.tv.TvInputService;
+import android.media.tv.TvInputService.PriorityHintUseCaseType;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the Tuner Resource Manager use case priority hints config info including a
+ * parser that can read the xml config from the vendors.
+ *
+ * @hide
+ */
+public class UseCasePriorityHints {
+ private static final String TAG = "UseCasePriorityHints";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final String PATH_TO_VENDOR_CONFIG_XML =
+ "/vendor/etc/tunerResourceManagerUseCaseConfig.xml";
+ private static final int INVALID_PRIORITY_VALUE = -1;
+ private static final int INVALID_USE_CASE = -1;
+
+ /**
+ * Array of the configured use case priority hints. Key is the use case id. Value is a size 2
+ * int array. The first element carries the priority of the use case on foreground. The second
+ * shows the background priority.
+ */
+ SparseArray<int[]> mPriorityHints = new SparseArray<>();
+
+ List<Integer> mVendorDefinedUseCase = new ArrayList<>();
+
+ private int mDefaultForeground = 150;
+ private int mDefaultBackground = 50;
+
+ int getForegroundPriority(int useCase) {
+ if (mPriorityHints.get(useCase) != null && mPriorityHints.get(useCase).length == 2) {
+ return mPriorityHints.get(useCase)[0];
+ }
+ return mDefaultForeground;
+ }
+
+ int getBackgroundPriority(int useCase) {
+ if (mPriorityHints.get(useCase) != null && mPriorityHints.get(useCase).length == 2) {
+ return mPriorityHints.get(useCase)[1];
+ }
+ return mDefaultBackground;
+ }
+
+ boolean isDefinedUseCase(int useCase) {
+ return (mVendorDefinedUseCase.contains(useCase) || isPredefinedUseCase(useCase));
+ }
+
+ /**
+ * To parse the vendor use case config.
+ */
+ public void parse() {
+ // Override the default priority with vendor setting if available.
+ File file = new File(PATH_TO_VENDOR_CONFIG_XML);
+ if (file.exists()) {
+ try {
+ InputStream in = new FileInputStream(file);
+ parseInternal(in);
+ return;
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading vendor file: " + file, e);
+ } catch (XmlPullParserException e) {
+ Slog.e(TAG, "Unable to parse vendor file: " + file, e);
+ }
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "no vendor priority configuration available. Using default priority");
+ }
+ addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND, 180, 100);
+ addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN, 450, 200);
+ addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 480, 300);
+ addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 490, 400);
+ addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 600, 500);
+ }
+ }
+
+ // We don't use namespaces
+ private static final String NS = null;
+
+ @VisibleForTesting
+ protected void parseInternal(InputStream in)
+ throws IOException, XmlPullParserException {
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+ parser.setInput(in, null);
+ parser.nextTag();
+ readUseCase(parser);
+ in.close();
+ } catch (IOException | XmlPullParserException e) {
+ throw e;
+ }
+ for (int i = 0; i < mPriorityHints.size(); i++) {
+ int useCase = mPriorityHints.keyAt(i);
+ int[] priorities = mPriorityHints.get(useCase);
+ if (DEBUG) {
+ Slog.d(TAG, "{defaultFg=" + mDefaultForeground
+ + ", defaultBg=" + mDefaultBackground + "}");
+ Slog.d(TAG, "{useCase=" + useCase
+ + ", fg=" + priorities[0]
+ + ", bg=" + priorities[1]
+ + "}");
+ }
+ }
+ }
+
+ private void readUseCase(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ parser.require(XmlPullParser.START_TAG, NS, "config");
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+ String name = parser.getName();
+ int useCase;
+ if (name.equals("useCaseDefault")) {
+ mDefaultForeground = readAttributeToInt("fgPriority", parser);
+ mDefaultBackground = readAttributeToInt("bgPriority", parser);
+ parser.nextTag();
+ parser.require(XmlPullParser.END_TAG, NS, name);
+ } else if (name.equals("useCasePreDefined")) {
+ useCase = formatTypeToNum("type", parser);
+ if (useCase == INVALID_USE_CASE) {
+ Slog.e(TAG, "Wrong predefined use case name given in the vendor config.");
+ continue;
+ }
+ addNewUseCasePriority(useCase,
+ readAttributeToInt("fgPriority", parser),
+ readAttributeToInt("bgPriority", parser));
+ parser.nextTag();
+ parser.require(XmlPullParser.END_TAG, NS, name);
+ } else if (name.equals("useCaseVendor")) {
+ useCase = readAttributeToInt("id", parser);
+ addNewUseCasePriority(useCase,
+ readAttributeToInt("fgPriority", parser),
+ readAttributeToInt("bgPriority", parser));
+ mVendorDefinedUseCase.add(useCase);
+ parser.nextTag();
+ parser.require(XmlPullParser.END_TAG, NS, name);
+ } else {
+ skip(parser);
+ }
+ }
+ }
+
+ private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ throw new IllegalStateException();
+ }
+ int depth = 1;
+ while (depth != 0) {
+ switch (parser.next()) {
+ case XmlPullParser.END_TAG:
+ depth--;
+ break;
+ case XmlPullParser.START_TAG:
+ depth++;
+ break;
+ }
+ }
+ }
+
+ private int readAttributeToInt(String attributeName, XmlPullParser parser) {
+ return Integer.valueOf(parser.getAttributeValue(null, attributeName));
+ }
+
+ private void addNewUseCasePriority(int useCase, int fgPriority, int bgPriority) {
+ int[] priorities = {fgPriority, bgPriority};
+ mPriorityHints.append(useCase, priorities);
+ }
+
+ @PriorityHintUseCaseType
+ private static int formatTypeToNum(String attributeName, XmlPullParser parser) {
+ String useCaseName = parser.getAttributeValue(null, attributeName);
+ switch (useCaseName) {
+ case "USE_CASE_BACKGROUND":
+ return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND;
+ case "USE_CASE_SCAN":
+ return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN;
+ case "USE_CASE_PLAYBACK":
+ return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK;
+ case "USE_CASE_LIVE":
+ return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE;
+ case "USE_CASE_RECORD":
+ return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD;
+ default:
+ return INVALID_USE_CASE;
+ }
+ }
+
+ private static boolean isPredefinedUseCase(int useCase) {
+ switch (useCase) {
+ case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND:
+ case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN:
+ case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK:
+ case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE:
+ case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD:
+ return true;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index e3b7c0a..fe34e86 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -739,6 +739,8 @@
final UriPermission perm = findOrCreateUriPermission(
pi.packageName, targetPkg, targetUid, grantUri);
perm.grantModes(modeFlags, owner);
+ getPmInternal().grantImplicitAccess(UserHandle.getUserId(targetUid), null,
+ UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false /*direct*/);
}
/** Like grantUriPermissionUnchecked, but takes an Intent. */
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 2115f7c..6eb3c0f 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1173,6 +1173,10 @@
}
};
+ private Runnable mTryToRebindRunnable = () -> {
+ tryToRebind();
+ };
+
WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
mInfo = info;
mWallpaper = wallpaper;
@@ -1279,7 +1283,7 @@
saveSettingsLocked(mWallpaper.userId);
}
FgThread.getHandler().removeCallbacks(mResetRunnable);
- mContext.getMainThreadHandler().removeCallbacks(this::tryToRebind);
+ mContext.getMainThreadHandler().removeCallbacks(mTryToRebindRunnable);
}
}
}
@@ -1337,7 +1341,7 @@
< WALLPAPER_RECONNECT_TIMEOUT_MS) {
// Bind fail without timeout, schedule rebind
Slog.w(TAG, "Rebind fail! Try again later");
- mContext.getMainThreadHandler().postDelayed(this::tryToRebind, 1000);
+ mContext.getMainThreadHandler().postDelayed(mTryToRebindRunnable, 1000);
} else {
// Timeout
Slog.w(TAG, "Reverting to built-in wallpaper!");
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 8130546..9bbeb72 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -211,8 +211,9 @@
PackageManagerInternal.class);
final int webviewUid = pmInternal.getPackageUidInternal(
webViewPackageName, 0, UserHandle.getUserId(callingUid));
- pmInternal.grantImplicitAccess(UserHandle.getUserId(callingUid), null, webviewUid,
- UserHandle.getAppId(callingUid));
+ pmInternal.grantImplicitAccess(UserHandle.getUserId(callingUid), null,
+ UserHandle.getAppId(callingUid), webviewUid,
+ true /*direct*/);
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 68a7188..4fea36c 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -275,8 +275,9 @@
}
/** @return {@code true} if the activity matches a launched activity in this transition. */
- boolean contains(ActivityRecord r) {
- return r == mLastLaunchedActivity || mPendingDrawActivities.contains(r);
+ boolean contains(WindowContainer wc) {
+ final ActivityRecord r = AppTransitionController.getAppFromContainer(wc);
+ return r != null && (r == mLastLaunchedActivity || mPendingDrawActivities.contains(r));
}
/** Called when the activity is drawn or won't be drawn. */
@@ -435,10 +436,10 @@
/** @return Non-null {@link TransitionInfo} if the activity is found in an active transition. */
@Nullable
- private TransitionInfo getActiveTransitionInfo(ActivityRecord r) {
+ private TransitionInfo getActiveTransitionInfo(WindowContainer wc) {
for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
final TransitionInfo info = mTransitionInfoList.get(i);
- if (info.contains(r)) {
+ if (info.contains(wc)) {
return info;
}
}
@@ -623,19 +624,19 @@
* @param activityToReason A map from activity to a reason integer, which must be on of
* ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
*/
- void notifyTransitionStarting(ArrayMap<ActivityRecord, Integer> activityToReason) {
+ void notifyTransitionStarting(ArrayMap<WindowContainer, Integer> activityToReason) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
final long timestampNs = SystemClock.elapsedRealtimeNanos();
for (int index = activityToReason.size() - 1; index >= 0; index--) {
- final ActivityRecord r = activityToReason.keyAt(index);
- final TransitionInfo info = getActiveTransitionInfo(r);
+ final WindowContainer wc = activityToReason.keyAt(index);
+ final TransitionInfo info = getActiveTransitionInfo(wc);
if (info == null || info.mLoggedTransitionStarting) {
// Ignore any subsequent notifyTransitionStarting.
continue;
}
if (DEBUG_METRICS) {
- Slog.i(TAG, "notifyTransitionStarting activity=" + r + " info=" + info);
+ Slog.i(TAG, "notifyTransitionStarting activity=" + wc + " info=" + info);
}
info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 190af7a..76bc366 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -40,7 +40,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.activityTypeToString;
@@ -106,7 +105,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_UNSET;
@@ -284,7 +282,6 @@
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
import android.view.InputApplicationHandle;
-import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -577,12 +574,6 @@
*/
private boolean mCurrentLaunchCanTurnScreenOn = true;
- /**
- * This leash is used to "freeze" the app surface in place after the state change, but before
- * the animation is ready to start.
- */
- private SurfaceControl mTransitChangeLeash = null;
-
/** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */
private boolean mLastSurfaceShowing = true;
@@ -1329,15 +1320,6 @@
mDisplayContent.executeAppTransition();
}
- if (prevDc.mChangingApps.remove(this)) {
- // This gets called *after* the ActivityRecord has been reparented to the new display.
- // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN),
- // so this token is now "frozen" while waiting for the animation to start on prevDc
- // (which will be cancelled since the window is no-longer a child). However, since this
- // is no longer a child of prevDc, this won't be notified of the cancelled animation,
- // so we need to cancel the change transition here.
- clearChangeLeash(getPendingTransaction(), true /* cancel */);
- }
prevDc.mClosingApps.remove(this);
if (prevDc.mFocusedApp == this) {
@@ -3092,7 +3074,7 @@
commitVisibility(false /* visible */, true /* performLayout */);
getDisplayContent().mOpeningApps.remove(this);
- getDisplayContent().mChangingApps.remove(this);
+ getDisplayContent().mChangingContainers.remove(this);
getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
mWmService.mTaskSnapshotController.onAppRemoved(this);
mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
@@ -3845,13 +3827,6 @@
mDisplayContent.setLayoutNeeded();
}
mWmService.mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
-
- // Notify the pinned stack upon all windows drawn. If there was an animation in
- // progress then this signal will resume that animation.
- final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
- if (pinnedStack != null) {
- pinnedStack.onAllWindowsDrawn();
- }
}
}
}
@@ -4002,13 +3977,11 @@
appToken, visible, appTransition, isVisible(), mVisibleRequested,
Debug.getCallers(6));
+ onChildVisibilityRequested(visible);
+
final DisplayContent displayContent = getDisplayContent();
displayContent.mOpeningApps.remove(this);
displayContent.mClosingApps.remove(this);
- if (isInChangeTransition()) {
- clearChangeLeash(getPendingTransaction(), true /* cancel */);
- }
- displayContent.mChangingApps.remove(this);
waitingToShow = false;
mVisibleRequested = visible;
mLastDeferHidingClient = deferHidingClient;
@@ -4205,13 +4178,6 @@
final DisplayContent displayContent = getDisplayContent();
if (!displayContent.mClosingApps.contains(this)
&& !displayContent.mOpeningApps.contains(this)) {
- // The token is not closing nor opening, so even if there is an animation set, that
- // doesn't mean that it goes through the normal app transition cycle so we have
- // to inform the docked controller about visibility change.
- // TODO(multi-display): notify docked divider on all displays where visibility was
- // affected.
- displayContent.getDockedDividerController().notifyAppVisibilityChanged();
-
// Take the screenshot before possibly hiding the WSA, otherwise the screenshot
// will not be taken.
mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
@@ -5812,11 +5778,6 @@
return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
}
- @Override
- boolean isChangingAppTransition() {
- return task != null ? task.isChangingAppTransition() : super.isChangingAppTransition();
- }
-
/**
* Creates a layer to apply crop to an animation.
*/
@@ -5837,84 +5798,19 @@
this, endDeferFinishCallback);
}
- private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
- if (mWmService.mDisableTransitionAnimation
- || !isVisible()
- || getDisplayContent().mAppTransition.isTransitionSet()
- || getSurfaceControl() == null) {
- return false;
- }
- // Only do an animation into and out-of freeform mode for now. Other mode
- // transition animations are currently handled by system-ui.
- return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
- }
-
@Override
boolean isWaitingForTransitionStart() {
final DisplayContent dc = getDisplayContent();
return dc != null && dc.mAppTransition.isTransitionSet()
&& (dc.mOpeningApps.contains(this)
|| dc.mClosingApps.contains(this)
- || dc.mChangingApps.contains(this));
+ || dc.mChangingContainers.contains(this));
}
- /**
- * Initializes a change transition. Because the app is visible already, there is a small period
- * of time where the user can see the app content/window update before the transition starts.
- * To prevent this, we immediately take a snapshot and place the app/snapshot into a leash which
- * "freezes" the location/crop until the transition starts.
- * <p>
- * Here's a walk-through of the process:
- * 1. Create a temporary leash ("interim-change-leash") and reparent the app to it.
- * 2. Set the temporary leash's position/crop to the current state.
- * 3. Create a snapshot and place that at the top of the leash to cover up content changes.
- * 4. Once the transition is ready, it will reparent the app to the animation leash.
- * 5. Detach the interim-change-leash.
- */
- private void initializeChangeTransition(Rect startBounds) {
- mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
- false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
- mDisplayContent.mChangingApps.add(this);
- mTransitStartRect.set(startBounds);
-
- final SurfaceControl.Builder builder = makeAnimationLeash()
- .setParent(getAnimationLeashParent())
- .setName(getSurfaceControl() + " - interim-change-leash");
- mTransitChangeLeash = builder.build();
- Transaction t = getPendingTransaction();
- t.setWindowCrop(mTransitChangeLeash, startBounds.width(), startBounds.height());
- t.setPosition(mTransitChangeLeash, startBounds.left, startBounds.top);
- t.show(mTransitChangeLeash);
- t.reparent(getSurfaceControl(), mTransitChangeLeash);
- onAnimationLeashCreated(t, mTransitChangeLeash);
-
- // Skip creating snapshot if this transition is controlled by a remote animator which
- // doesn't need it.
- ArraySet<Integer> activityTypes = new ArraySet<>();
- activityTypes.add(getActivityType());
- RemoteAnimationAdapter adapter =
- mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
- this, TRANSIT_TASK_CHANGE_WINDOWING_MODE, activityTypes);
- if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
- return;
- }
-
- if (mThumbnail == null && task != null && !hasCommittedReparentToAnimationLeash()) {
- SurfaceControl.ScreenshotGraphicBuffer snapshot =
- mWmService.mTaskSnapshotController.createTaskSnapshot(
- task, 1 /* scaleFraction */);
- if (snapshot != null) {
- mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory, t, this,
- snapshot.getGraphicBuffer(), true /* relative */);
- }
- }
- }
-
- @Override
- public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+ private int getAnimationLayer() {
// The leash is parented to the animation layer. We need to preserve the z-order by using
// the prefix order index, but we boost if necessary.
- int layer = 0;
+ int layer;
if (!inPinnedWindowingMode()) {
layer = getPrefixOrderIndex();
} else {
@@ -5927,21 +5823,17 @@
if (mNeedsZBoost) {
layer += Z_BOOST_BASE;
}
- if (!mNeedsAnimationBoundsLayer) {
- t.setLayer(leash, layer);
- }
+ return layer;
+ }
- final DisplayContent dc = getDisplayContent();
- dc.assignStackOrdering();
+ @Override
+ public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+ t.setLayer(leash, getAnimationLayer());
+ getDisplayContent().assignStackOrdering();
+ }
- if (leash == mTransitChangeLeash) {
- // This is a temporary state so skip any animation notifications
- return;
- } else if (mTransitChangeLeash != null) {
- // unparent mTransitChangeLeash for clean-up
- clearChangeLeash(t, false /* cancel */);
- }
-
+ @Override
+ public void onLeashAnimationStarting(Transaction t, SurfaceControl leash) {
if (mAnimatingActivityRegistry != null) {
mAnimatingActivityRegistry.notifyStarting(this);
}
@@ -5969,7 +5861,8 @@
// surface size has already same as the animating container.
t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
}
- t.setLayer(mAnimationBoundsLayer, layer);
+ t.setLayer(leash, 0);
+ t.setLayer(mAnimationBoundsLayer, getAnimationLayer());
// Reparent leash to animation bounds layer.
t.reparent(leash, mAnimationBoundsLayer);
@@ -6001,10 +5894,6 @@
return mLastSurfaceShowing;
}
- boolean isInChangeTransition() {
- return mTransitChangeLeash != null || AppTransition.isChangeTransit(mTransit);
- }
-
void attachThumbnailAnimation() {
if (!isAnimating(PARENTS)) {
return;
@@ -6141,31 +6030,6 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- /**
- * @param cancel {@code true} if clearing the leash due to cancelling instead of transferring
- * to another leash.
- */
- private void clearChangeLeash(Transaction t, boolean cancel) {
- if (mTransitChangeLeash == null) {
- return;
- }
- if (cancel) {
- clearThumbnail();
- SurfaceControl sc = getSurfaceControl();
- SurfaceControl parentSc = getParentSurfaceControl();
- // Don't reparent if surface is getting destroyed
- if (parentSc != null && sc != null) {
- t.reparent(sc, getParentSurfaceControl());
- }
- }
- t.hide(mTransitChangeLeash);
- t.remove(mTransitChangeLeash);
- mTransitChangeLeash = null;
- if (cancel) {
- onAnimationLeashLost(t);
- }
- }
-
void clearAnimatingFlags() {
boolean wallpaperMightChange = false;
for (int i = mChildren.size() - 1; i >= 0; i--) {
@@ -6181,7 +6045,7 @@
void cancelAnimation() {
cancelAnimationOnly();
clearThumbnail();
- clearChangeLeash(getPendingTransaction(), true /* cancel */);
+ mSurfaceFreezer.unfreeze(getPendingTransaction());
}
/**
@@ -6226,6 +6090,7 @@
mRemoteAnimationDefinition = null;
}
+ @Override
RemoteAnimationDefinition getRemoteAnimationDefinition() {
return mRemoteAnimationDefinition;
}
@@ -6686,8 +6551,6 @@
return;
}
}
- final int prevWinMode = getWindowingMode();
- mTmpPrevBounds.set(getBounds());
super.onConfigurationChanged(newParentConfig);
if (shouldUseSizeCompatMode()) {
@@ -6712,12 +6575,6 @@
}
}
- final int newWinMode = getWindowingMode();
- if ((prevWinMode != newWinMode) && (mDisplayContent != null)
- && shouldStartChangeTransition(prevWinMode, newWinMode)) {
- initializeChangeTransition(mTmpPrevBounds);
- }
-
// Configuration's equality doesn't consider seq so if only seq number changes in resolved
// override configuration. Therefore ConfigurationContainer doesn't change merged override
// configuration, but it's used to push configuration changes so explicitly update that.
@@ -6755,18 +6612,8 @@
// for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
if (pinnedStack == null) return;
- final Rect stackBounds;
- if (pinnedStack.lastAnimatingBoundsWasToFullscreen()) {
- // We are animating the bounds, use the pre-animation bounds to save the snap
- // fraction
- stackBounds = pinnedStack.mPreAnimationBounds;
- } else {
- // We skip the animation if the fullscreen configuration is not compatible, so
- // use the current bounds to calculate the saved snap fraction instead
- // (see PinnedActivityStack.skipResizeAnimation())
- stackBounds = mTmpRect;
- pinnedStack.getBounds(stackBounds);
- }
+ final Rect stackBounds = mTmpRect;
+ pinnedStack.getBounds(stackBounds);
mDisplayContent.mPinnedStackControllerLocked.saveReentryBounds(
mActivityComponent, stackBounds);
}
@@ -7612,6 +7459,7 @@
}
}
+ @Override
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 2f1cc05..598389b 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -17,8 +17,6 @@
package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
@@ -39,16 +37,10 @@
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
@@ -98,17 +90,8 @@
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
-import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.TaskProto.ACTIVITIES;
import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
-import static com.android.server.wm.TaskProto.ADJUSTED_BOUNDS;
-import static com.android.server.wm.TaskProto.ADJUSTED_FOR_IME;
-import static com.android.server.wm.TaskProto.ADJUST_DIVIDER_AMOUNT;
-import static com.android.server.wm.TaskProto.ADJUST_IME_AMOUNT;
import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
@@ -117,7 +100,6 @@
import static com.android.server.wm.TaskProto.DISPLAY_ID;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
-import static com.android.server.wm.TaskProto.MINIMIZE_AMOUNT;
import static com.android.server.wm.TaskProto.MIN_HEIGHT;
import static com.android.server.wm.TaskProto.MIN_WIDTH;
import static com.android.server.wm.TaskProto.ORIG_ACTIVITY;
@@ -172,7 +154,6 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
-import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.ITaskOrganizer;
import android.view.SurfaceControl;
@@ -181,8 +162,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.logging.MetricsLoggerWrapper;
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.internal.policy.DockedDividerUtils;
import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledFunction;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -202,7 +181,7 @@
/**
* State and management of a single stack of activities.
*/
-class ActivityStack extends Task implements BoundsAnimationTarget {
+class ActivityStack extends Task {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_APP = TAG + POSTFIX_APP;
@@ -242,13 +221,6 @@
/** Stack is completely invisible. */
static final int STACK_VISIBILITY_INVISIBLE = 2;
- /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
- * restrict IME adjustment so that a min portion of top stack remains visible.*/
- private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
-
- /** Dimming amount for non-focused stack when stacks are IME-adjusted. */
- private static final float IME_ADJUST_DIM_AMOUNT = 0.25f;
-
enum ActivityState {
INITIALIZING,
STARTED,
@@ -298,28 +270,10 @@
/** For Pinned stack controlling. */
private Rect mTmpToBounds = new Rect();
- /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
- private final Rect mAdjustedBounds = new Rect();
-
- /**
- * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they
- * represent the state when the animation has ended.
- */
- private final Rect mFullyAdjustedImeBounds = new Rect();
-
/** Detach this stack from its display when animation completes. */
// TODO: maybe tie this to WindowContainer#removeChild some how...
private boolean mDeferRemoval;
- private final Rect mTmpAdjustedBounds = new Rect();
- private boolean mAdjustedForIme;
- private boolean mImeGoingAway;
- private WindowState mImeWin;
- private float mMinimizeAmount;
- private float mAdjustImeAmount;
- private float mAdjustDividerAmount;
- private final int mDockedStackMinimizeThickness;
-
// If this is true, we are in the bounds animating mode. The task will be down or upscaled to
// perfectly fit the region it would have been cropped to. We may also avoid certain logic we
// would otherwise apply while resizing, while resizing in the bounds animating mode.
@@ -327,11 +281,8 @@
// Set when an animation has been requested but has not yet started from the UI thread. This is
// cleared when the animation actually starts.
private boolean mBoundsAnimatingRequested = false;
- private boolean mBoundsAnimatingToFullscreen = false;
- private boolean mCancelCurrentBoundsAnimation = false;
private Rect mBoundsAnimationTarget = new Rect();
private Rect mBoundsAnimationSourceHintBounds = new Rect();
- private @BoundsAnimationController.AnimationType int mAnimationType;
Rect mPreAnimationBounds = new Rect();
@@ -650,8 +601,6 @@
_realActivitySuspended, userSetupComplete, minWidth, minHeight, info, _voiceSession,
_voiceInteractor, stack);
- mDockedStackMinimizeThickness = mWmService.mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_minimize_thickness);
EventLogTags.writeWmStackCreated(id);
mHandler = new ActivityStackHandler(mStackSupervisor.mLooper);
mCurrentUser = mAtmService.mAmInternal.getCurrentUserId();
@@ -790,17 +739,6 @@
setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
false /* creating */);
- if (windowingMode == WINDOWING_MODE_PINNED) {
- // This stack will be visible before SystemUI requests PiP animation to start, and if
- // the selected animation type is fade in, this stack will be close first. If the
- // animation does not start early, user may see this full screen window appear again
- // after the closing transition finish, which could cause screen to flicker.
- // Check if animation should start here in advance.
- final BoundsAnimationController controller = getDisplay().mBoundsAnimationController;
- if (controller.isAnimationTypeFadeIn()) {
- setPinnedStackAlpha(0f);
- }
- }
}
/**
@@ -905,6 +843,9 @@
likelyResolvedMode = parent != null ? parent.getWindowingMode()
: WINDOWING_MODE_FULLSCREEN;
}
+ if (currentMode == WINDOWING_MODE_PINNED) {
+ mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned();
+ }
if (sendNonResizeableNotification && likelyResolvedMode != WINDOWING_MODE_FULLSCREEN
&& topActivity != null && !topActivity.noDisplay
&& topActivity.isNonResizableOrForcedResizable(likelyResolvedMode)) {
@@ -1217,8 +1158,8 @@
@Override
boolean isFocusable() {
- return super.isFocusable() && !(inSplitScreenPrimaryWindowingMode()
- && mRootWindowContainer.mIsDockMinimized);
+ // Special check for tile which isn't really in the hierarchy
+ return mTile != null ? mTile.isFocusable() : super.isFocusable();
}
boolean isTopActivityFocusable() {
@@ -3480,91 +3421,6 @@
}
}
- void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, int animationDuration,
- boolean fromFullscreen) {
- if (!inPinnedWindowingMode()) return;
-
- /**
- * TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation.
- * If this PIP Task is controlled by a TaskOrganizer, the animation occurs entirely
- * on the TaskOrganizer side, so we just hand over the leash without doing any animation.
- * We have to be careful to not schedule the enter-pip callback as the TaskOrganizer
- * needs to have flexibility to schedule that at an appropriate point in the animation.
- */
- if (isControlledByTaskOrganizer()) return;
- if (toBounds == null /* toFullscreen */) {
- final Configuration parentConfig = getParent().getConfiguration();
- final ActivityRecord top = topRunningNonOverlayTaskActivity();
- if (top != null && !top.isConfigurationCompatible(parentConfig)) {
- // The final orientation of this activity will change after moving to full screen.
- // Start freezing screen here to prevent showing a temporary full screen window.
- top.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
- dismissPip();
- return;
- }
- }
-
- // Get the from-bounds
- final Rect fromBounds = new Rect();
- getBounds(fromBounds);
-
- // Get non-null fullscreen to-bounds for animating if the bounds are null
- @BoundsAnimationController.SchedulePipModeChangedState int schedulePipModeChangedState =
- NO_PIP_MODE_CHANGED_CALLBACKS;
- final boolean toFullscreen = toBounds == null;
- if (toFullscreen) {
- if (fromFullscreen) {
- throw new IllegalArgumentException("Should not defer scheduling PiP mode"
- + " change on animation to fullscreen.");
- }
- schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
-
- mWmService.getStackBounds(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
- if (!mTmpToBounds.isEmpty()) {
- // If there is a fullscreen bounds, use that
- toBounds = new Rect(mTmpToBounds);
- } else {
- // Otherwise, use the display bounds
- toBounds = new Rect();
- getDisplayContent().getBounds(toBounds);
- }
- } else if (fromFullscreen) {
- schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
- }
-
- setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen);
-
- final Rect finalToBounds = toBounds;
- final @BoundsAnimationController.SchedulePipModeChangedState int
- finalSchedulePipModeChangedState = schedulePipModeChangedState;
- final DisplayContent displayContent = getDisplayContent();
- @BoundsAnimationController.AnimationType int intendedAnimationType =
- displayContent.mBoundsAnimationController.getAnimationType();
- if (intendedAnimationType == FADE_IN) {
- if (fromFullscreen) {
- setPinnedStackAlpha(0f);
- }
- if (toBounds.width() == fromBounds.width()
- && toBounds.height() == fromBounds.height()) {
- intendedAnimationType = BoundsAnimationController.BOUNDS;
- } else if (!fromFullscreen && !toBounds.equals(fromBounds)) {
- // intendedAnimationType may have been reset at the end of RecentsAnimation,
- // force it to BOUNDS type if we know for certain we're animating to
- // a different bounds, especially for expand and collapse of PiP window.
- intendedAnimationType = BoundsAnimationController.BOUNDS;
- }
- }
-
- final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType;
- mCancelCurrentBoundsAnimation = false;
- displayContent.mBoundsAnimationController.getHandler().post(() -> {
- displayContent.mBoundsAnimationController.animateBounds(this, fromBounds,
- finalToBounds, animationDuration, finalSchedulePipModeChangedState,
- fromFullscreen, toFullscreen, animationType);
- });
- }
-
void dismissPip() {
if (!isActivityTypeStandardOrUndefined()) {
throw new IllegalArgumentException(
@@ -3593,62 +3449,10 @@
});
}
- private void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
- boolean forceUpdate) {
- // It is guaranteed that the activities requiring the update will be in the pinned stack at
- // this point (either reparented before the animation into PiP, or before reparenting after
- // the animation out of PiP)
- if (!isAttached()) {
- return;
- }
- final PooledConsumer c = PooledLambda.obtainConsumer(
- ActivityStackSupervisor::updatePictureInPictureMode, mStackSupervisor,
- PooledLambda.__(Task.class), targetStackBounds, forceUpdate);
- forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
- }
-
void prepareFreezingTaskBounds() {
forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */);
}
- /**
- * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from
- * the normal task bounds.
- *
- * @param bounds The adjusted bounds.
- */
- private void setAdjustedBounds(Rect bounds) {
- if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) {
- return;
- }
-
- mAdjustedBounds.set(bounds);
- final boolean adjusted = !mAdjustedBounds.isEmpty();
- Rect insetBounds = null;
- if (adjusted && isAdjustedForMinimizedDockedStack()) {
- insetBounds = getRawBounds();
- } else if (adjusted && mAdjustedForIme) {
- if (mImeGoingAway) {
- insetBounds = getRawBounds();
- } else {
- insetBounds = mFullyAdjustedImeBounds;
- }
- }
-
- if (!matchParentBounds()) {
- final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP;
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::alignToAdjustedBounds,
- PooledLambda.__(Task.class), adjusted ? mAdjustedBounds : getRawBounds(),
- insetBounds, alignBottom);
- forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
- }
-
- mDisplayContent.setLayoutNeeded();
- updateSurfaceBounds();
- }
-
@Override
public int setBounds(Rect bounds) {
// Calling Task#setBounds() for leaf task since this is the a specialization of
@@ -3667,8 +3471,6 @@
final int result = super.setBounds(!inMultiWindowMode() ? null : bounds);
- updateAdjustedBounds();
-
updateSurfaceBounds();
return result;
}
@@ -3690,47 +3492,6 @@
bounds.set(getBounds());
}
- @Override
- public Rect getBounds() {
- // If we're currently adjusting for IME or minimized docked stack, we use the adjusted
- // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked
- // stack is visible since it is already what we want to represent to the rest of the
- // system.
- if (!mAdjustedBounds.isEmpty()) {
- return mAdjustedBounds;
- } else {
- return super.getBounds();
- }
- }
-
- /**
- * Sets the bounds animation target bounds ahead of an animation. This can't currently be done
- * in onAnimationStart() since that is started on the UiThread.
- */
- private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds,
- boolean toFullscreen) {
- if (mAnimationType == BoundsAnimationController.BOUNDS) {
- mBoundsAnimatingRequested = true;
- }
- mBoundsAnimatingToFullscreen = toFullscreen;
- if (destBounds != null) {
- mBoundsAnimationTarget.set(destBounds);
- } else {
- mBoundsAnimationTarget.setEmpty();
- }
- if (sourceHintBounds != null) {
- mBoundsAnimationSourceHintBounds.set(sourceHintBounds);
- } else if (!mBoundsAnimating) {
- // If the bounds are already animating, we don't want to reset the source hint. This is
- // because the source hint is sent when starting the animation from the client that
- // requested to enter pip. Other requests can adjust the pip bounds during an animation,
- // but could accidentally reset the source hint bounds.
- mBoundsAnimationSourceHintBounds.setEmpty();
- }
-
- mPreAnimationBounds.set(getRawBounds());
- }
-
/**
* @return the final bounds for the bounds animation.
*/
@@ -3763,137 +3524,6 @@
}
/**
- * Reset the current animation running on {@link #mBoundsAnimationTarget}.
- *
- * @param destinationBounds the final destination bounds
- */
- void resetCurrentBoundsAnimation(Rect destinationBounds) {
- boolean animating = (mBoundsAnimatingRequested || mBoundsAnimating)
- && !mBoundsAnimationTarget.isEmpty();
-
- // The final boundary is updated while there is an existing boundary animation. Let's
- // cancel this animation to prevent the obsolete animation overwritten updated bounds.
- if (animating && !destinationBounds.equals(mBoundsAnimationTarget)) {
- final BoundsAnimationController controller =
- getDisplayContent().mBoundsAnimationController;
- controller.getHandler().post(() -> controller.cancel(this));
- }
- // Once we've set the bounds based on the rotation of the old bounds in the new
- // orientation, clear the animation target bounds since they are obsolete, and
- // cancel any currently running animations
- mBoundsAnimationTarget.setEmpty();
- mBoundsAnimationSourceHintBounds.setEmpty();
- mCancelCurrentBoundsAnimation = true;
- }
-
- /**
- * Updates the passed-in {@code inOutBounds} based on the current state of the
- * docked controller. This gets run *after* the override configuration is updated, so it's
- * safe to rely on the controller's state in here (though eventually this dependence should
- * be removed).
- *
- * This does NOT modify this TaskStack's configuration. However, it does, for the time-being,
- * update docked controller state.
- *
- * @param parentConfig the parent configuration for reference.
- * @param inOutBounds the bounds to update (both input and output).
- */
- void calculateDockedBoundsForConfigChange(Configuration parentConfig, Rect inOutBounds) {
- final boolean primary =
- getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- repositionSplitScreenStackAfterRotation(parentConfig, primary, inOutBounds);
- final DisplayCutout cutout = mDisplayContent.getDisplayInfo().displayCutout;
- snapDockedStackAfterRotation(parentConfig, cutout, inOutBounds);
- if (primary) {
- final int newDockSide = getDockSide(parentConfig, inOutBounds);
- // Update the dock create mode and clear the dock create bounds, these
- // might change after a rotation and the original values will be invalid.
- mWmService.setDockedStackCreateStateLocked(
- (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
- ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
- : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
- null);
- mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
- }
- }
-
- /**
- * Some primary split screen sides are not allowed by the policy. This method queries the policy
- * and moves the primary stack around if needed.
- *
- * @param parentConfig the configuration of the stack's parent.
- * @param primary true if adjusting the primary docked stack, false for secondary.
- * @param inOutBounds the bounds of the stack to adjust.
- */
- void repositionSplitScreenStackAfterRotation(Configuration parentConfig, boolean primary,
- Rect inOutBounds) {
- final int dockSide = getDockSide(mDisplayContent, parentConfig, inOutBounds);
- final int otherDockSide = DockedDividerUtils.invertDockSide(dockSide);
- final int primaryDockSide = primary ? dockSide : otherDockSide;
- if (mDisplayContent.getDockedDividerController()
- .canPrimaryStackDockTo(primaryDockSide,
- parentConfig.windowConfiguration.getBounds(),
- parentConfig.windowConfiguration.getRotation())) {
- return;
- }
- final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
- switch (otherDockSide) {
- case DOCKED_LEFT:
- int movement = inOutBounds.left;
- inOutBounds.left -= movement;
- inOutBounds.right -= movement;
- break;
- case DOCKED_RIGHT:
- movement = parentBounds.right - inOutBounds.right;
- inOutBounds.left += movement;
- inOutBounds.right += movement;
- break;
- case DOCKED_TOP:
- movement = inOutBounds.top;
- inOutBounds.top -= movement;
- inOutBounds.bottom -= movement;
- break;
- case DOCKED_BOTTOM:
- movement = parentBounds.bottom - inOutBounds.bottom;
- inOutBounds.top += movement;
- inOutBounds.bottom += movement;
- break;
- }
- }
-
- /**
- * Snaps the bounds after rotation to the closest snap target for the docked stack.
- */
- void snapDockedStackAfterRotation(Configuration parentConfig, DisplayCutout displayCutout,
- Rect outBounds) {
-
- // Calculate the current position.
- final int dividerSize = mDisplayContent.getDockedDividerController().getContentWidth();
- final int dockSide = getDockSide(parentConfig, outBounds);
- final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
- dockSide, dividerSize);
- final int displayWidth = parentConfig.windowConfiguration.getBounds().width();
- final int displayHeight = parentConfig.windowConfiguration.getBounds().height();
-
- // Snap the position to a target.
- final int rotation = parentConfig.windowConfiguration.getRotation();
- final int orientation = parentConfig.orientation;
- mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight,
- displayCutout, outBounds);
- final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
- mWmService.mContext.getResources(), displayWidth, displayHeight,
- dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds,
- getDockSide(), isMinimizedDockAndHomeStackResizable());
- final DividerSnapAlgorithm.SnapTarget target =
- algorithm.calculateNonDismissingSnapTarget(dividerPosition);
-
- // Recalculate the bounds based on the position of the target.
- DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
- outBounds, displayWidth, displayHeight,
- dividerSize);
- }
-
- /**
* Put a Task in this stack. Used for adding only.
* When task is added to top of the stack, the entire branch of the hierarchy (including stack
* and display) will be brought to top.
@@ -4083,445 +3713,8 @@
}
}
- /**
- * Determines the stack and task bounds of the other stack when in docked mode. The current task
- * bounds is passed in but depending on the stack, the task and stack must match. Only in
- * minimized mode with resizable launcher, the other stack ignores calculating the stack bounds
- * and uses the task bounds passed in as the stack and task bounds, otherwise the stack bounds
- * is calculated and is also used for its task bounds.
- * If any of the out bounds are empty, it represents default bounds
- *
- * @param currentTempTaskBounds the current task bounds of the other stack
- * @param outStackBounds the calculated stack bounds of the other stack
- * @param outTempTaskBounds the calculated task bounds of the other stack
- */
- void getStackDockedModeBounds(Rect dockedBounds,
- Rect currentTempTaskBounds, Rect outStackBounds, Rect outTempTaskBounds) {
- final Configuration parentConfig = getParent().getConfiguration();
- outTempTaskBounds.setEmpty();
-
- if (dockedBounds == null || dockedBounds.isEmpty()) {
- // Calculate the primary docked bounds.
- final boolean dockedOnTopOrLeft = mWmService.mDockedStackCreateMode
- == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
- getStackDockedModeBounds(parentConfig,
- true /* primary */, outStackBounds, dockedBounds,
- mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
- return;
- }
- final int dockedSide = getDockSide(parentConfig, dockedBounds);
-
- // When the home stack is resizable, should always have the same stack and task bounds
- if (isActivityTypeHome()) {
- final Task homeTask = getTopMostTask();
- if (homeTask == null || homeTask.isResizeable()) {
- // Calculate the home stack bounds when in docked mode and the home stack is
- // resizeable.
- getDisplayContent().mDividerControllerLocked
- .getHomeStackBoundsInDockedMode(parentConfig,
- dockedSide, outStackBounds);
- } else {
- // Home stack isn't resizeable, so don't specify stack bounds.
- outStackBounds.setEmpty();
- }
-
- outTempTaskBounds.set(outStackBounds);
- return;
- }
-
- // When minimized state, the stack bounds for all non-home and docked stack bounds should
- // match the passed task bounds
- if (isMinimizedDockAndHomeStackResizable() && currentTempTaskBounds != null) {
- outStackBounds.set(currentTempTaskBounds);
- return;
- }
-
- if (dockedSide == DOCKED_INVALID) {
- // Not sure how you got here...Only thing we can do is return current bounds.
- Slog.e(TAG_WM, "Failed to get valid docked side for docked stack");
- outStackBounds.set(getRawBounds());
- return;
- }
-
- final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
- getStackDockedModeBounds(parentConfig,
- false /* primary */, outStackBounds, dockedBounds,
- mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
- }
-
- /**
- * Outputs the bounds a stack should be given the presence of a docked stack on the display.
- * @param parentConfig The parent configuration.
- * @param primary {@code true} if getting the primary stack bounds.
- * @param outBounds Output bounds that should be used for the stack.
- * @param dockedBounds Bounds of the docked stack.
- * @param dockDividerWidth We need to know the width of the divider make to the output bounds
- * close to the side of the dock.
- * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
- */
- private void getStackDockedModeBounds(Configuration parentConfig, boolean primary,
- Rect outBounds, Rect dockedBounds, int dockDividerWidth,
- boolean dockOnTopOrLeft) {
- final Rect displayRect = parentConfig.windowConfiguration.getBounds();
- final boolean splitHorizontally = displayRect.width() > displayRect.height();
-
- outBounds.set(displayRect);
- if (primary) {
- if (mWmService.mDockedStackCreateBounds != null) {
- outBounds.set(mWmService.mDockedStackCreateBounds);
- return;
- }
-
- // The initial bounds of the docked stack when it is created about half the screen space
- // and its bounds can be adjusted after that. The bounds of all other stacks are
- // adjusted to occupy whatever screen space the docked stack isn't occupying.
- final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout;
- mDisplayContent.getDisplayPolicy().getStableInsetsLw(
- parentConfig.windowConfiguration.getRotation(),
- displayRect.width(), displayRect.height(), displayCutout, mTmpRect2);
- final int position = new DividerSnapAlgorithm(mWmService.mContext.getResources(),
- displayRect.width(),
- displayRect.height(),
- dockDividerWidth,
- parentConfig.orientation == ORIENTATION_PORTRAIT,
- mTmpRect2).getMiddleTarget().position;
-
- if (dockOnTopOrLeft) {
- if (splitHorizontally) {
- outBounds.right = position;
- } else {
- outBounds.bottom = position;
- }
- } else {
- if (splitHorizontally) {
- outBounds.left = position + dockDividerWidth;
- } else {
- outBounds.top = position + dockDividerWidth;
- }
- }
- return;
- }
-
- // Other stacks occupy whatever space is left by the docked stack.
- if (!dockOnTopOrLeft) {
- if (splitHorizontally) {
- outBounds.right = dockedBounds.left - dockDividerWidth;
- } else {
- outBounds.bottom = dockedBounds.top - dockDividerWidth;
- }
- } else {
- if (splitHorizontally) {
- outBounds.left = dockedBounds.right + dockDividerWidth;
- } else {
- outBounds.top = dockedBounds.bottom + dockDividerWidth;
- }
- }
- DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft);
- }
-
- void resetDockedStackToMiddle() {
- if (!inSplitScreenPrimaryWindowingMode()) {
- throw new IllegalStateException("Not a docked stack=" + this);
- }
-
- mWmService.mDockedStackCreateBounds = null;
-
- final Rect bounds = new Rect();
- final Rect tempBounds = new Rect();
- getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
- bounds, tempBounds);
- mStackSupervisor.resizeDockedStackLocked(bounds, null /* tempTaskBounds */,
- null /* tempTaskInsetBounds */, null /* tempOtherTaskBounds */,
- null /* tempOtherTaskInsetBounds */, false /* preserveWindows */,
- false /* deferResume */);
- }
-
- /**
- * Adjusts the stack bounds if the IME is visible.
- *
- * @param imeWin The IME window.
- * @param keepLastAmount Use {@code true} to keep the last adjusted amount from
- * {@link DockedStackDividerController} for adjusting the stack bounds,
- * Use {@code false} to reset adjusted amount as 0.
- * @see #updateAdjustForIme(float, float, boolean)
- */
- void setAdjustedForIme(WindowState imeWin, boolean keepLastAmount) {
- mImeWin = imeWin;
- mImeGoingAway = false;
- if (!mAdjustedForIme || keepLastAmount) {
- mAdjustedForIme = true;
- DockedStackDividerController controller = getDisplayContent().mDividerControllerLocked;
- final float adjustImeAmount = keepLastAmount ? controller.mLastAnimationProgress : 0f;
- final float adjustDividerAmount = keepLastAmount ? controller.mLastDividerProgress : 0f;
- updateAdjustForIme(adjustImeAmount, adjustDividerAmount, true /* force */);
- }
- }
-
- boolean isAdjustedForIme() {
- return mAdjustedForIme;
- }
-
- boolean isAnimatingForIme() {
- return mImeWin != null && mImeWin.isAnimatingLw();
- }
-
- /**
- * Update the stack's bounds (crop or position) according to the IME window's
- * current position. When IME window is animated, the bottom stack is animated
- * together to track the IME window's current position, and the top stack is
- * cropped as necessary.
- *
- * @return true if a traversal should be performed after the adjustment.
- */
- boolean updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force) {
- if (adjustAmount != mAdjustImeAmount
- || adjustDividerAmount != mAdjustDividerAmount || force) {
- mAdjustImeAmount = adjustAmount;
- mAdjustDividerAmount = adjustDividerAmount;
- updateAdjustedBounds();
- return isVisible();
- } else {
- return false;
- }
- }
-
- /**
- * Resets the adjustment after it got adjusted for the IME.
- * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about
- * animations; otherwise, set flag and animates the window away together
- * with IME window.
- */
- void resetAdjustedForIme(boolean adjustBoundsNow) {
- if (adjustBoundsNow) {
- mImeWin = null;
- mImeGoingAway = false;
- mAdjustImeAmount = 0f;
- mAdjustDividerAmount = 0f;
- if (!mAdjustedForIme) {
- return;
- }
- mAdjustedForIme = false;
- updateAdjustedBounds();
- mWmService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
- } else {
- mImeGoingAway |= mAdjustedForIme;
- }
- }
-
- /**
- * Sets the amount how much we currently minimize our stack.
- *
- * @param minimizeAmount The amount, between 0 and 1.
- * @return Whether the amount has changed and a layout is needed.
- */
- boolean setAdjustedForMinimizedDock(float minimizeAmount) {
- if (minimizeAmount != mMinimizeAmount) {
- mMinimizeAmount = minimizeAmount;
- updateAdjustedBounds();
- return isVisible();
- } else {
- return false;
- }
- }
-
boolean shouldIgnoreInput() {
- return isAdjustedForMinimizedDockedStack()
- || (inSplitScreenPrimaryWindowingMode() && isMinimizedDockAndHomeStackResizable());
- }
-
- /**
- * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows
- * to the list of to be drawn windows the service is waiting for.
- */
- void beginImeAdjustAnimation() {
- forAllLeafTasks((t) -> {
- if (t.hasContentToDisplay()) {
- t.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
- t.setWaitingForDrawnIfResizingChanged();
- }
- }, true /* traverseTopToBottom */);
- }
-
- /** Resets the resizing state of all windows. */
- void endImeAdjustAnimation() {
- forAllLeafTasks((t) -> {
- t.setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
- }, true /* traverseTopToBottom */);
- }
-
- private int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
- return displayContentRect.top + (int)
- ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN);
- }
-
- private boolean adjustForIME(final WindowState imeWin) {
- // To prevent task stack resize animation may flicking when playing app transition
- // animation & IME window enter animation in parallel, we need to make sure app
- // transition is done and then adjust task size for IME, skip the new adjusted frame when
- // app transition is still running.
- if (getDisplayContent().mAppTransition.isRunning()) {
- return false;
- }
-
- final int dockedSide = getDockSide();
- final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
- if (imeWin == null || !dockedTopOrBottom) {
- return false;
- }
-
- final Rect displayStableRect = mTmpRect;
- final Rect contentBounds = mTmpRect2;
-
- // Calculate the content bounds excluding the area occupied by IME
- getDisplayContent().getStableRect(displayStableRect);
- contentBounds.set(displayStableRect);
- int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
-
- imeTop += imeWin.getGivenContentInsetsLw().top;
- if (contentBounds.bottom > imeTop) {
- contentBounds.bottom = imeTop;
- }
-
- final int yOffset = displayStableRect.bottom - contentBounds.bottom;
-
- final int dividerWidth =
- getDisplayContent().mDividerControllerLocked.getContentWidth();
- final int dividerWidthInactive =
- getDisplayContent().mDividerControllerLocked.getContentWidthInactive();
-
- if (dockedSide == DOCKED_TOP) {
- // If this stack is docked on top, we make it smaller so the bottom stack is not
- // occluded by IME. We shift its bottom up by the height of the IME, but
- // leaves at least 30% of the top stack visible.
- final int minTopStackBottom =
- getMinTopStackBottom(displayStableRect, getRawBounds().bottom);
- final int bottom = Math.max(
- getRawBounds().bottom - yOffset + dividerWidth - dividerWidthInactive,
- minTopStackBottom);
- mTmpAdjustedBounds.set(getRawBounds());
- mTmpAdjustedBounds.bottom = (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount)
- * getRawBounds().bottom);
- mFullyAdjustedImeBounds.set(getRawBounds());
- } else {
- // When the stack is on bottom and has no focus, it's only adjusted for divider width.
- final int dividerWidthDelta = dividerWidthInactive - dividerWidth;
-
- // When the stack is on bottom and has focus, it needs to be moved up so as to
- // not occluded by IME, and at the same time adjusted for divider width.
- // We try to move it up by the height of the IME window, but only to the extent
- // that leaves at least 30% of the top stack visible.
- // 'top' is where the top of bottom stack will move to in this case.
- final int topBeforeImeAdjust =
- getRawBounds().top - dividerWidth + dividerWidthInactive;
- final int minTopStackBottom =
- getMinTopStackBottom(displayStableRect,
- getRawBounds().top - dividerWidth);
- final int top = Math.max(
- getRawBounds().top - yOffset, minTopStackBottom + dividerWidthInactive);
-
- mTmpAdjustedBounds.set(getRawBounds());
- // Account for the adjustment for IME and divider width separately.
- // (top - topBeforeImeAdjust) is the amount of movement due to IME only,
- // and dividerWidthDelta is due to divider width change only.
- mTmpAdjustedBounds.top =
- getRawBounds().top + (int) (mAdjustImeAmount * (top - topBeforeImeAdjust)
- + mAdjustDividerAmount * dividerWidthDelta);
- mFullyAdjustedImeBounds.set(getRawBounds());
- mFullyAdjustedImeBounds.top = top;
- mFullyAdjustedImeBounds.bottom = top + getRawBounds().height();
- }
- return true;
- }
-
- private boolean adjustForMinimizedDockedStack(float minimizeAmount) {
- final int dockSide = getDockSide();
- if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) {
- return false;
- }
-
- if (dockSide == DOCKED_TOP) {
- mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
- int topInset = mTmpRect.top;
- mTmpAdjustedBounds.set(getRawBounds());
- mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount)
- * getRawBounds().bottom);
- } else if (dockSide == DOCKED_LEFT) {
- mTmpAdjustedBounds.set(getRawBounds());
- final int width = getRawBounds().width();
- mTmpAdjustedBounds.right =
- (int) (minimizeAmount * mDockedStackMinimizeThickness
- + (1 - minimizeAmount) * getRawBounds().right);
- mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width;
- } else if (dockSide == DOCKED_RIGHT) {
- mTmpAdjustedBounds.set(getRawBounds());
- mTmpAdjustedBounds.left =
- (int) (minimizeAmount * (getRawBounds().right - mDockedStackMinimizeThickness)
- + (1 - minimizeAmount) * getRawBounds().left);
- }
- return true;
- }
-
- boolean isMinimizedDockAndHomeStackResizable() {
- return mDisplayContent.mDividerControllerLocked.isMinimizedDock()
- && mDisplayContent.mDividerControllerLocked.isHomeStackResizable();
- }
-
- /**
- * @return the distance in pixels how much the stack gets minimized from it's original size
- */
- int getMinimizeDistance() {
- final int dockSide = getDockSide();
- if (dockSide == DOCKED_INVALID) {
- return 0;
- }
-
- if (dockSide == DOCKED_TOP) {
- mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
- int topInset = mTmpRect.top;
- return getRawBounds().bottom - topInset;
- } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
- return getRawBounds().width() - mDockedStackMinimizeThickness;
- } else {
- return 0;
- }
- }
-
- /**
- * Updates the adjustment depending on it's current state.
- */
- private void updateAdjustedBounds() {
- boolean adjust = false;
- if (mMinimizeAmount != 0f) {
- adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
- } else if (mAdjustedForIme) {
- adjust = adjustForIME(mImeWin);
- }
- if (!adjust) {
- mTmpAdjustedBounds.setEmpty();
- }
- setAdjustedBounds(mTmpAdjustedBounds);
-
- final boolean isImeTarget = (mWmService.getImeFocusStackLocked() == this);
- if (mAdjustedForIme && adjust && !isImeTarget) {
- final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount)
- * IME_ADJUST_DIM_AMOUNT;
- mWmService.setResizeDimLayer(true, getWindowingMode(), alpha);
- }
- }
-
- void applyAdjustForImeIfNeeded(Task task) {
- if (mMinimizeAmount != 0f || !mAdjustedForIme || mAdjustedBounds.isEmpty()) {
- return;
- }
-
- final Rect insetBounds = mImeGoingAway ? getRawBounds() : mFullyAdjustedImeBounds;
- task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP);
- mDisplayContent.setLayoutNeeded();
- }
-
-
- boolean isAdjustedForMinimizedDockedStack() {
- return mMinimizeAmount != 0f;
+ return inSplitScreenPrimaryWindowingMode() && !isFocusable();
}
@Override
@@ -4529,17 +3722,6 @@
pw.println(prefix + "mStackId=" + getRootTaskId());
pw.println(prefix + "mDeferRemoval=" + mDeferRemoval);
pw.println(prefix + "mBounds=" + getRawBounds().toShortString());
- if (mMinimizeAmount != 0f) {
- pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount);
- }
- if (mAdjustedForIme) {
- pw.println(prefix + "mAdjustedForIme=true");
- pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount);
- pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount);
- }
- if (!mAdjustedBounds.isEmpty()) {
- pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
- }
for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
mChildren.get(taskNdx).dump(pw, prefix + " ", dumpAll);
}
@@ -4558,143 +3740,6 @@
}
/**
- * For docked workspace (or workspace that's side-by-side to the docked), provides
- * information which side of the screen was the dock anchored.
- */
- int getDockSide() {
- return getDockSide(mDisplayContent.getConfiguration(), getRawBounds());
- }
-
- int getDockSideForDisplay(DisplayContent dc) {
- return getDockSide(dc, dc.getConfiguration(), getRawBounds());
- }
-
- int getDockSide(Configuration parentConfig, Rect bounds) {
- if (mDisplayContent == null) {
- return DOCKED_INVALID;
- }
- return getDockSide(mDisplayContent, parentConfig, bounds);
- }
-
- private int getDockSide(DisplayContent dc, Configuration parentConfig, Rect bounds) {
- return dc.getDockedDividerController().getDockSide(bounds,
- parentConfig.windowConfiguration.getBounds(),
- parentConfig.orientation, parentConfig.windowConfiguration.getRotation());
- }
-
- boolean hasTaskForUser(int userId) {
- final PooledPredicate p = PooledLambda.obtainPredicate(
- Task::isTaskForUser, PooledLambda.__(Task.class), userId);
- final Task task = getTask(p);
- p.recycle();
- return task != null;
- }
-
- public boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds) {
- // Hold the lock since this is called from the BoundsAnimator running on the UiThread
- synchronized (mWmService.mGlobalLock) {
- if (mCancelCurrentBoundsAnimation) {
- return false;
- }
- mStackSupervisor.resizePinnedStack(displayedBounds, configBounds);
- }
-
- return true;
- }
-
- void onAllWindowsDrawn() {
- if (!mBoundsAnimating && !mBoundsAnimatingRequested) {
- return;
- }
-
- getDisplayContent().mBoundsAnimationController.onAllWindowsDrawn();
- }
-
- @Override // AnimatesBounds
- public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate,
- @BoundsAnimationController.AnimationType int animationType) {
- // Hold the lock since this is called from the BoundsAnimator running on the UiThread
- synchronized (mWmService.mGlobalLock) {
- if (!isAttached()) {
- // Don't run the animation if the stack is already detached
- return false;
- }
-
- if (animationType == BoundsAnimationController.BOUNDS) {
- mBoundsAnimatingRequested = false;
- mBoundsAnimating = true;
- }
- mAnimationType = animationType;
-
- // If we are changing UI mode, as in the PiP to fullscreen
- // transition, then we need to wait for the window to draw.
- if (schedulePipModeChangedCallback) {
- forAllWindows((w) -> {
- w.mWinAnimator.resetDrawState();
- }, false /* traverseTopToBottom */);
- }
- }
-
- if (inPinnedWindowingMode()) {
- try {
- mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
- } catch (RemoteException e) {
- // I don't believe you...
- }
-
- if ((schedulePipModeChangedCallback || animationType == FADE_IN)) {
- // We need to schedule the PiP mode change before the animation up. It is possible
- // in this case for the animation down to not have been completed, so always
- // force-schedule and update to the client to ensure that it is notified that it
- // is no longer in picture-in-picture mode
- updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate);
- }
- }
- return true;
- }
-
- @Override // AnimatesBounds
- public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
- boolean moveToFullscreen) {
- synchronized (mWmService.mGlobalLock) {
- if (inPinnedWindowingMode()) {
- // Update to the final bounds if requested. This is done here instead of in the
- // bounds animator to allow us to coordinate this after we notify the PiP mode
- // changed
-
- if (schedulePipModeChangedCallback) {
- // We need to schedule the PiP mode change after the animation down, so use the
- // final bounds
- updatePictureInPictureModeForPinnedStackAnimation(mBoundsAnimationTarget,
- false /* forceUpdate */);
- }
-
- if (mAnimationType == BoundsAnimationController.FADE_IN) {
- setPinnedStackAlpha(1f);
- mWmService.mAtmService.notifyPinnedStackAnimationEnded();
- return;
- }
-
- if (finalStackSize != null && !mCancelCurrentBoundsAnimation) {
- setPinnedStackSize(finalStackSize, null);
- } else {
- // We have been canceled, so the final stack size is null, still run the
- // animation-end logic
- onPipAnimationEndResize();
- }
-
- mWmService.mAtmService.notifyPinnedStackAnimationEnded();
- if (moveToFullscreen) {
- ((ActivityStack) this).dismissPip();
- }
- } else {
- // No PiP animation, just run the normal animation-end logic
- onPipAnimationEndResize();
- }
- }
- }
-
- /**
* Sets the current picture-in-picture aspect ratio.
*/
void setPictureInPictureAspectRatio(float aspectRatio) {
@@ -4741,42 +3786,6 @@
getDisplayContent().getPinnedStackController().setActions(actions);
}
- /** Called immediately prior to resizing the tasks at the end of the pinned stack animation. */
- void onPipAnimationEndResize() {
- mBoundsAnimating = false;
- forAllLeafTasks(Task::clearPreserveNonFloatingState, false /* traverseTopToBottom */);
- mWmService.requestTraversal();
- }
-
- @Override
- public boolean shouldDeferStartOnMoveToFullscreen() {
- synchronized (mWmService.mGlobalLock) {
- if (!isAttached()) {
- // Unnecessary to pause the animation because the stack is detached.
- return false;
- }
-
- // Workaround for the recents animation -- normally we need to wait for the new activity
- // to show before starting the PiP animation, but because we start and show the home
- // activity early for the recents animation prior to the PiP animation starting, there
- // is no subsequent all-drawn signal. In this case, we can skip the pause when the home
- // stack is already visible and drawn.
- final ActivityStack homeStack = mDisplayContent.getRootHomeTask();
- if (homeStack == null) {
- return true;
- }
- final Task homeTask = homeStack.getTopMostTask();
- if (homeTask == null) {
- return true;
- }
- final ActivityRecord homeApp = homeTask.getTopVisibleActivity();
- if (!homeTask.isVisible() || homeApp == null) {
- return true;
- }
- return !homeApp.allDrawn;
- }
- }
-
/**
* @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen
* bounds and we have a deferred PiP mode changed callback set with the animation.
@@ -4798,25 +3807,6 @@
return mBoundsAnimating;
}
- public boolean isAnimatingBounds() {
- return mBoundsAnimating;
- }
-
- public boolean lastAnimatingBoundsWasToFullscreen() {
- return mBoundsAnimatingToFullscreen;
- }
-
- public boolean isAnimatingBoundsToFullscreen() {
- return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen();
- }
-
- public boolean pinnedStackResizeDisallowed() {
- if (mBoundsAnimating && mCancelCurrentBoundsAnimation) {
- return true;
- }
- return false;
- }
-
/** Returns true if a removal action is still being deferred. */
boolean checkCompleteDeferredRemoval() {
if (isAnimating(TRANSITION | CHILDREN)) {
@@ -4843,21 +3833,6 @@
|| activityType == ACTIVITY_TYPE_ASSISTANT;
}
- @Override
- public boolean setPinnedStackAlpha(float alpha) {
- // Hold the lock since this is called from the BoundsAnimator running on the UiThread
- synchronized (mWmService.mGlobalLock) {
- final SurfaceControl sc = getSurfaceControl();
- if (sc == null || !sc.isValid()) {
- // If the stack is already removed, don't bother updating any stack animation
- return false;
- }
- getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha);
- scheduleAnimation();
- return !mCancelCurrentBoundsAnimation;
- }
- }
-
public DisplayInfo getDisplayInfo() {
return mDisplayContent.getDisplayInfo();
}
@@ -4980,16 +3955,11 @@
bounds.dumpDebug(proto, BOUNDS);
}
getOverrideDisplayedBounds().dumpDebug(proto, DISPLAYED_BOUNDS);
- mAdjustedBounds.dumpDebug(proto, ADJUSTED_BOUNDS);
if (mLastNonFullscreenBounds != null) {
mLastNonFullscreenBounds.dumpDebug(proto, LAST_NON_FULLSCREEN_BOUNDS);
}
proto.write(DEFER_REMOVAL, mDeferRemoval);
- proto.write(MINIMIZE_AMOUNT, mMinimizeAmount);
- proto.write(ADJUSTED_FOR_IME, mAdjustedForIme);
- proto.write(ADJUST_IME_AMOUNT, mAdjustImeAmount);
- proto.write(ADJUST_DIVIDER_AMOUNT, mAdjustDividerAmount);
proto.write(ANIMATING_BOUNDS, mBoundsAnimating);
if (mSurfaceControl != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 97b6388..6d7f8fb 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -35,12 +35,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
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;
import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.graphics.Rect.copyOrNull;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
@@ -210,17 +208,6 @@
/** True if the docked stack is currently being resized. */
private boolean mDockedStackResizing;
- /**
- * True if there are pending docked bounds that need to be applied after
- * {@link #mDockedStackResizing} is reset to false.
- */
- private boolean mHasPendingDockedBounds;
- private Rect mPendingDockedBounds;
- private Rect mPendingTempDockedTaskBounds;
- private Rect mPendingTempDockedTaskInsetBounds;
- private Rect mPendingTempOtherTaskBounds;
- private Rect mPendingTempOtherTaskInsetBounds;
-
// Activity actions an app cannot start if it uses a permission which is not granted.
private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
new ArrayMap<>();
@@ -387,15 +374,6 @@
*/
private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
-
- /**
- * If set to {@code false} all calls to resize the docked stack {@link #resizeDockedStackLocked}
- * will be ignored. Useful for the case where the caller is handling resizing of other stack and
- * moving tasks around and doesn't want dock stack to be resized due to an automatic trigger
- * like the docked stack going empty.
- */
- private boolean mAllowDockedStackResize = true;
-
private KeyguardController mKeyguardController;
private PowerManager mPowerManager;
@@ -1541,12 +1519,6 @@
}
otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
}
-
- // Also disable docked stack resizing since we have manually adjusted the
- // size of other stacks above and we don't want to trigger a docked stack
- // resize when we remove task from it below and it is detached from the
- // display because it no longer contains any tasks.
- mAllowDockedStackResize = false;
}
// If we are moving from the pinned stack, then the animation takes care of updating
@@ -1563,7 +1535,6 @@
mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
mRootWindowContainer.resumeFocusedStacksTopActivities();
} finally {
- mAllowDockedStackResize = true;
mService.continueWindowLayout();
}
}
@@ -1580,154 +1551,6 @@
mDockedStackResizing = resizing;
mWindowManager.setDockedStackResizing(resizing);
-
- if (!resizing && mHasPendingDockedBounds) {
- resizeDockedStackLocked(mPendingDockedBounds, mPendingTempDockedTaskBounds,
- mPendingTempDockedTaskInsetBounds, mPendingTempOtherTaskBounds,
- mPendingTempOtherTaskInsetBounds, PRESERVE_WINDOWS);
-
- mHasPendingDockedBounds = false;
- mPendingDockedBounds = null;
- mPendingTempDockedTaskBounds = null;
- mPendingTempDockedTaskInsetBounds = null;
- mPendingTempOtherTaskBounds = null;
- mPendingTempOtherTaskInsetBounds = null;
- }
- }
-
- void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
- Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
- boolean preserveWindows) {
- resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
- tempOtherTaskBounds, tempOtherTaskInsetBounds, preserveWindows,
- false /* deferResume */);
- }
-
- void resizeDockedStackLocked(Rect displayedBounds, Rect tempDockedTaskBounds,
- Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
- boolean preserveWindows, boolean deferResume) {
-
- if (!mAllowDockedStackResize) {
- // Docked stack resize currently disabled.
- return;
- }
-
- final ActivityStack stack =
- mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
- if (stack == null) {
- Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
- return;
- }
-
- if (mDockedStackResizing) {
- mHasPendingDockedBounds = true;
- mPendingDockedBounds = copyOrNull(displayedBounds);
- mPendingTempDockedTaskBounds = copyOrNull(tempDockedTaskBounds);
- mPendingTempDockedTaskInsetBounds = copyOrNull(tempDockedTaskInsetBounds);
- mPendingTempOtherTaskBounds = copyOrNull(tempOtherTaskBounds);
- mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds);
- }
-
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeDockedStack");
- mService.deferWindowLayout();
- try {
- // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
- mAllowDockedStackResize = false;
- ActivityRecord r = stack.topRunningActivity();
- stack.resize(displayedBounds, tempDockedTaskBounds,
- !PRESERVE_WINDOWS, DEFER_RESUME);
-
- // TODO: Checking for isAttached might not be needed as if the user passes in null
- // dockedBounds then they want the docked stack to be dismissed.
- if (stack.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
- || (displayedBounds == null && !stack.isAttached())) {
- // The dock stack either was dismissed or went fullscreen, which is kinda the same.
- // In this case we make all other static stacks fullscreen and move all
- // docked stack tasks to the fullscreen stack.
- moveTasksToFullscreenStackLocked(stack, ON_TOP);
-
- // stack shouldn't contain anymore activities, so nothing to resume.
- r = null;
- } else {
- // Docked stacks occupy a dedicated region on screen so the size of all other
- // static stacks need to be adjusted so they don't overlap with the docked stack.
- // We get the bounds to use from window manager which has been adjusted for any
- // screen controls and is also the same for all stacks.
- final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
- final Rect otherTaskRect = new Rect();
- for (int i = display.getStackCount() - 1; i >= 0; --i) {
- final ActivityStack current = display.getStackAt(i);
- if (!current.inSplitScreenSecondaryWindowingMode()) {
- continue;
- }
- if (!current.affectedBySplitScreenResize()) {
- continue;
- }
- if (mDockedStackResizing && !current.isTopActivityVisible()) {
- // Non-visible stacks get resized once we're done with the resize
- // interaction.
- continue;
- }
- current.getStackDockedModeBounds(displayedBounds,
- tempOtherTaskBounds /* currentTempTaskBounds */,
- tempRect /* outStackBounds */,
- otherTaskRect /* outTempTaskBounds */);
-
- if (tempRect.isEmpty()) {
- // If this scenario is hit, it means something is not working right.
- // Empty/null bounds implies fullscreen. In the event that this stack
- // *should* be fullscreen, its mode should be set explicitly in a form
- // of setWindowingMode so that other parts of the system are updated
- // properly.
- throw new IllegalArgumentException("Trying to set null bounds on a"
- + " non-fullscreen stack");
- }
-
- current.resize(tempRect, tempOtherTaskBounds, preserveWindows, deferResume);
- }
- }
- if (!deferResume) {
- stack.ensureVisibleActivitiesConfiguration(r, preserveWindows);
- }
- } finally {
- mAllowDockedStackResize = true;
- mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
- }
-
- void resizePinnedStack(Rect displayedBounds, Rect inConfigBounds) {
- // TODO(multi-display): The display containing the stack should be passed in.
- final ActivityStack stack =
- mRootWindowContainer.getDefaultDisplay().getRootPinnedTask();
- if (stack == null) {
- Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
- return;
- }
-
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack");
- mService.deferWindowLayout();
- try {
- Rect configBounds = null;
- if (inConfigBounds != null) {
- // Use 0,0 as the position for the inset rect because we are headed for fullscreen.
- configBounds = tempRect;
- configBounds.top = 0;
- configBounds.left = 0;
- configBounds.right = inConfigBounds.width();
- configBounds.bottom = inConfigBounds.height();
- }
- if (displayedBounds != null && inConfigBounds == null) {
- // We have finished the animation into PiP, and are resizing the tasks to match the
- // stack bounds, while layouts are deferred, update any task state as a part of
- // transitioning it from fullscreen into a floating state.
- stack.onPipAnimationEndResize();
- }
- stack.resize(displayedBounds, configBounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
- } finally {
- mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
}
private void removeStackInSurfaceTransaction(ActivityStack stack) {
@@ -2757,9 +2580,6 @@
mService.deferWindowLayout();
try {
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mWindowManager.setDockedStackCreateStateLocked(
- activityOptions.getSplitScreenCreateMode(), null /* initialBounds */);
-
// Defer updating the stack in which recents is until the app transition is done, to
// not run into issues where we still need to draw the task in recents but the
// docked stack is already created.
@@ -2835,24 +2655,6 @@
// the window renders full-screen with the background filling the void. Also only
// call this at the end to make sure that tasks exists on the window manager side.
setResizingDuringAnimation(task);
-
- final DisplayContent display = task.getStack().getDisplay();
- final ActivityStack topSecondaryStack =
- display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- if (topSecondaryStack != null && topSecondaryStack.isActivityTypeHome()) {
- // If the home activity is the top split-screen secondary stack, then the
- // primary split-screen stack is in the minimized mode which means it can't
- // receive input keys, so we should move the focused app to the home app so that
- // window manager can correctly calculate the focus window that can receive
- // input keys.
- display.moveHomeActivityToTop(
- "startActivityFromRecents: homeVisibleInSplitScreen");
-
- // Immediately update the minimized docked stack mode, the upcoming animation
- // for the docked activity (WMS.overridePendingAppTransitionMultiThumbFuture)
- // will do the animation to the target bounds
- mWindowManager.checkSplitScreenMinimizedChanged(false /* animate */);
- }
}
mService.continueWindowLayout();
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 600a125..3210304 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -97,7 +97,6 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -336,6 +335,7 @@
int filterCallingUid;
PendingIntentRecord originatingPendingIntent;
boolean allowBackgroundActivityStart;
+ boolean isDream;
/**
* If set to {@code true}, allows this activity start to look into
@@ -387,6 +387,7 @@
filterCallingUid = UserHandle.USER_NULL;
originatingPendingIntent = null;
allowBackgroundActivityStart = false;
+ isDream = false;
}
/**
@@ -427,6 +428,7 @@
filterCallingUid = request.filterCallingUid;
originatingPendingIntent = request.originatingPendingIntent;
allowBackgroundActivityStart = request.allowBackgroundActivityStart;
+ isDream = request.isDream;
}
/**
@@ -970,7 +972,7 @@
restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
request.originatingPendingIntent, request.allowBackgroundActivityStart,
- intent);
+ request.isDream, intent);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
@@ -1180,13 +1182,18 @@
boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
final String callingPackage, int realCallingUid, int realCallingPid,
WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
- boolean allowBackgroundActivityStart, Intent intent) {
+ boolean allowBackgroundActivityStart, boolean isDream, Intent intent) {
// don't abort for the most important UIDs
final int callingAppId = UserHandle.getAppId(callingUid);
if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
|| callingAppId == Process.NFC_UID) {
return false;
}
+
+ // don't abort if this is the dream activity
+ if (isDream) {
+ return false;
+ }
// don't abort if the callingUid has a visible window or is a persistent system process
final int callingUidProcState = mService.getUidState(callingUid);
final boolean callingUidHasAnyVisibleWindow =
@@ -1558,9 +1565,8 @@
mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
mService.getPackageManagerInternalLocked().grantImplicitAccess(
mStartActivity.mUserId, mIntent,
- mCallingUid,
- UserHandle.getAppId(mStartActivity.info.applicationInfo.uid)
- );
+ UserHandle.getAppId(mStartActivity.info.applicationInfo.uid), mCallingUid,
+ true /*direct*/);
if (newTask) {
EventLogTags.writeWmCreateTask(mStartActivity.mUserId,
mStartActivity.getTask().mTaskId);
@@ -1813,12 +1819,12 @@
*/
private int deliverToCurrentTopIfNeeded(ActivityStack topStack) {
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
- final boolean dontStart = top != null
+ final boolean dontStart = top != null && mStartActivity.resultTo == null
&& top.mActivityComponent.equals(mStartActivity.mActivityComponent)
&& top.mUserId == mStartActivity.mUserId
&& top.attachedToProcess()
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
- || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
+ || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
// This allows home activity to automatically launch on secondary display when
// display added, if home was the top activity on default display, instead of
// sending new intent to the home activity on default display.
@@ -2049,6 +2055,8 @@
&& !isLaunchModeOneOf(LAUNCH_SINGLE_TASK, LAUNCH_SINGLE_INSTANCE)
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
+ sendNewTaskResultRequestIfNeeded();
+
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
@@ -2230,8 +2238,6 @@
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
}
-
- sendNewTaskResultRequestIfNeeded();
}
private void computeSourceStack() {
@@ -2401,7 +2407,6 @@
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
- updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);
if (DEBUG_TASKS) {
Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
@@ -2424,21 +2429,6 @@
mIntentDelivered = true;
}
- @VisibleForTesting
- void updateBounds(Task task, Rect bounds) {
- if (bounds.isEmpty()) {
- return;
- }
-
- final Task rootTask = task.getRootTask();
- if (rootTask != null && rootTask.inPinnedWindowingMode()) {
- mService.animateResizePinnedStack(rootTask.mTaskId, bounds, -1);
- } else {
- // TODO: I don't believe it is possible to reach this else condition anymore...
- task.setBounds(bounds);
- }
- }
-
private void addOrReparentStartingActivity(Task parent, String reason) {
if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
parent.addChild(mStartActivity);
@@ -2686,6 +2676,11 @@
return this;
}
+ ActivityStarter setIsDream(boolean isDream) {
+ mRequest.isDream = isDream;
+ return this;
+ }
+
void dump(PrintWriter pw, String prefix) {
prefix = prefix + " ";
pw.print(prefix);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 510072d..ca856ca 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -169,12 +169,6 @@
public abstract List<IBinder> getTopVisibleActivities();
/**
- * Callback for window manager to let activity manager know that docked stack changes its
- * minimized state.
- */
- public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
-
- /**
* Notify listeners that contents are drawn for the first time on a single task display.
*
* @param displayId An ID of the display on which contents are drawn.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5392257..770dabf 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -31,12 +31,12 @@
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
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;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -212,6 +212,8 @@
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.provider.Settings;
+import android.service.dreams.DreamActivity;
+import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
import android.sysprop.DisplayProperties;
@@ -1231,6 +1233,52 @@
}
@Override
+ public boolean startDreamActivity(Intent intent) {
+ final WindowProcessController process = mProcessMap.getProcess(Binder.getCallingPid());
+ final long origId = Binder.clearCallingIdentity();
+
+ // The dream activity is only called for non-doze dreams.
+ final ComponentName currentDream = LocalServices.getService(DreamManagerInternal.class)
+ .getActiveDreamComponent(/* doze= */ false);
+
+ if (currentDream == null || currentDream.getPackageName() == null
+ || !currentDream.getPackageName().equals(process.mInfo.packageName)) {
+ Slog.e(TAG, "Calling package is not the current dream package. "
+ + "Aborting startDreamActivity...");
+ return false;
+ }
+
+ final ActivityInfo a = new ActivityInfo();
+ a.theme = com.android.internal.R.style.Theme_Dream;
+ a.exported = true;
+ a.name = DreamActivity.class.getName();
+
+
+ a.packageName = process.mInfo.packageName;
+ a.applicationInfo = process.mInfo;
+ a.processName = process.mInfo.processName;
+ a.uiOptions = process.mInfo.uiOptions;
+ a.taskAffinity = "android:" + a.packageName + "/dream";
+ a.enabled = true;
+ a.launchMode = ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+
+ a.persistableMode = ActivityInfo.PERSIST_NEVER;
+ a.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+ a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+
+ try {
+ getActivityStartController().obtainStarter(intent, "dream")
+ .setActivityInfo(a)
+ .setIsDream(true)
+ .execute();
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
@@ -2261,9 +2309,7 @@
@Override
public boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- return setTaskWindowingModeSplitScreenPrimary(taskId,
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
- toTop, ANIMATE, null /* initialBounds */, true /* showRecents */);
+ return setTaskWindowingModeSplitScreenPrimary(taskId, toTop);
}
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
synchronized (mGlobalLock) {
@@ -2403,7 +2449,7 @@
final ActivityStarter starter = getActivityStartController().obtainStarter(
null /* intent */, "moveTaskToFront");
if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1,
- -1, callerApp, null, false, null)) {
+ -1, callerApp, null, false, false, null)) {
if (!isBackgroundActivityStartsEnabled()) {
return;
}
@@ -2633,10 +2679,6 @@
throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
+ taskId + " to stack " + stackId);
}
- if (stack.inSplitScreenPrimaryWindowingMode()) {
- mWindowManager.setDockedStackCreateStateLocked(
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
- }
task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
"moveTaskToStack");
} finally {
@@ -2645,84 +2687,15 @@
}
}
- @Override
- public void animateResizePinnedStack(int stackId, Rect destBounds, int animationDuration) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "animateResizePinnedStack()");
-
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- final ActivityStack stack = mRootWindowContainer.getStack(stackId);
- if (stack == null) {
- Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
- return;
- }
- if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
- throw new IllegalArgumentException("Stack: " + stackId
- + " doesn't support animated resize.");
- }
- stack.animateResizePinnedStack(destBounds, null /* sourceHintBounds */,
- animationDuration, false /* fromFullscreen */);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
- public void offsetPinnedStackBounds(int stackId, Rect compareBounds, int xOffset, int yOffset,
- int animationDuration) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "offsetPinnedStackBounds()");
-
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- if (xOffset == 0 && yOffset == 0) {
- return;
- }
- final ActivityStack stack = mRootWindowContainer.getStack(stackId);
- if (stack == null) {
- Slog.w(TAG, "offsetPinnedStackBounds: stackId " + stackId + " not found.");
- return;
- }
- if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
- throw new IllegalArgumentException("Stack: " + stackId
- + " doesn't support animated resize.");
- }
- final Rect destBounds = new Rect();
- stack.getAnimationOrCurrentBounds(destBounds);
- if (destBounds.isEmpty() || !destBounds.equals(compareBounds)) {
- Slog.w(TAG, "The current stack bounds does not matched! It may be obsolete.");
- return;
- }
- destBounds.offset(xOffset, yOffset);
- stack.animateResizePinnedStack(destBounds, null /* sourceHintBounds */,
- animationDuration, false /* fromFullscreen */);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
/**
* Moves the specified task to the primary-split-screen stack.
*
* @param taskId Id of task to move.
- * @param createMode The mode the primary split screen stack should be created in if it doesn't
- * exist already. See
- * {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
- * and
- * {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
* @param toTop If the task and stack should be moved to the top.
- * @param animate Whether we should play an animation for the moving the task.
- * @param initialBounds If the primary stack gets created, it will use these bounds for the
- * stack. Pass {@code null} to use default bounds.
- * @param showRecents If the recents activity should be shown on the other side of the task
- * going into split-screen mode.
* @return Whether the task was successfully put into splitscreen.
*/
@Override
- public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
- boolean toTop, boolean animate, Rect initialBounds, boolean showRecents) {
+ public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, boolean toTop) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"setTaskWindowingModeSplitScreenPrimary()");
synchronized (mGlobalLock) {
@@ -4016,17 +3989,7 @@
throw new IllegalArgumentException("Stack: " + stack
+ " doesn't support animated resize.");
}
- /**
- * TODO(b/146594635): Remove all PIP animation code from WM
- * once SysUI handles animation. Don't even try to animate TaskOrganized tasks.
- */
- if (animate && !stack.isControlledByTaskOrganizer()) {
- stack.animateResizePinnedStack(null /* destBounds */,
- null /* sourceHintBounds */, animationDuration,
- false /* fromFullscreen */);
- } else {
- stack.dismissPip();
- }
+ stack.dismissPip();
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -4121,15 +4084,10 @@
}
private boolean isInPictureInPictureMode(ActivityRecord r) {
- if (r == null || r.getRootTask() == null || !r.inPinnedWindowingMode()
- || r.getRootTask().isInStackLocked(r) == null) {
- return false;
- }
-
- // If we are animating to fullscreen then we have already dispatched the PIP mode
- // changed, so we should reflect that check here as well.
- final ActivityStack taskStack = r.getRootTask();
- return !taskStack.isAnimatingBoundsToFullscreen();
+ return r != null
+ && r.getRootTask() != null
+ && r.inPinnedWindowingMode()
+ && r.getRootTask().isInStackLocked(r) != null;
}
@Override
@@ -4213,11 +4171,9 @@
// if it is not already expanding to fullscreen. Otherwise, the arguments will
// be used the next time the activity enters PiP
final ActivityStack stack = r.getRootTask();
- if (!stack.isAnimatingBoundsToFullscreen()) {
- stack.setPictureInPictureAspectRatio(
- r.pictureInPictureArgs.getAspectRatio());
- stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
- }
+ stack.setPictureInPictureAspectRatio(
+ r.pictureInPictureArgs.getAspectRatio());
+ stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
}
logPictureInPictureArgs(params);
}
@@ -4300,6 +4256,7 @@
}
}
+ // TODO(b/149338177): remove when CTS no-longer requires it
@Override
public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds,
@@ -4308,9 +4265,42 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds,
- tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds,
- PRESERVE_WINDOWS);
+ final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
+ TaskTile primary = null;
+ TaskTile secondary = null;
+ for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+ final TaskTile t = dc.getStackAt(i).asTile();
+ if (t == null) {
+ continue;
+ }
+ if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ primary = t;
+ } else if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ secondary = t;
+ }
+ }
+ if (primary == null || secondary == null) {
+ return;
+ }
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final Rect primaryRect =
+ tempDockedTaskInsetBounds != null ? tempDockedTaskInsetBounds
+ : (tempDockedTaskBounds != null ? tempDockedTaskBounds
+ : dockedBounds);
+ wct.setBounds(primary.mRemoteToken, primaryRect);
+ Rect otherRect = tempOtherTaskInsetBounds != null ? tempOtherTaskInsetBounds
+ : tempOtherTaskBounds;
+ if (otherRect == null) {
+ // Temporary estimation... again this is just for tests.
+ otherRect = new Rect(secondary.getBounds());
+ if (dc.getBounds().width() > dc.getBounds().height()) {
+ otherRect.left = primaryRect.right + 6;
+ } else {
+ otherRect.top = primaryRect.bottom + 6;
+ }
+ }
+ wct.setBounds(secondary.mRemoteToken, otherRect);
+ mTaskOrganizerController.applyContainerTransaction(wct, null /* organizer */);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -4421,31 +4411,6 @@
.supportsLocalVoiceInteraction();
}
- /** Notifies all listeners when the pinned stack animation starts. */
- @Override
- public void notifyPinnedStackAnimationStarted() {
- mTaskChangeNotificationController.notifyPinnedStackAnimationStarted();
- }
-
- /** Notifies all listeners when the pinned stack animation ends. */
- @Override
- public void notifyPinnedStackAnimationEnded() {
- mTaskChangeNotificationController.notifyPinnedStackAnimationEnded();
- }
-
- @Override
- public void resizePinnedStack(Rect displayedBounds, Rect configBounds) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- mStackSupervisor.resizePinnedStack(displayedBounds, configBounds);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
@Override
public boolean updateConfiguration(Configuration values) {
mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
@@ -5920,8 +5885,8 @@
* Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home
* activities.
*
- * @param preferredPackage Specify a preferred package name, otherwise use secondary home
- * component defined in config_secondaryHomeComponent.
+ * @param preferredPackage Specify a preferred package name, otherwise use the package name
+ * defined in config_secondaryHomePackage.
* @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME}
*/
Intent getSecondaryHomeIntent(String preferredPackage) {
@@ -5929,10 +5894,10 @@
final boolean useSystemProvidedLauncher = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
if (preferredPackage == null || useSystemProvidedLauncher) {
- // Using the component stored in config if no package name or forced.
- final String secondaryHomeComponent = mContext.getResources().getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent));
+ // Using the package name stored in config if no preferred package name or forced.
+ final String secondaryHomePackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_secondaryHomePackage);
+ intent.setPackage(secondaryHomePackage);
} else {
intent.setPackage(preferredPackage);
}
@@ -6188,13 +6153,6 @@
}
@Override
- public void notifyDockedStackMinimizedChanged(boolean minimized) {
- synchronized (mGlobalLock) {
- mRootWindowContainer.setDockedStackMinimized(minimized);
- }
- }
-
- @Override
public int startActivitiesAsPackage(String packageName, @Nullable String featureId,
int userId, Intent[] intents, Bundle bOptions) {
Objects.requireNonNull(intents, "intents");
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 8fa8119..4cce212 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -111,7 +111,7 @@
final ActivityStarter starter = mService.getActivityStartController().obtainStarter(
null /* intent */, "moveToFront");
if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid,
- callingPackage, -1, -1, callerApp, null, false, null)) {
+ callingPackage, -1, -1, callerApp, null, false, false, null)) {
if (!mService.isBackgroundActivityStartsEnabled()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 8cf0881..4916c31 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -446,8 +446,6 @@
? topOpeningAnim.getStatusBarTransitionsStartTime()
: SystemClock.uptimeMillis(),
AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
- mDisplayContent.getDockedDividerController()
- .notifyAppTransitionStarting(openingApps, transit);
if (mRemoteAnimationController != null) {
mRemoteAnimationController.goodToGo();
@@ -2308,14 +2306,14 @@
}
notifyAppTransitionTimeoutLocked();
if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty()
- || !dc.mChangingApps.isEmpty()) {
+ || !dc.mChangingContainers.isEmpty()) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"*** APP TRANSITION TIMEOUT. displayId=%d isTransitionSet()=%b "
+ "mOpeningApps.size()=%d mClosingApps.size()=%d "
+ "mChangingApps.size()=%d",
dc.getDisplayId(), dc.mAppTransition.isTransitionSet(),
dc.mOpeningApps.size(), dc.mClosingApps.size(),
- dc.mChangingApps.size());
+ dc.mChangingContainers.size());
setTimeout();
mService.mWindowPlacerLocked.performSurfacePlacement();
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 3f4e791..0912b2e 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -55,6 +55,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.NonNull;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -86,7 +87,7 @@
private final WallpaperController mWallpaperControllerLocked;
private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
- private final ArrayMap<ActivityRecord, Integer> mTempTransitionReasons = new ArrayMap<>();
+ private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -104,7 +105,8 @@
void handleAppTransitionReady() {
mTempTransitionReasons.clear();
if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
- || !transitionGoodToGo(mDisplayContent.mChangingApps, mTempTransitionReasons)) {
+ || !transitionGoodToGo(mDisplayContent.mChangingContainers,
+ mTempTransitionReasons)) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
@@ -130,17 +132,21 @@
// transition selection depends on wallpaper target visibility.
mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();
}
- appCount = mDisplayContent.mChangingApps.size();
+ appCount = mDisplayContent.mChangingContainers.size();
for (int i = 0; i < appCount; ++i) {
// Clearing for same reason as above.
- mDisplayContent.mChangingApps.valueAtUnchecked(i).clearAnimatingFlags();
+ final ActivityRecord activity = getAppFromContainer(
+ mDisplayContent.mChangingContainers.valueAtUnchecked(i));
+ if (activity != null) {
+ activity.clearAnimatingFlags();
+ }
}
// Adjust wallpaper before we pull the lower/upper target, since pending changes
// (like the clearAnimatingFlags() above) might affect wallpaper target result.
// Or, the opening app window should be a wallpaper target.
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
- mDisplayContent.mOpeningApps, mDisplayContent.mChangingApps);
+ mDisplayContent.mOpeningApps);
// Determine if closing and opening app token sets are wallpaper targets, in which case
// special animations are needed.
@@ -159,7 +165,7 @@
// no need to do an animation. This is the case, for example, when this transition is being
// done behind a dream window.
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
- mDisplayContent.mClosingApps, mDisplayContent.mChangingApps);
+ mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw();
final ActivityRecord animLpActivity = allowAnimations
? findAnimLayoutParamsToken(transit, activityTypes)
@@ -171,14 +177,13 @@
? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */)
: null;
final ActivityRecord topChangingApp = allowAnimations
- ? getTopApp(mDisplayContent.mChangingApps, false /* ignoreHidden */)
+ ? getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */)
: null;
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
- || containsVoiceInteraction(mDisplayContent.mOpeningApps)
- || containsVoiceInteraction(mDisplayContent.mChangingApps);
+ || containsVoiceInteraction(mDisplayContent.mOpeningApps);
final int layoutRedo;
mService.mSurfaceAnimationRunner.deferStartingAnimations();
@@ -206,7 +211,7 @@
mDisplayContent.mOpeningApps.clear();
mDisplayContent.mClosingApps.clear();
- mDisplayContent.mChangingApps.clear();
+ mDisplayContent.mChangingContainers.clear();
mDisplayContent.mUnknownAppVisibilityController.clear();
// This has changed the visibility of windows, so perform
@@ -235,9 +240,9 @@
return mainWindow != null ? mainWindow.mAttrs : null;
}
- RemoteAnimationAdapter getRemoteAnimationOverride(ActivityRecord animLpActivity,
+ RemoteAnimationAdapter getRemoteAnimationOverride(@NonNull WindowContainer container,
@TransitionType int transit, ArraySet<Integer> activityTypes) {
- final RemoteAnimationDefinition definition = animLpActivity.getRemoteAnimationDefinition();
+ final RemoteAnimationDefinition definition = container.getRemoteAnimationDefinition();
if (definition != null) {
final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
if (adapter != null) {
@@ -271,6 +276,11 @@
}
}
+ static ActivityRecord getAppFromContainer(WindowContainer wc) {
+ return wc.asTask() != null ? wc.asTask().getTopNonFinishingActivity()
+ : wc.asActivityRecord();
+ }
+
/**
* @return The window token that determines the animation theme.
*/
@@ -279,14 +289,14 @@
ActivityRecord result;
final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
- final ArraySet<ActivityRecord> changingApps = mDisplayContent.mChangingApps;
+ final ArraySet<WindowContainer> changingApps = mDisplayContent.mChangingContainers;
// Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.getRemoteAnimationDefinition() != null
&& w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
if (result != null) {
- return result;
+ return getAppFromContainer(result);
}
result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.fillsParent() && w.findMainWindow() != null);
@@ -302,7 +312,7 @@
* of apps in {@code array1}, {@code array2}, and {@code array3}.
*/
private static ArraySet<Integer> collectActivityTypes(ArraySet<ActivityRecord> array1,
- ArraySet<ActivityRecord> array2, ArraySet<ActivityRecord> array3) {
+ ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3) {
final ArraySet<Integer> result = new ArraySet<>();
for (int i = array1.size() - 1; i >= 0; i--) {
result.add(array1.valueAt(i).getActivityType());
@@ -317,7 +327,7 @@
}
private static ActivityRecord lookForHighestTokenWithFilter(ArraySet<ActivityRecord> array1,
- ArraySet<ActivityRecord> array2, ArraySet<ActivityRecord> array3,
+ ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3,
Predicate<ActivityRecord> filter) {
final int array2base = array1.size();
final int array3base = array2.size() + array2base;
@@ -325,15 +335,16 @@
int bestPrefixOrderIndex = Integer.MIN_VALUE;
ActivityRecord bestToken = null;
for (int i = 0; i < count; i++) {
- final ActivityRecord wtoken = i < array2base
+ final WindowContainer wtoken = i < array2base
? array1.valueAt(i)
: (i < array3base
? array2.valueAt(i - array2base)
: array3.valueAt(i - array3base));
final int prefixOrderIndex = wtoken.getPrefixOrderIndex();
- if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) {
+ final ActivityRecord r = getAppFromContainer(wtoken);
+ if (r != null && filter.test(r) && prefixOrderIndex > bestPrefixOrderIndex) {
bestPrefixOrderIndex = prefixOrderIndex;
- bestToken = wtoken;
+ bestToken = r;
}
}
return bestToken;
@@ -589,21 +600,13 @@
}
private void handleChangingApps(@TransitionType int transit) {
- final ArraySet<ActivityRecord> apps = mDisplayContent.mChangingApps;
+ final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers;
final int appsCount = apps.size();
for (int i = 0; i < appsCount; i++) {
- ActivityRecord activity = apps.valueAt(i);
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", activity);
- activity.cancelAnimationOnly();
- activity.applyAnimation(null, transit, true, false,
+ WindowContainer wc = apps.valueAt(i);
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", wc);
+ wc.applyAnimation(null, transit, true, false,
null /* animationFinishedCallback */);
- activity.updateReportedVisibilityLocked();
- mService.openSurfaceTransaction();
- try {
- activity.showAllWindowsLocked();
- } finally {
- mService.closeSurfaceTransaction("handleChangingApps");
- }
}
}
@@ -628,8 +631,8 @@
}
}
- private boolean transitionGoodToGo(ArraySet<ActivityRecord> apps,
- ArrayMap<ActivityRecord, Integer> outReasons) {
+ private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps,
+ ArrayMap<WindowContainer, Integer> outReasons) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
@@ -652,13 +655,17 @@
return false;
}
for (int i = 0; i < apps.size(); i++) {
- ActivityRecord activity = apps.valueAt(i);
+ WindowContainer wc = apps.valueAt(i);
+ final ActivityRecord activity = getAppFromContainer(wc);
+ if (activity == null) {
+ continue;
+ }
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
- + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
- activity, activity.allDrawn, activity.startingDisplayed,
- activity.startingMoved, activity.isRelaunching(),
- activity.startingWindow);
+ "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
+ + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
+ activity, activity.allDrawn, activity.startingDisplayed,
+ activity.startingMoved, activity.isRelaunching(),
+ activity.startingWindow);
final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
@@ -838,7 +845,7 @@
@VisibleForTesting
boolean isTransitWithinTask(@TransitionType int transit, Task task) {
if (task == null
- || !mDisplayContent.mChangingApps.isEmpty()) {
+ || !mDisplayContent.mChangingContainers.isEmpty()) {
// if there is no task, then we can't constrain to the task.
// if anything is changing, it can animate outside its task.
return false;
@@ -882,12 +889,13 @@
* {@link ActivityRecord#isVisible}.
* @return The top {@link ActivityRecord}.
*/
- private ActivityRecord getTopApp(ArraySet<ActivityRecord> apps, boolean ignoreInvisible) {
+ private ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps,
+ boolean ignoreInvisible) {
int topPrefixOrderIndex = Integer.MIN_VALUE;
ActivityRecord topApp = null;
for (int i = apps.size() - 1; i >= 0; i--) {
- final ActivityRecord app = apps.valueAt(i);
- if (ignoreInvisible && !app.isVisible()) {
+ final ActivityRecord app = getAppFromContainer(apps.valueAt(i));
+ if (app == null || ignoreInvisible && !app.isVisible()) {
continue;
}
final int prefixOrderIndex = app.getPrefixOrderIndex();
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
deleted file mode 100644
index 5385e2f..0000000
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ /dev/null
@@ -1,584 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.animation.AnimationHandler;
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.annotation.IntDef;
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.view.Choreographer;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Enables animating bounds of objects.
- *
- * In multi-window world bounds of both stack and tasks can change. When we need these bounds to
- * change smoothly and not require the app to relaunch (e.g. because it handles resizes and
- * relaunching it would cause poorer experience), these class provides a way to directly animate
- * the bounds of the resized object.
- *
- * The object that is resized needs to implement {@link BoundsAnimationTarget} interface.
- *
- * NOTE: All calls to methods in this class should be done on the Animation thread
- */
-public class BoundsAnimationController {
- private static final boolean DEBUG_LOCAL = false;
- private static final boolean DEBUG = DEBUG_LOCAL || DEBUG_ANIM;
- private static final String TAG = TAG_WITH_CLASS_NAME || DEBUG_LOCAL
- ? "BoundsAnimationController" : TAG_WM;
- private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1;
-
- private static final int DEFAULT_TRANSITION_DURATION = 425;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({NO_PIP_MODE_CHANGED_CALLBACKS, SCHEDULE_PIP_MODE_CHANGED_ON_START,
- SCHEDULE_PIP_MODE_CHANGED_ON_END})
- public @interface SchedulePipModeChangedState {}
- /** Do not schedule any PiP mode changed callbacks as a part of this animation. */
- public static final int NO_PIP_MODE_CHANGED_CALLBACKS = 0;
- /** Schedule a PiP mode changed callback when this animation starts. */
- public static final int SCHEDULE_PIP_MODE_CHANGED_ON_START = 1;
- /** Schedule a PiP mode changed callback when this animation ends. */
- public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2;
-
- public static final int BOUNDS = 0;
- public static final int FADE_IN = 1;
-
- @IntDef({BOUNDS, FADE_IN}) public @interface AnimationType {}
-
- private static final int FADE_IN_DURATION = 500;
-
- // Only accessed on UI thread.
- private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
-
- private final class AppTransitionNotifier
- extends WindowManagerInternal.AppTransitionListener implements Runnable {
-
- public void onAppTransitionCancelledLocked() {
- if (DEBUG) Slog.d(TAG, "onAppTransitionCancelledLocked:"
- + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition);
- animationFinished();
- }
- public void onAppTransitionFinishedLocked(IBinder token) {
- if (DEBUG) Slog.d(TAG, "onAppTransitionFinishedLocked:"
- + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition);
- animationFinished();
- }
- private void animationFinished() {
- if (mFinishAnimationAfterTransition) {
- mHandler.removeCallbacks(this);
- // This might end up calling into activity manager which will be bad since we have
- // the window manager lock held at this point. Post a message to take care of the
- // processing so we don't deadlock.
- mHandler.post(this);
- }
- }
-
- @Override
- public void run() {
- for (int i = 0; i < mRunningAnimations.size(); i++) {
- final BoundsAnimator b = mRunningAnimations.valueAt(i);
- b.onAnimationEnd(null);
- }
- }
- }
-
- private final Handler mHandler;
- private final AppTransition mAppTransition;
- private final AppTransitionNotifier mAppTransitionNotifier = new AppTransitionNotifier();
- private final Interpolator mFastOutSlowInInterpolator;
- private boolean mFinishAnimationAfterTransition = false;
- private final AnimationHandler mAnimationHandler;
- private Choreographer mChoreographer;
- private @AnimationType int mAnimationType;
-
- private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000;
-
- BoundsAnimationController(Context context, AppTransition transition, Handler handler,
- AnimationHandler animationHandler) {
- mHandler = handler;
- mAppTransition = transition;
- mAppTransition.registerListenerLocked(mAppTransitionNotifier);
- mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_slow_in);
- mAnimationHandler = animationHandler;
- if (animationHandler != null) {
- // If an animation handler is provided, then ensure that it runs on the sf vsync tick
- handler.post(() -> {
- mChoreographer = Choreographer.getSfInstance();
- animationHandler.setProvider(new SfVsyncFrameCallbackProvider(mChoreographer));
- });
- }
- }
-
- @VisibleForTesting
- final class BoundsAnimator extends ValueAnimator
- implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
-
- private final BoundsAnimationTarget mTarget;
- private final @AnimationType int mAnimationType;
- private final Rect mFrom = new Rect();
- private final Rect mTo = new Rect();
- private final Rect mTmpRect = new Rect();
- private final Rect mTmpTaskBounds = new Rect();
-
- // True if this this animation was canceled and will be replaced the another animation from
- // the same {@link #BoundsAnimationTarget} target.
- private boolean mSkipFinalResize;
- // True if this animation was canceled by the user, not as a part of a replacing animation
- private boolean mSkipAnimationEnd;
-
- // True if the animation target is animating from the fullscreen. Only one of
- // {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be true at any time in the
- // animation.
- private boolean mMoveFromFullscreen;
- // True if the animation target should be moved to the fullscreen stack at the end of this
- // animation. Only one of {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be
- // true at any time in the animation.
- private boolean mMoveToFullscreen;
-
- // Whether to schedule PiP mode changes on animation start/end
- private @SchedulePipModeChangedState int mSchedulePipModeChangedState;
- private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState;
-
- // Depending on whether we are animating from
- // a smaller to a larger size
- private int mFrozenTaskWidth;
- private int mFrozenTaskHeight;
-
- // Timeout callback to ensure we continue the animation if waiting for resuming or app
- // windows drawn fails
- private final Runnable mResumeRunnable = () -> {
- if (DEBUG) Slog.d(TAG, "pause: timed out waiting for windows drawn");
- resume();
- };
-
- // If this animator is explicitly cancelled when it's in paused state, we should not
- // attempt to resume the animation. Use this flag to avoid such behavior.
- private boolean mIsCancelled;
-
- BoundsAnimator(BoundsAnimationTarget target, @AnimationType int animationType, Rect from,
- Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState,
- @SchedulePipModeChangedState int prevShedulePipModeChangedState,
- boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask) {
- super();
- mTarget = target;
- mAnimationType = animationType;
- mFrom.set(from);
- mTo.set(to);
- mSchedulePipModeChangedState = schedulePipModeChangedState;
- mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState;
- mMoveFromFullscreen = moveFromFullscreen;
- mMoveToFullscreen = moveToFullscreen;
- addUpdateListener(this);
- addListener(this);
-
- // If we are animating from smaller to larger, we want to change the task bounds
- // to their final size immediately so we can use scaling to make the window
- // larger. Likewise if we are going from bigger to smaller, we want to wait until
- // the end so we don't have to upscale from the smaller finished size.
- if (mAnimationType == BOUNDS) {
- if (animatingToLargerSize()) {
- mFrozenTaskWidth = mTo.width();
- mFrozenTaskHeight = mTo.height();
- } else {
- mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width();
- mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height();
- }
- }
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
- + " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState
- + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
- mIsCancelled = false;
- mFinishAnimationAfterTransition = false;
- mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth,
- mFrom.top + mFrozenTaskHeight);
-
- // Boost the thread priority of the animation thread while the bounds animation is
- // running
- updateBooster();
-
- // Ensure that we have prepared the target for animation before we trigger any size
- // changes, so it can swap surfaces in to appropriate modes, or do as it wishes
- // otherwise.
- boolean continueAnimation;
- if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) {
- continueAnimation = mTarget.onAnimationStart(
- mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START,
- false /* forceUpdate */, mAnimationType);
-
- // When starting an animation from fullscreen, pause here and wait for the
- // windows-drawn signal before we start the rest of the transition down into PiP.
- if (continueAnimation && mMoveFromFullscreen
- && mTarget.shouldDeferStartOnMoveToFullscreen()) {
- pause();
- }
- } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END &&
- mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
- // We are replacing a running animation into PiP, but since it hasn't completed, the
- // client will not currently receive any picture-in-picture mode change callbacks.
- // However, we still need to report to them that they are leaving PiP, so this will
- // force an update via a mode changed callback.
- continueAnimation = mTarget.onAnimationStart(
- true /* schedulePipModeChangedCallback */, true /* forceUpdate */,
- mAnimationType);
- } else {
- // The animation is already running, but we should check that the TaskStack is still
- // valid before continuing with the animation
- continueAnimation = mTarget.isAttached();
- }
-
- if (!continueAnimation) {
- // No point of trying to animate something that isn't attached to the hierarchy
- // anymore.
- cancel();
- return;
- }
-
- // Immediately update the task bounds if they have to become larger, but preserve
- // the starting position so we don't jump at the beginning of the animation.
- if (animatingToLargerSize()) {
- mTarget.setPinnedStackSize(mFrom, mTmpRect);
-
- // We pause the animation until the app has drawn at the new size.
- // The target will notify us via BoundsAnimationController#resume.
- // We do this here and pause the animation, rather than just defer starting it
- // so we can enter the animating state and have WindowStateAnimator apply the
- // correct logic to make this resize seamless.
- if (mMoveToFullscreen) {
- pause();
- }
- }
- }
-
- @Override
- public void pause() {
- if (DEBUG) Slog.d(TAG, "pause: waiting for windows drawn");
- super.pause();
- mHandler.postDelayed(mResumeRunnable, WAIT_FOR_DRAW_TIMEOUT_MS);
- }
-
- @Override
- public void resume() {
- if (DEBUG) Slog.d(TAG, "resume:");
- mHandler.removeCallbacks(mResumeRunnable);
- if (!mIsCancelled) super.resume();
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- final float value = (Float) animation.getAnimatedValue();
- if (mAnimationType == FADE_IN) {
- if (!mTarget.setPinnedStackAlpha(value)) {
- cancelAndCallAnimationEnd();
- }
- return;
- }
-
- final float remains = 1 - value;
- mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f);
- mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f);
- mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value + 0.5f);
- mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value + 0.5f);
- if (DEBUG) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + " mBounds="
- + mTmpRect + " from=" + mFrom + " mTo=" + mTo + " value=" + value
- + " remains=" + remains);
-
- mTmpTaskBounds.set(mTmpRect.left, mTmpRect.top,
- mTmpRect.left + mFrozenTaskWidth, mTmpRect.top + mFrozenTaskHeight);
-
- if (!mTarget.setPinnedStackSize(mTmpRect, mTmpTaskBounds)) {
- // Whoops, the target doesn't feel like animating anymore. Let's immediately finish
- // any further animation.
- if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled");
-
- // If we have already scheduled a PiP mode changed at the start of the animation,
- // then we need to clean up and schedule one at the end, since we have canceled the
- // animation to the final state.
- if (mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
- mSchedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
- }
-
- // Since we are cancelling immediately without a replacement animation, send the
- // animation end to maintain callback parity, but also skip any further resizes
- cancelAndCallAnimationEnd();
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
- + " mSkipFinalResize=" + mSkipFinalResize
- + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition
- + " mAppTransitionIsRunning=" + mAppTransition.isRunning()
- + " callers=" + Debug.getCallers(2));
-
- // There could be another animation running. For example in the
- // move to fullscreen case, recents will also be closing while the
- // previous task will be taking its place in the fullscreen stack.
- // we have to ensure this is completed before we finish the animation
- // and take our place in the fullscreen stack.
- if (mAppTransition.isRunning() && !mFinishAnimationAfterTransition) {
- mFinishAnimationAfterTransition = true;
- return;
- }
-
- if (!mSkipAnimationEnd) {
- // If this animation has already scheduled the picture-in-picture mode on start, and
- // we are not skipping the final resize due to being canceled, then move the PiP to
- // fullscreen once the animation ends
- if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
- + " moveToFullscreen=" + mMoveToFullscreen);
- mTarget.onAnimationEnd(mSchedulePipModeChangedState ==
- SCHEDULE_PIP_MODE_CHANGED_ON_END, !mSkipFinalResize ? mTo : null,
- mMoveToFullscreen);
- }
-
- // Clean up this animation
- removeListener(this);
- removeUpdateListener(this);
- mRunningAnimations.remove(mTarget);
-
- // Reset the thread priority of the animation thread after the bounds animation is done
- updateBooster();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mIsCancelled = true;
- // Always skip the final resize when the animation is canceled
- mSkipFinalResize = true;
- mMoveToFullscreen = false;
- }
-
- private void cancelAndCallAnimationEnd() {
- if (DEBUG) Slog.d(TAG, "cancelAndCallAnimationEnd: mTarget=" + mTarget);
- mSkipAnimationEnd = false;
- super.cancel();
- }
-
- @Override
- public void cancel() {
- if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget);
- mSkipAnimationEnd = true;
- super.cancel();
-
- // Reset the thread priority of the animation thread if the bounds animation is canceled
- updateBooster();
- }
-
- /**
- * @return true if the animation target is the same as the input bounds.
- */
- boolean isAnimatingTo(Rect bounds) {
- return mTo.equals(bounds);
- }
-
- /**
- * @return true if we are animating to a larger surface size
- */
- @VisibleForTesting
- boolean animatingToLargerSize() {
- // TODO: Fix this check for aspect ratio changes
- return (mFrom.width() * mFrom.height() < mTo.width() * mTo.height());
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- // Do nothing
- }
-
- @Override
- public AnimationHandler getAnimationHandler() {
- if (mAnimationHandler != null) {
- return mAnimationHandler;
- }
- return super.getAnimationHandler();
- }
- }
-
- public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to,
- int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
- boolean moveFromFullscreen, boolean moveToFullscreen,
- @AnimationType int animationType) {
- animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState,
- moveFromFullscreen, moveToFullscreen, animationType);
- }
-
- /**
- * Cancel existing animation if the destination was modified.
- */
- void cancel(final BoundsAnimationTarget target) {
- final BoundsAnimator existing = mRunningAnimations.get(target);
- if (existing != null) {
- // Cancel animation. Since its already started, send animation end to client.
- if (DEBUG) Slog.d(TAG, "cancel: mTarget= " + target);
- existing.cancelAndCallAnimationEnd();
- }
- }
-
- @VisibleForTesting
- BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to,
- int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
- boolean moveFromFullscreen, boolean moveToFullscreen,
- @AnimationType int animationType) {
- final BoundsAnimator existing = mRunningAnimations.get(target);
-
- if (isRunningFadeInAnimation(target) && from.width() == to.width()
- && from.height() == to.height()) {
- animationType = FADE_IN;
- }
- final boolean replacing = existing != null;
- @SchedulePipModeChangedState int prevSchedulePipModeChangedState =
- NO_PIP_MODE_CHANGED_CALLBACKS;
-
- if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to
- + " schedulePipModeChangedState=" + schedulePipModeChangedState
- + " replacing=" + replacing);
-
- Rect frozenTask = new Rect();
- if (replacing) {
- if (existing.isAnimatingTo(to) && (!moveToFullscreen || existing.mMoveToFullscreen)
- && (!moveFromFullscreen || existing.mMoveFromFullscreen)) {
- // Just let the current animation complete if it has the same destination as the
- // one we are trying to start, and, if moveTo/FromFullscreen was requested, already
- // has that flag set.
- if (DEBUG) Slog.d(TAG, "animateBounds: same destination and moveTo/From flags as "
- + "existing=" + existing + ", ignoring...");
- return existing;
- }
-
- // Save the previous state
- prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState;
-
- // Update the PiP callback states if we are replacing the animation
- if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
- if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
- if (DEBUG) Slog.d(TAG, "animateBounds: still animating to fullscreen, keep"
- + " existing deferred state");
- } else {
- if (DEBUG) Slog.d(TAG, "animateBounds: fullscreen animation canceled, callback"
- + " on start already processed, schedule deferred update on end");
- schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
- }
- } else if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END) {
- if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
- if (DEBUG) Slog.d(TAG, "animateBounds: non-fullscreen animation canceled,"
- + " callback on start will be processed");
- } else {
- if (DEBUG) Slog.d(TAG, "animateBounds: still animating from fullscreen, keep"
- + " existing deferred state");
- schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
- }
- }
-
- // We need to keep the previous moveTo/FromFullscreen flag, unless the new animation
- // specifies a direction.
- if (!moveFromFullscreen && !moveToFullscreen) {
- moveToFullscreen = existing.mMoveToFullscreen;
- moveFromFullscreen = existing.mMoveFromFullscreen;
- }
-
- // We are in the middle of an existing animation, so that this new animation may
- // start from an interpolated bounds. We should keep using the existing frozen task
- // width/height for consistent configurations.
- frozenTask.set(0, 0, existing.mFrozenTaskWidth, existing.mFrozenTaskHeight);
-
- // Since we are replacing, we skip both animation start and end callbacks
- existing.cancel();
- }
- if (animationType == FADE_IN) {
- target.setPinnedStackSize(to, null);
- }
-
- final BoundsAnimator animator = new BoundsAnimator(target, animationType, from, to,
- schedulePipModeChangedState, prevSchedulePipModeChangedState,
- moveFromFullscreen, moveToFullscreen, frozenTask);
- mRunningAnimations.put(target, animator);
- animator.setFloatValues(0f, 1f);
- animator.setDuration(animationType == FADE_IN ? FADE_IN_DURATION
- : (animationDuration != -1 ? animationDuration : DEFAULT_TRANSITION_DURATION)
- * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
- animator.setInterpolator(mFastOutSlowInInterpolator);
- animator.start();
- return animator;
- }
-
- public void setAnimationType(@AnimationType int animationType) {
- mAnimationType = animationType;
- }
-
- /** return the current animation type. */
- public @AnimationType int getAnimationType() {
- @AnimationType int animationType = mAnimationType;
- // Default to BOUNDS.
- mAnimationType = BOUNDS;
- return animationType;
- }
-
- boolean isAnimationTypeFadeIn() {
- return mAnimationType == FADE_IN;
- }
-
- public Handler getHandler() {
- return mHandler;
- }
-
- public void onAllWindowsDrawn() {
- if (DEBUG) Slog.d(TAG, "onAllWindowsDrawn:");
- mHandler.post(this::resume);
- }
-
- private boolean isRunningFadeInAnimation(final BoundsAnimationTarget target) {
- final BoundsAnimator existing = mRunningAnimations.get(target);
- return existing != null && existing.mAnimationType == FADE_IN && existing.isStarted();
- }
-
- private void resume() {
- for (int i = 0; i < mRunningAnimations.size(); i++) {
- final BoundsAnimator b = mRunningAnimations.valueAt(i);
- b.resume();
- }
- }
-
- private void updateBooster() {
- WindowManagerService.sThreadPriorityBooster.setBoundsAnimationRunning(
- !mRunningAnimations.isEmpty());
- }
-}
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
deleted file mode 100644
index b1d5359..0000000
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.graphics.Rect;
-
-/**
- * The target for a BoundsAnimation.
- * @see BoundsAnimationController
- */
-interface BoundsAnimationTarget {
-
- /**
- * Callback for the target to inform it that the animation has started, so it can do some
- * necessary preparation.
- *
- * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
- * callbacks
- * @return whether to continue the animation
- */
- boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate,
- @BoundsAnimationController.AnimationType int animationType);
-
- /**
- * @return Whether the animation should be paused waiting for the windows to draw before
- * entering PiP.
- */
- boolean shouldDeferStartOnMoveToFullscreen();
-
- /**
- * Sets the size of the target (without any intermediate steps, like scheduling animation)
- * but freezes the bounds of any tasks in the target at taskBounds, to allow for more
- * flexibility during resizing. Only works for the pinned stack at the moment. This will
- * only be called between onAnimationStart() and onAnimationEnd().
- *
- * @return Whether the target should continue to be animated and this call was successful.
- * If false, the animation will be cancelled because the user has determined that the
- * animation is now invalid and not required. In such a case, the cancel will trigger the
- * animation end callback as well, but will not send any further size changes.
- */
- boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds);
-
- /** Sets the alpha of the animation target */
- boolean setPinnedStackAlpha(float alpha);
-
- /**
- * Callback for the target to inform it that the animation has ended, so it can do some
- * necessary cleanup.
- *
- * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
- * callbacks
- * @param finalStackSize the final stack bounds to set on the target (can be to indicate that
- * the animation was cancelled and the target does not need to update to the final stack bounds)
- * @param moveToFullscreen whether or the target should reparent itself to the fullscreen stack
- * when the animation completes
- */
- void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
- boolean moveToFullscreen);
-
- /** @return True if the target is attached to the window hierarchy. */
- default boolean isAttached() {
- return true;
- }
-}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0029dc8..5cd2930 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -91,11 +91,9 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
-import static com.android.server.wm.DisplayContentProto.CHANGING_APPS;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
-import static com.android.server.wm.DisplayContentProto.DOCKED_STACK_DIVIDER_CONTROLLER;
import static com.android.server.wm.DisplayContentProto.DPI;
import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
@@ -132,7 +130,7 @@
import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE;
import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE;
import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
-import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
+import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS;
import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION;
@@ -150,7 +148,6 @@
import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
-import android.animation.AnimationHandler;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -315,9 +312,8 @@
final ArraySet<ActivityRecord> mOpeningApps = new ArraySet<>();
final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>();
- final ArraySet<ActivityRecord> mChangingApps = new ArraySet<>();
+ final ArraySet<WindowContainer> mChangingContainers = new ArraySet<>();
final UnknownAppVisibilityController mUnknownAppVisibilityController;
- BoundsAnimationController mBoundsAnimationController;
private MetricsLogger mMetricsLogger;
@@ -977,10 +973,6 @@
mAppTransitionController = new AppTransitionController(mWmService, this);
mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
- AnimationHandler animationHandler = new AnimationHandler();
- mBoundsAnimationController = new BoundsAnimationController(mWmService.mContext,
- mAppTransition, mWmService.mAnimationHandler, animationHandler);
-
final InputChannel inputChannel = mWmService.mInputManager.monitorInput(
"PointerEventDispatcher" + mDisplayId, mDisplayId);
mPointerEventDispatcher = new PointerEventDispatcher(inputChannel);
@@ -1012,7 +1004,7 @@
mDisplayPolicy.systemReady();
}
mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius();
- mDividerControllerLocked = new DockedStackDividerController(mWmService, this);
+ mDividerControllerLocked = new DockedStackDividerController(this);
mPinnedStackControllerLocked = new PinnedStackController(mWmService, this);
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
@@ -2239,12 +2231,6 @@
* for bounds calculations.
*/
void preOnConfigurationChanged() {
- final DockedStackDividerController dividerController = getDockedDividerController();
-
- if (dividerController != null) {
- getDockedDividerController().onConfigurationChanged();
- }
-
final PinnedStackController pinnedStackController = getPinnedStackController();
if (pinnedStackController != null) {
@@ -2628,10 +2614,11 @@
// We also remove the outside touch area for resizing for all freeform
// tasks (including the focused).
// We save the focused task region once we find it, and add it back at the end.
- // If the task is home stack and it is resizable in the minimized state, we want to
- // exclude the docked stack from touch so we need the entire screen area and not just a
+ // If the task is home stack and it is resizable and visible (top of its root task), we want
+ // to exclude the docked stack from touch so we need the entire screen area and not just a
// small portion which the home stack currently is resized to.
- if (task.isActivityTypeHome() && task.getStack().isMinimizedDockAndHomeStackResizable()) {
+ if (task.isActivityTypeHome() && task.isVisible() && task.getStack().getTile() != null
+ && task.isResizeable()) {
mDisplayContent.getBounds(mTmpRect);
} else {
task.getDimBounds(mTmpRect);
@@ -2640,6 +2627,8 @@
if (task == focusedTask) {
// Add the focused task rect back into the exclude region once we are done
// processing stacks.
+ // NOTE: this *looks* like a no-op, but this usage of mTmpRect2 is expected by
+ // updateTouchExcludeRegion.
mTmpRect2.set(mTmpRect);
}
@@ -2701,7 +2690,7 @@
// Clear all transitions & screen frozen states when removing display.
mOpeningApps.clear();
mClosingApps.clear();
- mChangingApps.clear();
+ mChangingContainers.clear();
mUnknownAppVisibilityController.clear();
mAppTransition.removeAppTransitionTimeoutCallbacks();
handleAnimatingStoppedAndTransition();
@@ -2718,6 +2707,9 @@
mRemovingDisplay = false;
}
+ // Apply the pending transaction here since we may not be able to reach the DisplayContent
+ // on the next traversal if it's removed from RootWindowContainer child list.
+ getPendingTransaction().apply();
mWmService.mWindowPlacerLocked.requestTraversal();
}
@@ -2750,58 +2742,6 @@
return mDeferredRemoval;
}
- boolean animateForIme(float interpolatedValue, float animationTarget,
- float dividerAnimationTarget) {
- boolean updated = false;
-
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- if (stack == null || !stack.isAdjustedForIme()) {
- continue;
- }
-
- if (interpolatedValue >= 1f && animationTarget == 0f && dividerAnimationTarget == 0f) {
- stack.resetAdjustedForIme(true /* adjustBoundsNow */);
- updated = true;
- } else {
- mDividerControllerLocked.mLastAnimationProgress =
- mDividerControllerLocked.getInterpolatedAnimationValue(interpolatedValue);
- mDividerControllerLocked.mLastDividerProgress =
- mDividerControllerLocked.getInterpolatedDividerValue(interpolatedValue);
- updated |= stack.updateAdjustForIme(
- mDividerControllerLocked.mLastAnimationProgress,
- mDividerControllerLocked.mLastDividerProgress,
- false /* force */);
- }
- if (interpolatedValue >= 1f) {
- stack.endImeAdjustAnimation();
- }
- }
-
- return updated;
- }
-
- boolean clearImeAdjustAnimation() {
- boolean changed = false;
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- if (stack != null && stack.isAdjustedForIme()) {
- stack.resetAdjustedForIme(true /* adjustBoundsNow */);
- changed = true;
- }
- }
- return changed;
- }
-
- void beginImeAdjustAnimation() {
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- if (stack.isVisible() && stack.isAdjustedForIme()) {
- stack.beginImeAdjustAnimation();
- }
- }
- }
-
void adjustForImeIfNeeded() {
final WindowState imeWin = mInputMethodWindow;
final boolean imeVisible = imeWin != null && imeWin.isVisibleLw()
@@ -2896,7 +2836,6 @@
final ActivityStack stack = mTaskContainers.getChildAt(i);
stack.dumpDebug(proto, TASKS, logLevel);
}
- mDividerControllerLocked.dumpDebug(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
for (int i = mOverlayContainers.getChildCount() - 1; i >= 0; --i) {
final WindowToken windowToken = mOverlayContainers.getChildAt(i);
windowToken.dumpDebug(proto, OVERLAY_WINDOWS, logLevel);
@@ -2919,9 +2858,6 @@
for (int i = mClosingApps.size() - 1; i >= 0; i--) {
mClosingApps.valueAt(i).writeIdentifierToProto(proto, CLOSING_APPS);
}
- for (int i = mChangingApps.size() - 1; i >= 0; i--) {
- mChangingApps.valueAt(i).writeIdentifierToProto(proto, CHANGING_APPS);
- }
proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
final ActivityStack focusedStack = getFocusedStack();
@@ -3069,8 +3005,6 @@
}
pw.println();
- mDividerControllerLocked.dump(prefix, pw);
- pw.println();
mPinnedStackControllerLocked.dump(prefix, pw);
pw.println();
@@ -3673,7 +3607,7 @@
}
}
- if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty() || !mChangingApps.isEmpty()) {
+ if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty() || !mChangingContainers.isEmpty()) {
pw.println();
if (mOpeningApps.size() > 0) {
pw.print(" mOpeningApps="); pw.println(mOpeningApps);
@@ -3681,8 +3615,8 @@
if (mClosingApps.size() > 0) {
pw.print(" mClosingApps="); pw.println(mClosingApps);
}
- if (mChangingApps.size() > 0) {
- pw.print(" mChangingApps="); pw.println(mChangingApps);
+ if (mChangingContainers.size() > 0) {
+ pw.print(" mChangingApps="); pw.println(mChangingContainers);
}
}
@@ -4091,7 +4025,7 @@
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
- mWmService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
+ mWmService.mH.sendEmptyMessage(UPDATE_MULTI_WINDOW_STACKS);
}
/**
@@ -4699,17 +4633,18 @@
@Override
int getOrientation(int candidate) {
if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
- // Apps and their containers are not allowed to specify an orientation while the
- // docked stack is visible...except for the home stack if the docked stack is
- // minimized and it actually set something and the bounds is different from the
- // display.
+ // Apps and their containers are not allowed to specify an orientation while using
+ // root tasks...except for the home stack if it is not resizable and currently
+ // visible (top of) its root task.
if (mRootHomeTask != null && mRootHomeTask.isVisible()
- && mDividerControllerLocked.isMinimizedDock()
- && !(mDividerControllerLocked.isHomeStackResizable()
- && mRootHomeTask.matchParentBounds())) {
- final int orientation = mRootHomeTask.getOrientation();
- if (orientation != SCREEN_ORIENTATION_UNSET) {
- return orientation;
+ && mRootHomeTask.getTile() != null) {
+ final Task topMost = mRootHomeTask.getTopMostTask();
+ final boolean resizable = topMost == null && topMost.isResizeable();
+ if (!(resizable && mRootHomeTask.matchParentBounds())) {
+ final int orientation = mRootHomeTask.getOrientation();
+ if (orientation != SCREEN_ORIENTATION_UNSET) {
+ return orientation;
+ }
}
}
return SCREEN_ORIENTATION_UNSPECIFIED;
@@ -6174,8 +6109,6 @@
t.removeAllChildren();
}
}
- mDividerControllerLocked.setMinimizedDockedStack(false /* minimized */,
- false /* animate */);
} finally {
final ActivityStack topFullscreenStack =
getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index f02a9dd..497c473 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1178,8 +1178,6 @@
return R.anim.dock_left_enter;
}
}
- } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
- return selectDockedDividerAnimation(win, transit);
}
if (transit == TRANSIT_PREVIEW_DONE) {
@@ -1204,36 +1202,6 @@
return ANIMATION_STYLEABLE;
}
- private int selectDockedDividerAnimation(WindowState win, int transit) {
- int insets = mDisplayContent.getDockedDividerController().getContentInsets();
-
- // If the divider is behind the navigation bar, don't animate.
- final Rect frame = win.getFrameLw();
- final boolean behindNavBar = mNavigationBar != null
- && ((mNavigationBarPosition == NAV_BAR_BOTTOM
- && frame.top + insets >= mNavigationBar.getFrameLw().top)
- || (mNavigationBarPosition == NAV_BAR_RIGHT
- && frame.left + insets >= mNavigationBar.getFrameLw().left)
- || (mNavigationBarPosition == NAV_BAR_LEFT
- && frame.right - insets <= mNavigationBar.getFrameLw().right));
- final boolean landscape = frame.height() > frame.width();
- final boolean offscreenLandscape = landscape && (frame.right - insets <= 0
- || frame.left + insets >= win.getDisplayFrameLw().right);
- final boolean offscreenPortrait = !landscape && (frame.top - insets <= 0
- || frame.bottom + insets >= win.getDisplayFrameLw().bottom);
- final boolean offscreen = offscreenLandscape || offscreenPortrait;
- if (behindNavBar || offscreen) {
- return ANIMATION_STYLEABLE;
- }
- if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
- return R.anim.fade_in;
- } else if (transit == TRANSIT_EXIT) {
- return R.anim.fade_out;
- } else {
- return ANIMATION_STYLEABLE;
- }
- }
-
/**
* Called when a new system UI visibility is being reported, allowing
* the policy to adjust what is actually reported.
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6431e11..20738ed 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -16,329 +16,26 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
-
-import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
-import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.server.wm.DockedStackDividerControllerProto.MINIMIZED_DOCK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.util.ArraySet;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-import android.view.DisplayCutout;
-import android.view.DisplayInfo;
-import android.view.IDockedStackListener;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.internal.policy.DockedDividerUtils;
-import com.android.server.LocalServices;
-import com.android.server.inputmethod.InputMethodManagerInternal;
-import com.android.server.wm.WindowManagerService.H;
-
-import java.io.PrintWriter;
/**
* Keeps information about the docked stack divider.
*/
public class DockedStackDividerController {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
-
- /**
- * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
- * revealing surface at the earliest.
- */
- private static final float CLIP_REVEAL_MEET_EARLIEST = 0.6f;
-
- /**
- * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
- * revealing surface at the latest.
- */
- private static final float CLIP_REVEAL_MEET_LAST = 1f;
-
- /**
- * If the app translates at least CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, we start
- * meet somewhere between {@link #CLIP_REVEAL_MEET_LAST} and {@link #CLIP_REVEAL_MEET_EARLIEST}.
- */
- private static final float CLIP_REVEAL_MEET_FRACTION_MIN = 0.4f;
-
- /**
- * If the app translates equals or more than CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance,
- * we meet at {@link #CLIP_REVEAL_MEET_EARLIEST}.
- */
- private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f;
-
- private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR =
- new PathInterpolator(0.2f, 0f, 0.1f, 1f);
-
- private static final long IME_ADJUST_ANIM_DURATION = 280;
-
- private static final long IME_ADJUST_DRAWN_TIMEOUT = 200;
-
- private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
-
- private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
- private int mDividerWindowWidth;
- private int mDividerWindowWidthInactive;
- private int mDividerInsets;
- private int mTaskHeightInMinimizedMode;
private boolean mResizing;
- private WindowState mWindow;
- private final Rect mTmpRect = new Rect();
- private final Rect mTmpRect2 = new Rect();
- private final Rect mTmpRect3 = new Rect();
- private final Rect mLastRect = new Rect();
- private boolean mLastVisibility = false;
- private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
- = new RemoteCallbackList<>();
- private boolean mMinimizedDock;
- private int mOriginalDockedSide = DOCKED_INVALID;
- private boolean mAnimatingForMinimizedDockedStack;
- private boolean mAnimationStarted;
- private long mAnimationStartTime;
- private float mAnimationStart;
- private float mAnimationTarget;
- private long mAnimationDuration;
- private boolean mAnimationStartDelayed;
- private final Interpolator mMinimizedDockInterpolator;
- private float mMaximizeMeetFraction;
private final Rect mTouchRegion = new Rect();
- private boolean mAnimatingForIme;
- private boolean mAdjustedForIme;
- private int mImeHeight;
- private WindowState mDelayedImeWin;
- private boolean mAdjustedForDivider;
- private float mDividerAnimationStart;
- private float mDividerAnimationTarget;
- float mLastAnimationProgress;
- float mLastDividerProgress;
- private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4];
- private boolean mImeHideRequested;
- private ActivityStack mDimmedStack;
- DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
- mService = service;
+ DockedStackDividerController(DisplayContent displayContent) {
mDisplayContent = displayContent;
- final Context context = service.mContext;
- mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
- context, android.R.interpolator.fast_out_slow_in);
- loadDimens();
- }
-
- int getSmallestWidthDpForBounds(Rect bounds) {
- final DisplayInfo di = mDisplayContent.getDisplayInfo();
-
- final int baseDisplayWidth = mDisplayContent.mBaseDisplayWidth;
- final int baseDisplayHeight = mDisplayContent.mBaseDisplayHeight;
- int minWidth = Integer.MAX_VALUE;
-
- // Go through all screen orientations and find the orientation in which the task has the
- // smallest width.
- for (int rotation = 0; rotation < 4; rotation++) {
- mTmpRect.set(bounds);
- mDisplayContent.rotateBounds(di.rotation, rotation, mTmpRect);
- final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- mTmpRect2.set(0, 0,
- rotated ? baseDisplayHeight : baseDisplayWidth,
- rotated ? baseDisplayWidth : baseDisplayHeight);
- final int orientation = mTmpRect2.width() <= mTmpRect2.height()
- ? ORIENTATION_PORTRAIT
- : ORIENTATION_LANDSCAPE;
- final int dockSide = getDockSide(mTmpRect, mTmpRect2, orientation, rotation);
- final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide,
- getContentWidth());
-
- final DisplayCutout displayCutout = mDisplayContent.calculateDisplayCutoutForRotation(
- rotation).getDisplayCutout();
-
- // Since we only care about feasible states, snap to the closest snap target, like it
- // would happen when actually rotating the screen.
- final int snappedPosition = mSnapAlgorithmForRotation[rotation]
- .calculateNonDismissingSnapTarget(position).position;
- DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, mTmpRect,
- mTmpRect2.width(), mTmpRect2.height(), getContentWidth());
- mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, mTmpRect2.width(),
- mTmpRect2.height(), displayCutout, mTmpRect3);
- mService.intersectDisplayInsetBounds(mTmpRect2, mTmpRect3, mTmpRect);
- minWidth = Math.min(mTmpRect.width(), minWidth);
- }
- return (int) (minWidth / mDisplayContent.getDisplayMetrics().density);
- }
-
- /**
- * Get the current docked side. Determined by its location of {@param bounds} within
- * {@param displayRect} but if both are the same, it will try to dock to each side and determine
- * if allowed in its respected {@param orientation}.
- *
- * @param bounds bounds of the docked task to get which side is docked
- * @param displayRect bounds of the display that contains the docked task
- * @param orientation the origination of device
- * @return current docked side
- */
- int getDockSide(Rect bounds, Rect displayRect, int orientation, int rotation) {
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- // Portrait mode, docked either at the top or the bottom.
- final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top);
- if (diff > 0) {
- return DOCKED_TOP;
- } else if (diff < 0) {
- return DOCKED_BOTTOM;
- }
- return canPrimaryStackDockTo(DOCKED_TOP, displayRect, rotation)
- ? DOCKED_TOP : DOCKED_BOTTOM;
- } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
- // Landscape mode, docked either on the left or on the right.
- final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left);
- if (diff > 0) {
- return DOCKED_LEFT;
- } else if (diff < 0) {
- return DOCKED_RIGHT;
- }
- return canPrimaryStackDockTo(DOCKED_LEFT, displayRect, rotation)
- ? DOCKED_LEFT : DOCKED_RIGHT;
- }
- return DOCKED_INVALID;
- }
-
- void getHomeStackBoundsInDockedMode(Configuration parentConfig, int dockSide, Rect outBounds) {
- final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout;
- final int displayWidth = parentConfig.windowConfiguration.getBounds().width();
- final int displayHeight = parentConfig.windowConfiguration.getBounds().height();
- mDisplayContent.getDisplayPolicy().getStableInsetsLw(
- parentConfig.windowConfiguration.getRotation(), displayWidth, displayHeight,
- displayCutout, mTmpRect);
- int dividerSize = mDividerWindowWidth - 2 * mDividerInsets;
- // The offset in the left (landscape)/top (portrait) is calculated with the minimized
- // offset value with the divider size and any system insets in that direction.
- if (parentConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
- outBounds.set(0, mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top,
- displayWidth, displayHeight);
- } else {
- // In landscape also inset the left/right side with the status bar height to match the
- // minimized size height in portrait mode.
- final int primaryTaskWidth = mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top;
- int left = mTmpRect.left;
- int right = displayWidth - mTmpRect.right;
- if (dockSide == DOCKED_LEFT) {
- left += primaryTaskWidth;
- } else if (dockSide == DOCKED_RIGHT) {
- right -= primaryTaskWidth;
- }
- outBounds.set(left, 0, right, displayHeight);
- }
- }
-
- boolean isHomeStackResizable() {
- final ActivityStack homeStack = mDisplayContent.getRootHomeTask();
- if (homeStack == null) {
- return false;
- }
- final Task homeTask = homeStack.getTopMostTask();
- return homeTask != null && homeTask.isResizeable();
- }
-
- private void initSnapAlgorithmForRotations() {
- final Configuration baseConfig = mDisplayContent.getConfiguration();
-
- // Initialize the snap algorithms for all 4 screen orientations.
- final Configuration config = new Configuration();
- for (int rotation = 0; rotation < 4; rotation++) {
- final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- final int dw = rotated
- ? mDisplayContent.mBaseDisplayHeight
- : mDisplayContent.mBaseDisplayWidth;
- final int dh = rotated
- ? mDisplayContent.mBaseDisplayWidth
- : mDisplayContent.mBaseDisplayHeight;
- final DisplayCutout displayCutout =
- mDisplayContent.calculateDisplayCutoutForRotation(rotation).getDisplayCutout();
- final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
- displayPolicy.getStableInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
- config.unset();
- config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
-
- final int appWidth = displayPolicy.getNonDecorDisplayWidth(dw, dh, rotation,
- baseConfig.uiMode, displayCutout);
- final int appHeight = displayPolicy.getNonDecorDisplayHeight(dw, dh, rotation,
- baseConfig.uiMode, displayCutout);
- displayPolicy.getNonDecorInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
- final int leftInset = mTmpRect.left;
- final int topInset = mTmpRect.top;
-
- config.windowConfiguration.setAppBounds(leftInset /*left*/, topInset /*top*/,
- leftInset + appWidth /*right*/, topInset + appHeight /*bottom*/);
-
- final float density = mDisplayContent.getDisplayMetrics().density;
- config.screenWidthDp = (int) (displayPolicy.getConfigDisplayWidth(dw, dh, rotation,
- baseConfig.uiMode, displayCutout) / density);
- config.screenHeightDp = (int) (displayPolicy.getConfigDisplayHeight(dw, dh, rotation,
- baseConfig.uiMode, displayCutout) / density);
- final Context rotationContext = mService.mContext.createConfigurationContext(config);
- mSnapAlgorithmForRotation[rotation] = new DividerSnapAlgorithm(
- rotationContext.getResources(), dw, dh, getContentWidth(),
- config.orientation == ORIENTATION_PORTRAIT, mTmpRect);
- }
- }
-
- private void loadDimens() {
- final Context context = mService.mContext;
- mDividerWindowWidth = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_thickness);
- mDividerInsets = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_insets);
- mDividerWindowWidthInactive = WindowManagerService.dipToPixel(
- DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics());
- mTaskHeightInMinimizedMode = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.task_height_of_minimized_mode);
- initSnapAlgorithmForRotations();
- }
-
- void onConfigurationChanged() {
- loadDimens();
}
boolean isResizing() {
return mResizing;
}
- int getContentWidth() {
- return mDividerWindowWidth - 2 * mDividerInsets;
- }
-
- int getContentInsets() {
- return mDividerInsets;
- }
-
- int getContentWidthInactive() {
- return mDividerWindowWidthInactive;
- }
-
void setResizing(boolean resizing) {
if (mResizing != resizing) {
mResizing = resizing;
@@ -352,675 +49,10 @@
void getTouchRegion(Rect outRegion) {
outRegion.set(mTouchRegion);
- if (mWindow != null) {
- outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
- }
}
private void resetDragResizingChangeReported() {
mDisplayContent.forAllWindows(WindowState::resetDragResizingChangeReported,
true /* traverseTopToBottom */ );
}
-
- void setWindow(WindowState window) {
- mWindow = window;
- reevaluateVisibility(false);
- }
-
- void reevaluateVisibility(boolean force) {
- if (mWindow == null) {
- return;
- }
- ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask();
-
- // If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide
- final boolean visible = stack != null;
- if (mLastVisibility == visible && !force) {
- return;
- }
- mLastVisibility = visible;
- notifyDockedDividerVisibilityChanged(visible);
- if (!visible) {
- setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f);
- }
- }
-
- private boolean wasVisible() {
- return mLastVisibility;
- }
-
- void setAdjustedForIme(
- boolean adjustedForIme, boolean adjustedForDivider,
- boolean animate, WindowState imeWin, int imeHeight) {
- if (mAdjustedForIme != adjustedForIme || (adjustedForIme && mImeHeight != imeHeight)
- || mAdjustedForDivider != adjustedForDivider) {
- if (animate && !mAnimatingForMinimizedDockedStack) {
- // Notify SystemUI to set the target docked stack size according current docked
- // state without animation when calling startImeAdjustAnimation.
- notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */,
- isHomeStackResizable());
- startImeAdjustAnimation(adjustedForIme, adjustedForDivider, imeWin);
- } else {
- // Animation might be delayed, so only notify if we don't run an animation.
- notifyAdjustedForImeChanged(adjustedForIme || adjustedForDivider, 0 /* duration */);
- }
- mAdjustedForIme = adjustedForIme;
- mImeHeight = imeHeight;
- mAdjustedForDivider = adjustedForDivider;
- }
- }
-
- int getImeHeightAdjustedFor() {
- return mImeHeight;
- }
-
- void positionDockedStackedDivider(Rect frame) {
- ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask();
- if (stack == null) {
- // Unfortunately we might end up with still having a divider, even though the underlying
- // stack was already removed. This is because we are on AM thread and the removal of the
- // divider was deferred to WM thread and hasn't happened yet. In that case let's just
- // keep putting it in the same place it was before the stack was removed to have
- // continuity and prevent it from jumping to the center. It will get hidden soon.
- frame.set(mLastRect);
- return;
- } else {
- stack.getDimBounds(mTmpRect);
- }
- int side = stack.getDockSide();
- switch (side) {
- case DOCKED_LEFT:
- frame.set(mTmpRect.right - mDividerInsets, frame.top,
- mTmpRect.right + frame.width() - mDividerInsets, frame.bottom);
- break;
- case DOCKED_TOP:
- frame.set(frame.left, mTmpRect.bottom - mDividerInsets,
- mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets);
- break;
- case DOCKED_RIGHT:
- frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top,
- mTmpRect.left + mDividerInsets, frame.bottom);
- break;
- case DOCKED_BOTTOM:
- frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets,
- frame.right, mTmpRect.top + mDividerInsets);
- break;
- }
- mLastRect.set(frame);
- }
-
- private void notifyDockedDividerVisibilityChanged(boolean visible) {
- final int size = mDockedStackListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
- try {
- listener.onDividerVisibilityChanged(visible);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering divider visibility changed event.", e);
- }
- }
- mDockedStackListeners.finishBroadcast();
- }
-
- /**
- * Checks if the primary stack is allowed to dock to a specific side based on its original dock
- * side.
- *
- * @param dockSide the side to see if it is valid
- * @return true if the side provided is valid
- */
- boolean canPrimaryStackDockTo(int dockSide, Rect parentRect, int rotation) {
- final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
- return isDockSideAllowed(dockSide, mOriginalDockedSide,
- policy.navigationBarPosition(parentRect.width(), parentRect.height(), rotation),
- policy.navigationBarCanMove());
- }
-
- @VisibleForTesting
- static boolean isDockSideAllowed(int dockSide, int originalDockSide, int navBarPosition,
- boolean navigationBarCanMove) {
- if (dockSide == DOCKED_TOP) {
- return true;
- }
-
- if (navigationBarCanMove) {
- // Only allow the dockside opposite to the nav bar position in landscape
- return dockSide == DOCKED_LEFT && navBarPosition == NAV_BAR_RIGHT
- || dockSide == DOCKED_RIGHT && navBarPosition == NAV_BAR_LEFT;
- }
-
- // Side is the same as original side
- if (dockSide == originalDockSide) {
- return true;
- }
-
- // Only if original docked side was top in portrait will allow left for landscape
- return dockSide == DOCKED_LEFT && originalDockSide == DOCKED_TOP;
- }
-
- void notifyDockedStackExistsChanged(boolean exists) {
- // TODO(multi-display): Perform all actions only for current display.
- final int size = mDockedStackListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
- try {
- listener.onDockedStackExistsChanged(exists);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering docked stack exists changed event.", e);
- }
- }
- mDockedStackListeners.finishBroadcast();
- if (exists) {
- InputMethodManagerInternal inputMethodManagerInternal =
- LocalServices.getService(InputMethodManagerInternal.class);
- if (inputMethodManagerInternal != null) {
-
- // Hide the current IME to avoid problems with animations from IME adjustment when
- // attaching the docked stack.
- inputMethodManagerInternal.hideCurrentInputMethod();
- mImeHideRequested = true;
- }
-
- // If a primary stack was just created, it will not have access to display content at
- // this point so pass it from here to get a valid dock side.
- final ActivityStack stack =
- mDisplayContent.getRootSplitScreenPrimaryTask();
- mOriginalDockedSide = stack.getDockSideForDisplay(mDisplayContent);
- return;
- }
- mOriginalDockedSide = DOCKED_INVALID;
- setMinimizedDockedStack(false /* minimizedDock */, false /* animate */);
-
- if (mDimmedStack != null) {
- mDimmedStack.stopDimming();
- mDimmedStack = null;
- }
- }
-
- /**
- * Resets the state that IME hide has been requested. See {@link #isImeHideRequested}.
- */
- void resetImeHideRequested() {
- mImeHideRequested = false;
- }
-
- /**
- * The docked stack divider controller makes sure the IME gets hidden when attaching the docked
- * stack, to avoid animation problems. This flag indicates whether the request to hide the IME
- * has been sent in an asynchronous manner, and the IME should be treated as hidden already.
- *
- * @return whether IME hide request has been sent
- */
- boolean isImeHideRequested() {
- return mImeHideRequested;
- }
-
- private void notifyDockedStackMinimizedChanged(boolean minimizedDock, boolean animate,
- boolean isHomeStackResizable) {
- long animDuration = 0;
- if (animate) {
- final ActivityStack stack =
- mDisplayContent.getRootSplitScreenPrimaryTask();
- final long transitionDuration = isAnimationMaximizing()
- ? mDisplayContent.mAppTransition.getLastClipRevealTransitionDuration()
- : DEFAULT_APP_TRANSITION_DURATION;
- mAnimationDuration = (long)
- (transitionDuration * mService.getTransitionAnimationScaleLocked());
- mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
- animDuration = (long) (mAnimationDuration * mMaximizeMeetFraction);
- }
- final int size = mDockedStackListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
- try {
- listener.onDockedStackMinimizedChanged(minimizedDock, animDuration,
- isHomeStackResizable);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e);
- }
- }
- mDockedStackListeners.finishBroadcast();
- // Only notify ATM after we update the remote listeners, otherwise it may trigger another
- // minimize change, which would lead to an inversion of states send to the listeners
- mService.mAtmInternal.notifyDockedStackMinimizedChanged(minimizedDock);
- }
-
- void notifyDockSideChanged(int newDockSide) {
- final int size = mDockedStackListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
- try {
- listener.onDockSideChanged(newDockSide);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering dock side changed event.", e);
- }
- }
- mDockedStackListeners.finishBroadcast();
- }
-
- private void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {
- final int size = mDockedStackListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
- try {
- listener.onAdjustedForImeChanged(adjustedForIme, animDuration);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e);
- }
- }
- mDockedStackListeners.finishBroadcast();
- }
-
- void registerDockedStackListener(IDockedStackListener listener) {
- mDockedStackListeners.register(listener);
- notifyDockedDividerVisibilityChanged(wasVisible());
- notifyDockedStackExistsChanged(
- mDisplayContent.getRootSplitScreenPrimaryTask() != null);
- notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */,
- isHomeStackResizable());
- notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
-
- }
-
- /**
- * Shows a dim layer with {@param alpha} if {@param visible} is true and
- * {@param targetWindowingMode} isn't
- * {@link android.app.WindowConfiguration#WINDOWING_MODE_UNDEFINED} and there is a stack on the
- * display in that windowing mode.
- */
- void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
- // TODO: Maybe only allow split-screen windowing modes?
- final ActivityStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
- ? mDisplayContent.getTopStackInWindowingMode(targetWindowingMode)
- : null;
- final ActivityStack dockedStack = mDisplayContent.getRootSplitScreenPrimaryTask();
- boolean visibleAndValid = visible && stack != null && dockedStack != null;
-
- // Ensure an old dim that was shown for the docked stack divider is removed so we don't end
- // up with dim layers that can no longer be removed.
- if (mDimmedStack != null && mDimmedStack != stack) {
- mDimmedStack.stopDimming();
- mDimmedStack = null;
- }
-
- if (visibleAndValid) {
- mDimmedStack = stack;
- stack.dim(alpha);
- }
- if (!visibleAndValid && stack != null) {
- mDimmedStack = null;
- stack.stopDimming();
- }
- }
-
- /**
- * Notifies the docked stack divider controller of a visibility change that happens without
- * an animation.
- */
- void notifyAppVisibilityChanged() {
- checkMinimizeChanged(false /* animate */);
- }
-
- void notifyAppTransitionStarting(ArraySet<ActivityRecord> openingApps, int appTransition) {
- final boolean wasMinimized = mMinimizedDock;
- checkMinimizeChanged(true /* animate */);
-
- // We were minimized, and now we are still minimized, but somebody is trying to launch an
- // app in docked stack, better show recent apps so we actually get unminimized! However do
- // not do this if keyguard is dismissed such as when the device is unlocking. This catches
- // any case that was missed in ActivityStarter.postStartActivityUncheckedProcessing because
- // we couldn't retrace the launch of the app in the docked stack to the launch from
- // homescreen.
- if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps)
- && appTransition != TRANSIT_NONE &&
- !AppTransition.isKeyguardGoingAwayTransit(appTransition)) {
- if (mService.mAtmInternal.isRecentsComponentHomeActivity(mService.mCurrentUserId)) {
- // When the home activity is the recents component and we are already minimized,
- // then there is nothing to do here since home is already visible
- } else {
- mService.showRecentApps();
- }
- }
- }
-
- /**
- * @return true if {@param apps} contains an activity in the docked stack, false otherwise.
- */
- private boolean containsAppInDockedStack(ArraySet<ActivityRecord> apps) {
- for (int i = apps.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = apps.valueAt(i);
- if (activity.getTask() != null && activity.inSplitScreenPrimaryWindowingMode()) {
- return true;
- }
- }
- return false;
- }
-
- boolean isMinimizedDock() {
- return mMinimizedDock;
- }
-
- void checkMinimizeChanged(boolean animate) {
- if (mDisplayContent.getRootSplitScreenPrimaryTask() == null) {
- return;
- }
- final ActivityStack homeStack = mDisplayContent.getRootHomeTask();
- if (homeStack == null) {
- return;
- }
- final Task homeTask = homeStack.getTopMostTask();
- if (homeTask == null || !isWithinDisplay(homeTask)) {
- return;
- }
-
- // Do not minimize when dock is already minimized while keyguard is showing and not
- // occluded such as unlocking the screen
- if (mMinimizedDock && mService.mKeyguardOrAodShowingOnDefaultDisplay) {
- return;
- }
- final ActivityStack topSecondaryStack = mDisplayContent.getTopStackInWindowingMode(
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController();
- final boolean minimizedForRecentsAnimation = recentsAnim != null &&
- recentsAnim.isSplitScreenMinimized();
- boolean homeVisible = homeTask.getTopVisibleActivity() != null;
- if (homeVisible && topSecondaryStack != null) {
- // Home should only be considered visible if it is greater or equal to the top secondary
- // stack in terms of z-order.
- homeVisible = homeStack.compareTo(topSecondaryStack) >= 0;
- }
- setMinimizedDockedStack(homeVisible || minimizedForRecentsAnimation, animate);
- }
-
- private boolean isWithinDisplay(Task task) {
- task.getBounds(mTmpRect);
- mDisplayContent.getBounds(mTmpRect2);
- return mTmpRect.intersect(mTmpRect2);
- }
-
- /**
- * Sets whether the docked stack is currently in a minimized state, i.e. all the tasks in the
- * docked stack are heavily clipped so you can only see a minimal peek state.
- *
- * @param minimizedDock Whether the docked stack is currently minimized.
- * @param animate Whether to animate the change.
- */
- void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
- final boolean wasMinimized = mMinimizedDock;
- mMinimizedDock = minimizedDock;
- if (minimizedDock == wasMinimized) {
- return;
- }
-
- final boolean imeChanged = clearImeAdjustAnimation();
- boolean minimizedChange = false;
- if (isHomeStackResizable()) {
- notifyDockedStackMinimizedChanged(minimizedDock, animate,
- true /* isHomeStackResizable */);
- minimizedChange = true;
- } else {
- if (minimizedDock) {
- if (animate) {
- startAdjustAnimation(0f, 1f);
- } else {
- minimizedChange |= setMinimizedDockedStack(true);
- }
- } else {
- if (animate) {
- startAdjustAnimation(1f, 0f);
- } else {
- minimizedChange |= setMinimizedDockedStack(false);
- }
- }
- }
- if (imeChanged || minimizedChange) {
- if (imeChanged && !minimizedChange) {
- Slog.d(TAG, "setMinimizedDockedStack: IME adjust changed due to minimizing,"
- + " minimizedDock=" + minimizedDock
- + " minimizedChange=" + minimizedChange);
- }
- mService.mWindowPlacerLocked.performSurfacePlacement();
- }
- }
-
- private boolean clearImeAdjustAnimation() {
- final boolean changed = mDisplayContent.clearImeAdjustAnimation();
- mAnimatingForIme = false;
- return changed;
- }
-
- private void startAdjustAnimation(float from, float to) {
- mAnimatingForMinimizedDockedStack = true;
- mAnimationStarted = false;
- mAnimationStart = from;
- mAnimationTarget = to;
- }
-
- private void startImeAdjustAnimation(
- boolean adjustedForIme, boolean adjustedForDivider, WindowState imeWin) {
-
- // If we're not in an animation, the starting point depends on whether we're adjusted
- // or not. If we're already in an animation, we start from where the current animation
- // left off, so that the motion doesn't look discontinuous.
- if (!mAnimatingForIme) {
- mAnimationStart = mAdjustedForIme ? 1 : 0;
- mDividerAnimationStart = mAdjustedForDivider ? 1 : 0;
- mLastAnimationProgress = mAnimationStart;
- mLastDividerProgress = mDividerAnimationStart;
- } else {
- mAnimationStart = mLastAnimationProgress;
- mDividerAnimationStart = mLastDividerProgress;
- }
- mAnimatingForIme = true;
- mAnimationStarted = false;
- mAnimationTarget = adjustedForIme ? 1 : 0;
- mDividerAnimationTarget = adjustedForDivider ? 1 : 0;
-
- mDisplayContent.beginImeAdjustAnimation();
-
- // We put all tasks into drag resizing mode - wait until all of them have completed the
- // drag resizing switch.
- final Runnable existingWaitingForDrwanCallback =
- mService.mWaitingForDrawnCallbacks.get(mService.mRoot);
- if (existingWaitingForDrwanCallback != null) {
- mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, mService.mRoot);
- mService.mH.sendMessageDelayed(mService.mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT,
- mService.mRoot),
- IME_ADJUST_DRAWN_TIMEOUT);
- mAnimationStartDelayed = true;
- if (imeWin != null) {
-
- // There might be an old window delaying the animation start - clear it.
- if (mDelayedImeWin != null) {
- mDelayedImeWin.endDelayingAnimationStart();
- }
- mDelayedImeWin = imeWin;
- imeWin.startDelayingAnimationStart();
- }
-
- // If we are already waiting for something to be drawn, clear out the old one so it
- // still gets executed.
- // TODO: Have a real system where we can wait on different windows to be drawn with
- // different callbacks.
- existingWaitingForDrwanCallback.run();
- mService.mWaitingForDrawnCallbacks.put(mService.mRoot, () -> {
- synchronized (mService.mGlobalLock) {
- mAnimationStartDelayed = false;
- if (mDelayedImeWin != null) {
- mDelayedImeWin.endDelayingAnimationStart();
- }
- // If the adjust status changed since this was posted, only notify
- // the new states and don't animate.
- long duration = 0;
- if (mAdjustedForIme == adjustedForIme
- && mAdjustedForDivider == adjustedForDivider) {
- duration = IME_ADJUST_ANIM_DURATION;
- } else {
- Slog.w(TAG, "IME adjust changed while waiting for drawn:"
- + " adjustedForIme=" + adjustedForIme
- + " adjustedForDivider=" + adjustedForDivider
- + " mAdjustedForIme=" + mAdjustedForIme
- + " mAdjustedForDivider=" + mAdjustedForDivider);
- }
- notifyAdjustedForImeChanged(
- mAdjustedForIme || mAdjustedForDivider, duration);
- }
- });
- } else {
- notifyAdjustedForImeChanged(
- adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
- }
- }
-
- private boolean setMinimizedDockedStack(boolean minimized) {
- final ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask();
- notifyDockedStackMinimizedChanged(minimized, false /* animate */, isHomeStackResizable());
- return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f);
- }
-
- private boolean isAnimationMaximizing() {
- return mAnimationTarget == 0f;
- }
-
- public boolean animate(long now) {
- if (mWindow == null) {
- return false;
- }
- if (mAnimatingForMinimizedDockedStack) {
- return animateForMinimizedDockedStack(now);
- } else if (mAnimatingForIme && !mDisplayContent.mAppTransition.isRunning()) {
- // To prevent task stack resize animation may flicking when playing app transition
- // animation & IME window enter animation in parallel, make sure app transition is done
- // and then start to animate for IME.
- return animateForIme(now);
- }
- return false;
- }
-
- private boolean animateForIme(long now) {
- if (!mAnimationStarted || mAnimationStartDelayed) {
- mAnimationStarted = true;
- mAnimationStartTime = now;
- mAnimationDuration = (long)
- (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked());
- }
- float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
- t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
- .getInterpolation(t);
- final boolean updated =
- mDisplayContent.animateForIme(t, mAnimationTarget, mDividerAnimationTarget);
- if (updated) {
- mService.mWindowPlacerLocked.performSurfacePlacement();
- }
- if (t >= 1.0f) {
- mLastAnimationProgress = mAnimationTarget;
- mLastDividerProgress = mDividerAnimationTarget;
- mAnimatingForIme = false;
- return false;
- } else {
- return true;
- }
- }
-
- private boolean animateForMinimizedDockedStack(long now) {
- final ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask();
- if (!mAnimationStarted) {
- mAnimationStarted = true;
- mAnimationStartTime = now;
- notifyDockedStackMinimizedChanged(mMinimizedDock, true /* animate */,
- isHomeStackResizable() /* isHomeStackResizable */);
- }
- float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
- t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
- .getInterpolation(t);
- if (stack != null) {
- if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) {
- mService.mWindowPlacerLocked.performSurfacePlacement();
- }
- }
- if (t >= 1.0f) {
- mAnimatingForMinimizedDockedStack = false;
- return false;
- } else {
- return true;
- }
- }
-
- float getInterpolatedAnimationValue(float t) {
- return t * mAnimationTarget + (1 - t) * mAnimationStart;
- }
-
- float getInterpolatedDividerValue(float t) {
- return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart;
- }
-
- /**
- * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
- */
- private float getMinimizeAmount(ActivityStack stack, float t) {
- final float naturalAmount = getInterpolatedAnimationValue(t);
- if (isAnimationMaximizing()) {
- return adjustMaximizeAmount(stack, t, naturalAmount);
- } else {
- return naturalAmount;
- }
- }
-
- /**
- * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount
- * during the transition such that the edge of the clip reveal rect is met earlier in the
- * transition so we don't create a visible "hole", but only if both the clip reveal and the
- * docked stack divider start from about the same portion on the screen.
- */
- private float adjustMaximizeAmount(ActivityStack stack, float t, float naturalAmount) {
- if (mMaximizeMeetFraction == 1f) {
- return naturalAmount;
- }
- final int minimizeDistance = stack.getMinimizeDistance();
- final float startPrime = mDisplayContent.mAppTransition.getLastClipRevealMaxTranslation()
- / (float) minimizeDistance;
- final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime;
- final float t2 = Math.min(t / mMaximizeMeetFraction, 1);
- return amountPrime * t2 + naturalAmount * (1 - t2);
- }
-
- /**
- * Retrieves the animation fraction at which the docked stack has to meet the clip reveal
- * edge. See {@link #adjustMaximizeAmount}.
- */
- private float getClipRevealMeetFraction(ActivityStack stack) {
- if (!isAnimationMaximizing() || stack == null ||
- !mDisplayContent.mAppTransition.hadClipRevealAnimation()) {
- return 1f;
- }
- final int minimizeDistance = stack.getMinimizeDistance();
- final float fraction = Math.abs(mDisplayContent.mAppTransition
- .getLastClipRevealMaxTranslation()) / (float) minimizeDistance;
- final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN)
- / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN)));
- return CLIP_REVEAL_MEET_EARLIEST
- + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
- }
-
- public String toShortString() {
- return TAG;
- }
-
- WindowState getWindow() {
- return mWindow;
- }
-
- void dump(String prefix, PrintWriter pw) {
- pw.println(prefix + "DockedStackDividerController");
- pw.println(prefix + " mLastVisibility=" + mLastVisibility);
- pw.println(prefix + " mMinimizedDock=" + mMinimizedDock);
- pw.println(prefix + " mAdjustedForIme=" + mAdjustedForIme);
- pw.println(prefix + " mAdjustedForDivider=" + mAdjustedForDivider);
- }
-
- void dumpDebug(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- proto.write(MINIMIZED_DOCK, mMinimizedDock);
- proto.end(token);
- }
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 668b609..e14b8ae 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -52,7 +50,7 @@
* 1) When first entering PiP: the controller returns the valid bounds given, taking aspect ratio
* and IME state into account.
* 2) When rotating the device: the controller calculates the new bounds in the new orientation,
- * taking the minimized and IME state into account. In this case, we currently ignore the
+ * taking the IME state into account. In this case, we currently ignore the
* SystemUI adjustments (ie. expanded for menu, interaction, etc).
*
* Other changes in the system, including adjustment of IME, configuration change, and more are
@@ -72,8 +70,6 @@
private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback();
- // States that affect how the PIP can be manipulated
- private boolean mIsMinimized;
private boolean mIsImeShowing;
private int mImeHeight;
@@ -84,9 +80,6 @@
// Used to calculate stack bounds across rotations
private final DisplayInfo mDisplayInfo = new DisplayInfo();
- // The size and position information that describes where the pinned stack will go by default.
- private float mDefaultAspectRatio;
-
// The aspect ratio bounds of the PIP.
private float mMinAspectRatio;
private float mMaxAspectRatio;
@@ -98,43 +91,12 @@
* The callback object passed to listeners for them to notify the controller of state changes.
*/
private class PinnedStackControllerCallback extends IPinnedStackController.Stub {
-
- @Override
- public void setIsMinimized(final boolean isMinimized) {
- mHandler.post(() -> {
- mIsMinimized = isMinimized;
- });
- }
-
@Override
public int getDisplayRotation() {
synchronized (mService.mGlobalLock) {
return mDisplayInfo.rotation;
}
}
-
- @Override
- public void startAnimation(Rect destinationBounds, Rect sourceRectHint,
- int animationDuration) {
- synchronized (mService.mGlobalLock) {
- final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
- pinnedStack.animateResizePinnedStack(destinationBounds,
- sourceRectHint, animationDuration, true /* fromFullscreen */);
- }
- }
-
- @Override
- public void resetBoundsAnimation(Rect bounds) {
- synchronized (mService.mGlobalLock) {
- if (mDisplayContent.hasPinnedTask()) {
- final ActivityStack pinnedStack = mDisplayContent.getTopStackInWindowingMode(
- WINDOWING_MODE_PINNED);
- if (pinnedStack != null) {
- pinnedStack.resetCurrentBoundsAnimation(bounds);
- }
- }
- }
- }
}
/**
@@ -157,10 +119,6 @@
mDisplayContent = displayContent;
mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
reloadResources();
- // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
- // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
- // triggers a configuration change and the resources to be reloaded.
- mAspectRatio = mDefaultAspectRatio;
}
void onConfigurationChanged() {
@@ -172,8 +130,6 @@
*/
private void reloadResources() {
final Resources res = mService.mContext.getResources();
- mDefaultAspectRatio = res.getFloat(
- com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
mDisplayContent.getDisplay().getRealMetrics(mTmpMetrics);
mMinAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
@@ -191,12 +147,8 @@
mPinnedStackListener = listener;
notifyDisplayInfoChanged(mDisplayInfo);
notifyImeVisibilityChanged(mIsImeShowing, mImeHeight);
- // The movement bounds notification needs to be sent before the minimized state, since
- // SystemUI may use the bounds to restore the minimized position
- notifyMovementBoundsChanged(false /* fromImeAdjustment */,
- false /* fromShelfAdjustment */);
+ notifyMovementBoundsChanged(false /* fromImeAdjustment */);
notifyActionsChanged(mActions);
- notifyMinimizeChanged(mIsMinimized);
} catch (RemoteException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
}
@@ -247,8 +199,7 @@
void onDisplayInfoChanged(DisplayInfo displayInfo) {
synchronized (mService.mGlobalLock) {
setDisplayInfo(displayInfo);
- notifyMovementBoundsChanged(false /* fromImeAdjustment */,
- false /* fromShelfAdjustment */);
+ notifyMovementBoundsChanged(false /* fromImeAdjustment */);
}
}
@@ -269,7 +220,7 @@
mIsImeShowing = imeShowing;
mImeHeight = imeHeight;
notifyImeVisibilityChanged(imeShowing, imeHeight);
- notifyMovementBoundsChanged(true /* fromImeAdjustment */, false /* fromShelfAdjustment */);
+ notifyMovementBoundsChanged(true /* fromImeAdjustment */);
}
/**
@@ -279,10 +230,7 @@
if (Float.compare(mAspectRatio, aspectRatio) != 0) {
mAspectRatio = aspectRatio;
notifyAspectRatioChanged(aspectRatio);
- notifyMovementBoundsChanged(false /* fromImeAdjustment */,
- false /* fromShelfAdjustment */);
- notifyPrepareAnimation(null /* sourceHintRect */, aspectRatio,
- null /* stackBounds */);
+ notifyMovementBoundsChanged(false /* fromImeAdjustment */);
}
}
@@ -304,10 +252,6 @@
notifyActionsChanged(mActions);
}
- void prepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
- notifyPrepareAnimation(sourceRectHint, aspectRatio, stackBounds);
- }
-
/**
* Notifies listeners that the PIP needs to be adjusted for the IME.
*/
@@ -331,19 +275,6 @@
}
/**
- * Notifies listeners that the PIP minimized state has changed.
- */
- private void notifyMinimizeChanged(boolean isMinimized) {
- if (mPinnedStackListener != null) {
- try {
- mPinnedStackListener.onMinimizedStateChanged(isMinimized);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering minimize changed event.", e);
- }
- }
- }
-
- /**
* Notifies listeners that the PIP actions have changed.
*/
private void notifyActionsChanged(List<RemoteAction> actions) {
@@ -359,8 +290,7 @@
/**
* Notifies listeners that the PIP movement bounds have changed.
*/
- private void notifyMovementBoundsChanged(boolean fromImeAdjustment,
- boolean fromShelfAdjustment) {
+ private void notifyMovementBoundsChanged(boolean fromImeAdjustment) {
synchronized (mService.mGlobalLock) {
if (mPinnedStackListener == null) {
return;
@@ -371,8 +301,7 @@
if (pinnedStack != null) {
pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
}
- mPinnedStackListener.onMovementBoundsChanged(animatingBounds,
- fromImeAdjustment, fromShelfAdjustment);
+ mPinnedStackListener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment);
} catch (RemoteException e) {
Slog.e(TAG_WM, "Error delivering actions changed event.", e);
}
@@ -391,24 +320,10 @@
}
}
- /**
- * Notifies listeners that the PIP animation is about to happen.
- */
- private void notifyPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
- if (mPinnedStackListener == null) return;
- try {
- mPinnedStackListener.onPrepareAnimation(sourceRectHint, aspectRatio, stackBounds);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering prepare animation event.", e);
- }
- }
-
void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "PinnedStackController");
- pw.println(prefix + " mDefaultAspectRatio=" + mDefaultAspectRatio);
pw.println(prefix + " mIsImeShowing=" + mIsImeShowing);
pw.println(prefix + " mImeHeight=" + mImeHeight);
- pw.println(prefix + " mIsMinimized=" + mIsMinimized);
pw.println(prefix + " mAspectRatio=" + mAspectRatio);
pw.println(prefix + " mMinAspectRatio=" + mMinAspectRatio);
pw.println(prefix + " mMaxAspectRatio=" + mMaxAspectRatio);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index e92bbaa..adafdec 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -26,8 +26,6 @@
import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.BoundsAnimationController.BOUNDS;
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -426,11 +424,6 @@
return;
}
- final DisplayContent dc =
- mService.mRootWindowContainer.getDefaultDisplay().mDisplayContent;
- dc.mBoundsAnimationController.setAnimationType(
- controller.shouldDeferCancelUntilNextTransition() ? FADE_IN : BOUNDS);
-
// We defer canceling the recents animation until the next app transition in the following
// cases:
// 1) The next launching task is not being animated by the recents animation
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6b6b946..54cea93 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -26,7 +26,6 @@
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
@@ -55,6 +54,7 @@
import android.view.SurfaceControl.Transaction;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledFunction;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -125,10 +125,6 @@
// enabled for it to start intercepting touch events.
private boolean mInputConsumerEnabled;
- // Whether or not the recents animation should cause the primary split-screen stack to be
- // minimized
- private boolean mSplitScreenMinimized;
-
private final Rect mTmpRect = new Rect();
private boolean mLinkedToDeathOfRunner;
@@ -231,7 +227,6 @@
mCallbacks.onAnimationFinished(moveHomeToTop
? REORDER_MOVE_TO_TOP
: REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint);
- mDisplayContent.mBoundsAnimationController.setAnimationType(FADE_IN);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -278,30 +273,14 @@
}
@Override
- public void setSplitScreenMinimized(boolean minimized) {
- final long token = Binder.clearCallingIdentity();
- try {
- synchronized (mService.getWindowManagerLock()) {
- if (mCanceled) {
- return;
- }
-
- mSplitScreenMinimized = minimized;
- mService.checkSplitScreenMinimizedChanged(true /* animate */);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
public void hideCurrentInputMethod() {
final long token = Binder.clearCallingIdentity();
try {
final InputMethodManagerInternal inputMethodManagerInternal =
LocalServices.getService(InputMethodManagerInternal.class);
if (inputMethodManagerInternal != null) {
- inputMethodManagerInternal.hideCurrentInputMethod();
+ inputMethodManagerInternal.hideCurrentInputMethod(
+ SoftInputShowHideReason.HIDE_RECENTS_ANIMATION);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -309,14 +288,6 @@
}
@Override
- @Deprecated
- public void setCancelWithDeferredScreenshot(boolean screenshot) {
- synchronized (mService.mGlobalLock) {
- setDeferredCancel(true /* deferred */, screenshot);
- }
- }
-
- @Override
public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
synchronized (mService.mGlobalLock) {
setDeferredCancel(defer, screenshot);
@@ -508,7 +479,7 @@
}
if (mTargetActivityRecord != null) {
- final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>(1);
+ final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>(1);
reasons.put(mTargetActivityRecord, APP_TRANSITION_RECENTS_ANIM);
mService.mAtmService.mStackSupervisor.getActivityMetricsLogger()
.notifyTransitionStarting(reasons);
@@ -746,10 +717,6 @@
}
}
- boolean isSplitScreenMinimized() {
- return mSplitScreenMinimized;
- }
-
boolean isWallpaperVisible(WindowState w) {
return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION &&
((w.mActivityRecord != null && mTargetActivityRecord == w.mActivityRecord)
@@ -952,7 +919,6 @@
pw.print(innerPrefix); pw.println("mPendingAnimations=" + mPendingAnimations.size());
pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
- pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized);
pw.print(innerPrefix); pw.println("mTargetActivityRecord=" + mTargetActivityRecord);
pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper());
pw.print(innerPrefix); pw.println("mRequestDeferCancelUntilNextTransition="
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index d2dbab8..0eb9daf 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -387,7 +387,7 @@
final ActivityRecord topActivity = mWindowContainer.getTopMostActivity();
if (dc.mOpeningApps.contains(topActivity)) {
return RemoteAnimationTarget.MODE_OPENING;
- } else if (dc.mChangingApps.contains(topActivity)) {
+ } else if (dc.mChangingContainers.contains(topActivity)) {
return RemoteAnimationTarget.MODE_CHANGING;
} else {
return RemoteAnimationTarget.MODE_CLOSING;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index aa6bdfd..ada5685 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -258,9 +258,6 @@
*/
final ArrayList<ActivityTaskManagerInternal.SleepToken> mSleepTokens = new ArrayList<>();
- /** Is dock currently minimized. */
- boolean mIsDockMinimized;
-
/** Set when a power hint has started, but not ended. */
private boolean mPowerHintSent;
@@ -1011,9 +1008,7 @@
// Send any pending task-info changes that were queued-up during a layout deferment
mWmService.mAtmService.mTaskOrganizerController.dispatchPendingTaskInfoChanges();
- if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
- "performSurfacePlacementInner exit: animating="
- + mWmService.mAnimator.isAnimating());
+ if (DEBUG_WINDOW_TRACE) Slog.e(TAG, "performSurfacePlacementInner exit");
}
private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
@@ -1989,8 +1984,7 @@
// We dismiss the docked stack whenever we switch users.
final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask();
if (dockedStack != null) {
- mStackSupervisor.moveTasksToFullscreenStackLocked(
- dockedStack, dockedStack.isFocusedStackOnDisplay());
+ getDefaultDisplay().onSplitScreenModeDismissed();
}
// Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
// also cause all tasks to be moved to the fullscreen stack at a position that is
@@ -2149,19 +2143,6 @@
mService.continueWindowLayout();
}
- // TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation.
- // Notify the pinned stack controller to prepare the PiP animation, expect callback
- // delivered from SystemUI to WM to start the animation. Unless we are using
- // the TaskOrganizer in which case the animation will be entirely handled
- // on that side.
- if (mService.mTaskOrganizerController.getTaskOrganizer(WINDOWING_MODE_PINNED)
- == null) {
- final PinnedStackController pinnedStackController =
- display.mDisplayContent.getPinnedStackController();
- pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio,
- null /* stackBounds */);
- }
-
// TODO: revisit the following statement after the animation is moved from WM to SysUI.
// Update the visibility of all activities after the they have been reparented to the new
// stack. This MUST run after the animation above is scheduled to ensure that the windows
@@ -2180,21 +2161,6 @@
}
}
- void setDockedStackMinimized(boolean minimized) {
- // Get currently focused stack before setting mIsDockMinimized. We do this because if
- // split-screen is active, primary stack will not be focusable (see #isFocusable) while
- // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
- final ActivityStack current = getTopDisplayFocusedStack();
- mIsDockMinimized = minimized;
- if (mIsDockMinimized) {
- if (current.inSplitScreenPrimaryWindowingMode()) {
- // The primary split-screen stack can't be focused while it is minimize, so move
- // focus to something else.
- current.adjustFocusToNextFocusableStack("setDockedStackMinimized");
- }
- }
- }
-
ActivityRecord findTask(ActivityRecord r, int preferredDisplayId) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
mTmpFindTaskResult.clear();
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 7164cd8..1e54e69 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -128,7 +128,8 @@
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
- @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback,
+ @Nullable SurfaceFreezer freezer) {
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mAnimation = anim;
mAnimationType = type;
@@ -139,9 +140,14 @@
cancelAnimation();
return;
}
- mLeash = createAnimationLeash(surface, t,
- mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
- mAnimatable.onAnimationLeashCreated(t, mLeash);
+ mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
+ if (mLeash == null) {
+ mLeash = createAnimationLeash(mAnimatable, surface, t,
+ mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
+ 0 /* y */, hidden);
+ mAnimatable.onAnimationLeashCreated(t, mLeash);
+ }
+ mAnimatable.onLeashAnimationStarting(t, mLeash);
if (mAnimationStartDelayed) {
if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
return;
@@ -150,6 +156,12 @@
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
+ @AnimationType int type,
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+ startAnimation(t, anim, hidden, type, animationFinishedCallback, null /* freezer */);
+ }
+
+ void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type) {
startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);
}
@@ -311,15 +323,31 @@
}
private void reset(Transaction t, boolean destroyLeash) {
- final SurfaceControl surface = mAnimatable.getSurfaceControl();
- final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
+ mService.mAnimationTransferMap.remove(mAnimation);
+ mAnimation = null;
+ mAnimationFinishedCallback = null;
+ mAnimationType = ANIMATION_TYPE_NONE;
+ if (mLeash == null) {
+ return;
+ }
+ SurfaceControl leash = mLeash;
+ mLeash = null;
+ final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
+ if (scheduleAnim) {
+ mService.scheduleAnimationLocked();
+ }
+ }
+ static boolean removeLeash(Transaction t, Animatable animatable, @NonNull SurfaceControl leash,
+ boolean destroy) {
boolean scheduleAnim = false;
+ final SurfaceControl surface = animatable.getSurfaceControl();
+ final SurfaceControl parent = animatable.getParentSurfaceControl();
// If the surface was destroyed or the leash is invalid, we don't care to reparent it back.
// Note that we also set this variable to true even if the parent isn't valid anymore, in
// order to ensure onAnimationLeashLost still gets called in this case.
- final boolean reparent = mLeash != null && surface != null;
+ final boolean reparent = surface != null;
if (reparent) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent: " + parent);
// We shouldn't really need these isValid checks but we do
@@ -329,40 +357,30 @@
scheduleAnim = true;
}
}
- mService.mAnimationTransferMap.remove(mAnimation);
- if (mLeash != null && destroyLeash) {
- t.remove(mLeash);
+ if (destroy) {
+ t.remove(leash);
scheduleAnim = true;
}
- mLeash = null;
- mAnimation = null;
- mAnimationFinishedCallback = null;
- mAnimationType = ANIMATION_TYPE_NONE;
if (reparent) {
// Make sure to inform the animatable after the surface was reparented (or reparent
// wasn't possible, but we still need to invoke the callback)
- mAnimatable.onAnimationLeashLost(t);
+ animatable.onAnimationLeashLost(t);
scheduleAnim = true;
}
-
- if (scheduleAnim) {
- mService.scheduleAnimationLocked();
- }
+ return scheduleAnim;
}
- private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
- int height, boolean hidden) {
+ static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
+ Transaction t, int width, int height, int x, int y, boolean hidden) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
- final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
- .setParent(mAnimatable.getAnimationLeashParent())
+ final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
+ .setParent(animatable.getAnimationLeashParent())
.setHidden(hidden)
.setName(surface + " - animation-leash");
final SurfaceControl leash = builder.build();
t.setWindowCrop(leash, width, height);
-
- // TODO: rely on builder.setHidden(hidden) instead of show and setAlpha when b/138459974 is
- // fixed.
+ t.setPosition(leash, x, y);
t.show(leash);
t.setAlpha(leash, hidden ? 0 : 1);
@@ -489,7 +507,8 @@
void commitPendingTransaction();
/**
- * Called when the was created.
+ * Called when the animation leash is created. Note that this is also called by
+ * {@link SurfaceFreezer}, so this doesn't mean we're about to start animating.
*
* @param t The transaction to use to apply any necessary changes.
* @param leash The leash that was created.
@@ -497,6 +516,14 @@
void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
/**
+ * Called when the animator is about to start animating the leash.
+ *
+ * @param t The transaction to use to apply any necessary changes.
+ * @param leash The leash that was created.
+ */
+ default void onLeashAnimationStarting(Transaction t, SurfaceControl leash) { }
+
+ /**
* Called when the leash is being destroyed, or when the leash is being transferred to
* another SurfaceAnimator.
*
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
new file mode 100644
index 0000000..20435ea
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -0,0 +1,245 @@
+/*
+ * 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.wm;
+
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+import com.android.server.protolog.common.ProtoLog;
+
+import java.util.function.Supplier;
+
+/**
+ * This class handles "freezing" of an Animatable. The Animatable in question should implement
+ * Freezable.
+ *
+ * The point of this is to enable WindowContainers to each be capable of freezing themselves.
+ * Freezing means taking a snapshot and placing it above everything in the sub-hierarchy.
+ * The "placing above" requires that a parent surface be inserted above the target surface so that
+ * the target surface and the snapshot are siblings.
+ *
+ * The overall flow for a transition using this would be:
+ * 1. Set transition and record animatable in mChangingApps
+ * 2. Call {@link #freeze} to set-up the leashes and cover with a snapshot.
+ * 3. When transition participants are ready, start SurfaceAnimator with this as a parameter
+ * 4. SurfaceAnimator will then {@link #takeLeashForAnimation} instead of creating another leash.
+ * 5. The animation system should eventually clean this up via {@link #unfreeze}.
+ */
+class SurfaceFreezer {
+
+ private final Freezable mAnimatable;
+ private final WindowManagerService mWmService;
+ private SurfaceControl mLeash;
+ Snapshot mSnapshot = null;
+ final Rect mFreezeBounds = new Rect();
+
+ /**
+ * @param animatable The object to animate.
+ */
+ SurfaceFreezer(Freezable animatable, WindowManagerService service) {
+ mAnimatable = animatable;
+ mWmService = service;
+ }
+
+ /**
+ * Freeze the target surface. This is done by creating a leash (inserting a parent surface
+ * above the target surface) and then taking a snapshot and placing it over the target surface.
+ *
+ * @param startBounds The original bounds (on screen) of the surface we are snapshotting.
+ */
+ void freeze(SurfaceControl.Transaction t, Rect startBounds) {
+ mFreezeBounds.set(startBounds);
+
+ mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(),
+ t, startBounds.width(), startBounds.height(), startBounds.left, startBounds.top,
+ false /* hidden */);
+ mAnimatable.onAnimationLeashCreated(t, mLeash);
+
+ SurfaceControl freezeTarget = mAnimatable.getFreezeSnapshotTarget();
+ if (freezeTarget != null) {
+ GraphicBuffer snapshot = createSnapshotBuffer(freezeTarget, startBounds);
+ if (snapshot != null) {
+ mSnapshot = new Snapshot(mWmService.mSurfaceFactory, t, snapshot, mLeash);
+ }
+ }
+ }
+
+ /**
+ * Used by {@link SurfaceAnimator}. This "transfers" the leash to be used for animation.
+ * By transferring the leash, this will no longer try to clean-up the leash when finished.
+ */
+ SurfaceControl takeLeashForAnimation() {
+ SurfaceControl out = mLeash;
+ mLeash = null;
+ return out;
+ }
+
+ /**
+ * Clean-up the snapshot and remove leash. If the leash was taken, this just cleans-up the
+ * snapshot.
+ */
+ void unfreeze(SurfaceControl.Transaction t) {
+ if (mSnapshot != null) {
+ mSnapshot.destroy(t);
+ }
+ if (mLeash == null) {
+ return;
+ }
+ SurfaceControl leash = mLeash;
+ mLeash = null;
+ final boolean scheduleAnim = SurfaceAnimator.removeLeash(t, mAnimatable, leash,
+ false /* destroy */);
+ if (scheduleAnim) {
+ mWmService.scheduleAnimationLocked();
+ }
+ }
+
+ boolean hasLeash() {
+ return mLeash != null;
+ }
+
+ private static GraphicBuffer createSnapshotBuffer(@NonNull SurfaceControl target,
+ @Nullable Rect bounds) {
+ Rect cropBounds = null;
+ if (bounds != null) {
+ cropBounds = new Rect(bounds);
+ cropBounds.offsetTo(0, 0);
+ }
+ final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
+ SurfaceControl.captureLayers(
+ target, cropBounds, 1.f /* frameScale */, PixelFormat.RGBA_8888);
+ final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer()
+ : null;
+ if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
+ return null;
+ }
+ return buffer;
+ }
+
+ class Snapshot {
+ private SurfaceControl mSurfaceControl;
+ private AnimationAdapter mAnimation;
+ private SurfaceAnimator.OnAnimationFinishedCallback mFinishedCallback;
+
+ /**
+ * @param t Transaction to create the thumbnail in.
+ * @param thumbnailHeader A thumbnail or placeholder for thumbnail to initialize with.
+ */
+ Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t,
+ GraphicBuffer thumbnailHeader, SurfaceControl parent) {
+ Surface drawSurface = surfaceFactory.get();
+ // We can't use a delegating constructor since we need to
+ // reference this::onAnimationFinished
+ final int width = thumbnailHeader.getWidth();
+ final int height = thumbnailHeader.getHeight();
+
+ mSurfaceControl = mAnimatable.makeAnimationLeash()
+ .setName("snapshot anim: " + mAnimatable.toString())
+ .setBufferSize(width, height)
+ .setFormat(PixelFormat.TRANSLUCENT)
+ .setParent(parent)
+ .build();
+
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, " THUMBNAIL %s: CREATE", mSurfaceControl);
+
+ // Transfer the thumbnail to the surface
+ drawSurface.copyFrom(mSurfaceControl);
+ drawSurface.attachAndQueueBuffer(thumbnailHeader);
+ drawSurface.release();
+ t.show(mSurfaceControl);
+
+ // We parent the thumbnail to the container, and just place it on top of anything else
+ // in the container.
+ t.setLayer(mSurfaceControl, Integer.MAX_VALUE);
+ }
+
+ void destroy(SurfaceControl.Transaction t) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+ t.remove(mSurfaceControl);
+ mSurfaceControl = null;
+ }
+
+ /**
+ * Starts an animation.
+ *
+ * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the
+ * component responsible for running the animation. It runs the animation with
+ * {@link AnimationAdapter#startAnimation} once the hierarchy with
+ * the Leash has been set up.
+ * @param animationFinishedCallback The callback being triggered when the animation
+ * finishes.
+ */
+ void startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type,
+ @Nullable SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback) {
+ cancelAnimation(t, true /* restarting */);
+ mAnimation = anim;
+ mFinishedCallback = animationFinishedCallback;
+ if (mSurfaceControl == null) {
+ cancelAnimation(t, false /* restarting */);
+ return;
+ }
+ mAnimation.startAnimation(mSurfaceControl, t, type, animationFinishedCallback);
+ }
+
+ /**
+ * Cancels the animation, and resets the leash.
+ *
+ * @param t The transaction to use for all cancelling surface operations.
+ * @param restarting Whether we are restarting the animation.
+ */
+ private void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) {
+ final SurfaceControl leash = mSurfaceControl;
+ final AnimationAdapter animation = mAnimation;
+ final SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback =
+ mFinishedCallback;
+ mAnimation = null;
+ mFinishedCallback = null;
+ if (animation != null) {
+ animation.onAnimationCancelled(leash);
+ if (!restarting) {
+ if (animationFinishedCallback != null) {
+ animationFinishedCallback.onAnimationFinished(
+ ANIMATION_TYPE_APP_TRANSITION, animation);
+ }
+ }
+ }
+ if (!restarting) {
+ // TODO: do we need to destroy?
+ destroy(t);
+ }
+ }
+ }
+
+ /** freezable */
+ public interface Freezable extends SurfaceAnimator.Animatable {
+ /**
+ * @return The surface to take a snapshot of. If this returns {@code null}, no snapshot
+ * will be generated (but the rest of the freezing logic will still happen).
+ */
+ @Nullable SurfaceControl getFreezeSnapshotTarget();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ecfac1b..27acb23 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -58,6 +58,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
+import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
@@ -78,6 +79,9 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -111,7 +115,6 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.drawable.Icon;
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
@@ -120,11 +123,13 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
-import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.ITaskOrganizer;
+import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -1886,6 +1891,8 @@
.setBounds(mLastNonFullscreenBounds);
}
+ final int prevWinMode = getWindowingMode();
+ mTmpPrevBounds.set(getBounds());
final boolean wasInMultiWindowMode = inMultiWindowMode();
super.onConfigurationChanged(newParentConfig);
if (wasInMultiWindowMode != inMultiWindowMode()) {
@@ -1893,6 +1900,12 @@
updateShadowsRadius(isFocused(), getPendingTransaction());
}
+ final int newWinMode = getWindowingMode();
+ if ((prevWinMode != newWinMode) && (mDisplayContent != null)
+ && shouldStartChangeTransition(prevWinMode, newWinMode)) {
+ initializeChangeTransition(mTmpPrevBounds);
+ }
+
// If the configuration supports persistent bounds (eg. Freeform), keep track of the
// current (non-fullscreen) bounds for persistence.
if (getWindowConfiguration().persistTaskBounds()) {
@@ -1907,6 +1920,63 @@
}
/**
+ * Initializes a change transition. See {@link SurfaceFreezer} for more information.
+ */
+ private void initializeChangeTransition(Rect startBounds) {
+ mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
+ false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
+ mDisplayContent.mChangingContainers.add(this);
+
+ mSurfaceFreezer.freeze(getPendingTransaction(), startBounds);
+ }
+
+ private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
+ if (mWmService.mDisableTransitionAnimation
+ || !isVisible()
+ || getDisplayContent().mAppTransition.isTransitionSet()
+ || getSurfaceControl() == null) {
+ return false;
+ }
+ // Only do an animation into and out-of freeform mode for now. Other mode
+ // transition animations are currently handled by system-ui.
+ return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
+ }
+
+ @VisibleForTesting
+ boolean isInChangeTransition() {
+ return mSurfaceFreezer.hasLeash() || AppTransition.isChangeTransit(mTransit);
+ }
+
+ @Override
+ public SurfaceControl getFreezeSnapshotTarget() {
+ final int transit = mDisplayContent.mAppTransition.getAppTransition();
+ if (!AppTransition.isChangeTransit(transit)) {
+ return null;
+ }
+ // Skip creating snapshot if this transition is controlled by a remote animator which
+ // doesn't need it.
+ final ArraySet<Integer> activityTypes = new ArraySet<>();
+ activityTypes.add(getActivityType());
+ final RemoteAnimationAdapter adapter =
+ mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
+ this, transit, activityTypes);
+ if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
+ return null;
+ }
+ return getSurfaceControl();
+ }
+
+ @Override
+ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ proto.write(USER_ID, mUserId);
+ proto.write(TITLE, intent != null && intent.getComponent() != null
+ ? intent.getComponent().flattenToShortString() : "Task");
+ proto.end(token);
+ }
+
+ /**
* Saves launching state if necessary so that we can launch the activity to its latest state.
* It only saves state if this task has been shown to user and it's in fullscreen or freeform
* mode on freeform displays.
@@ -2038,18 +2108,6 @@
intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
}
- /**
- * Asks docked-divider controller for the smallestwidthdp given bounds.
- * @param bounds bounds to calculate smallestwidthdp for.
- */
- private int getSmallestScreenWidthDpForDockedBounds(Rect bounds) {
- DisplayContent dc = getDisplayContent();
- if (dc != null) {
- return dc.getDockedDividerController().getSmallestWidthDpForBounds(bounds);
- }
- return Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
- }
-
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
@NonNull Configuration parentConfig) {
computeConfigResourceOverrides(inOutConfig, parentConfig, null /* compatInsets */);
@@ -2616,12 +2674,22 @@
if (!isRootTask) {
adjustBoundsForDisplayChangeIfNeeded(dc);
}
+ final DisplayContent prevDc = mDisplayContent;
super.onDisplayChanged(dc);
if (!isRootTask) {
final int displayId = (dc != null) ? dc.getDisplayId() : INVALID_DISPLAY;
mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged(
mTaskId, displayId);
}
+ if (prevDc != null && prevDc.mChangingContainers.remove(this)) {
+ // This gets called *after* this has been reparented to the new display.
+ // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN),
+ // so this token is now "frozen" while waiting for the animation to start on prevDc
+ // (which will be cancelled since the window is no-longer a child). However, since this
+ // is no longer a child of prevDc, this won't be notified of the cancelled animation,
+ // so we need to cancel the change transition here.
+ mSurfaceFreezer.unfreeze(getPendingTransaction());
+ }
}
/**
@@ -2910,8 +2978,7 @@
* we will have a jump at the end.
*/
boolean isFloating() {
- return getWindowConfiguration().tasksAreFloating()
- && !getStack().isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState;
+ return getWindowConfiguration().tasksAreFloating() && !mPreserveNonFloatingState;
}
/**
@@ -3013,15 +3080,6 @@
return forAllTasks((t) -> { return t != this && t.isTaskAnimating(); });
}
- /**
- * @return {@code true} if changing app transition is running.
- */
- @Override
- boolean isChangingAppTransition() {
- final ActivityRecord activity = getTopVisibleActivity();
- return activity != null && getDisplayContent().mChangingApps.contains(activity);
- }
-
@Override
RemoteAnimationTarget createRemoteAnimationTarget(
RemoteAnimationController.RemoteAnimationRecord record) {
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index f715d8f..96a9127 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -37,7 +37,6 @@
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_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4;
- private static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5;
private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6;
private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7;
private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8;
@@ -48,7 +47,6 @@
private static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
private static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14;
private static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15;
- private static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16;
private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17;
private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18;
private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19;
@@ -124,14 +122,6 @@
l.onPinnedActivityRestartAttempt(m.arg1 != 0);
};
- private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> {
- l.onPinnedStackAnimationStarted();
- };
-
- private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> {
- l.onPinnedStackAnimationEnded();
- };
-
private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> {
l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2);
};
@@ -233,12 +223,6 @@
case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG:
forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg);
break;
- case NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG:
- forAllRemoteListeners(mNotifyPinnedStackAnimationStarted, msg);
- break;
- case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG:
- forAllRemoteListeners(mNotifyPinnedStackAnimationEnded, msg);
- break;
case NOTIFY_FORCED_RESIZABLE_MSG:
forAllRemoteListeners(mNotifyActivityForcedResizable, msg);
break;
@@ -386,24 +370,6 @@
msg.sendToTarget();
}
- /** Notifies all listeners when the pinned stack animation starts. */
- void notifyPinnedStackAnimationStarted() {
- mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
- final Message msg =
- mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
- forAllLocalListeners(mNotifyPinnedStackAnimationStarted, msg);
- msg.sendToTarget();
- }
-
- /** Notifies all listeners when the pinned stack animation ends. */
- void notifyPinnedStackAnimationEnded() {
- mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
- final Message msg =
- mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
- forAllLocalListeners(mNotifyPinnedStackAnimationEnded, msg);
- msg.sendToTarget();
- }
-
void notifyActivityDismissingDockedStack() {
mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 6caa27c..0f1e623 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -306,7 +306,8 @@
task.fillTaskInfo(mTmpTaskInfo);
boolean changed = lastInfo == null
|| mTmpTaskInfo.topActivityType != lastInfo.topActivityType
- || mTmpTaskInfo.isResizable() != lastInfo.isResizable();
+ || mTmpTaskInfo.isResizable() != lastInfo.isResizable()
+ || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams;
if (!(changed || force)) {
return;
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 5c73f92..f83b052 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -18,19 +18,18 @@
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static com.android.server.wm.TaskSnapshotPersister.DISABLE_HIGH_RES_BITMAPS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
@@ -89,14 +88,6 @@
@VisibleForTesting
static final int SNAPSHOT_MODE_NONE = 2;
- /**
- * Constant for <code>scaleFactor</code> when calling {@link #snapshotTask} which is
- * interpreted as using the most appropriate scale ratio for the system.
- * This may yield a smaller ratio on low memory devices.
- */
- @VisibleForTesting
- static final float SNAPSHOT_SCALE_AUTO = -1f;
-
private final WindowManagerService mService;
private final TaskSnapshotCache mCache;
@@ -229,7 +220,7 @@
@Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
boolean isLowResolution) {
return mCache.getSnapshot(taskId, userId, restoreFromDisk, isLowResolution
- || DISABLE_HIGH_RES_BITMAPS);
+ && mPersister.enableLowResSnapshots());
}
/**
@@ -273,8 +264,6 @@
* information from the task and populates the builder.
*
* @param task the task to capture
- * @param scaleFraction the scale fraction between 0-1.0, or {@link #SNAPSHOT_SCALE_AUTO}
- * to automatically select
* @param pixelFormat the desired pixel format, or {@link PixelFormat#UNKNOWN} to
* automatically select
* @param builder the snapshot builder to populate
@@ -282,8 +271,7 @@
* @return true if the state of the task is ok to proceed
*/
@VisibleForTesting
- boolean prepareTaskSnapshot(Task task, float scaleFraction, int pixelFormat,
- TaskSnapshot.Builder builder) {
+ boolean prepareTaskSnapshot(Task task, int pixelFormat, TaskSnapshot.Builder builder) {
if (!mService.mPolicy.isScreenOn()) {
if (DEBUG_SCREENSHOT) {
Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
@@ -314,18 +302,6 @@
builder.setId(System.currentTimeMillis());
builder.setContentInsets(getInsets(mainWindow));
- final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
-
- if (scaleFraction == SNAPSHOT_SCALE_AUTO) {
- builder.setScaleFraction(isLowRamDevice
- ? mPersister.getLowResScale()
- : mHighResTaskSnapshotScale);
- builder.setIsLowResolution(isLowRamDevice);
- } else {
- builder.setScaleFraction(scaleFraction);
- builder.setIsLowResolution(scaleFraction < 1.0f);
- }
-
final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0;
@@ -351,13 +327,23 @@
@Nullable
SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
- float scaleFraction) {
- return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888);
+ TaskSnapshot.Builder builder) {
+ Point taskSize = new Point();
+ final SurfaceControl.ScreenshotGraphicBuffer taskSnapshot = createTaskSnapshot(task,
+ mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize);
+ builder.setTaskSize(taskSize);
+ return taskSnapshot;
}
@Nullable
SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
- float scaleFraction, int pixelFormat) {
+ float scaleFraction) {
+ return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888, null);
+ }
+
+ @Nullable
+ SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
+ float scaleFraction, int pixelFormat, Point outTaskSize) {
if (task.getSurfaceControl() == null) {
if (DEBUG_SCREENSHOT) {
Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
@@ -369,6 +355,10 @@
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
SurfaceControl.captureLayers(
task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat);
+ if (outTaskSize != null) {
+ outTaskSize.x = mTmpRect.width();
+ outTaskSize.y = mTmpRect.height();
+ }
final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer()
: null;
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
@@ -379,21 +369,20 @@
@Nullable
TaskSnapshot snapshotTask(Task task) {
- return snapshotTask(task, SNAPSHOT_SCALE_AUTO, PixelFormat.UNKNOWN);
+ return snapshotTask(task, PixelFormat.UNKNOWN);
}
@Nullable
- TaskSnapshot snapshotTask(Task task, float scaleFraction, int pixelFormat) {
+ TaskSnapshot snapshotTask(Task task, int pixelFormat) {
TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
- if (!prepareTaskSnapshot(task, scaleFraction, pixelFormat, builder)) {
+ if (!prepareTaskSnapshot(task, pixelFormat, builder)) {
// Failed some pre-req. Has been logged.
return null;
}
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
- createTaskSnapshot(task, builder.getScaleFraction(),
- builder.getPixelFormat());
+ createTaskSnapshot(task, builder);
if (screenshotBuffer == null) {
// Failed to acquire image. Has been logged.
@@ -472,8 +461,10 @@
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
mHighResTaskSnapshotScale, mainWindow.getRequestedInsetsState());
- final int width = (int) (task.getBounds().width() * mHighResTaskSnapshotScale);
- final int height = (int) (task.getBounds().height() * mHighResTaskSnapshotScale);
+ final int taskWidth = task.getBounds().width();
+ final int taskHeight = task.getBounds().height();
+ final int width = (int) (taskWidth * mHighResTaskSnapshotScale);
+ final int height = (int) (taskHeight * mHighResTaskSnapshotScale);
final RenderNode node = RenderNode.create("TaskSnapshotController", null);
node.setLeftTopRightBottom(0, 0, width, height);
@@ -494,9 +485,9 @@
System.currentTimeMillis() /* id */,
topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(),
hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
- mainWindow.getWindowConfiguration().getRotation(),
- getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* isLowResolution */,
- mHighResTaskSnapshotScale, false /* isRealSnapshot */, task.getWindowingMode(),
+ mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
+ getInsets(mainWindow), false /* isLowResolution */,
+ false /* isRealSnapshot */, task.getWindowingMode(),
getSystemUiVisibility(task), false);
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
index 01f3427..c20ce5f 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -19,6 +19,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.content.ComponentName;
import android.graphics.Bitmap;
@@ -26,6 +27,7 @@
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.GraphicBuffer;
+import android.graphics.Point;
import android.graphics.Rect;
import android.util.Slog;
@@ -52,28 +54,110 @@
mPersister = persister;
}
+ static class PreRLegacySnapshotConfig {
+ /**
+ * If isPreRLegacy is {@code true}, specifies the scale the snapshot was taken at
+ */
+ final float mScale;
+
+ /**
+ * If {@code true}, always load *_reduced.jpg file, no matter what was requested
+ */
+ final boolean mForceLoadReducedJpeg;
+
+ PreRLegacySnapshotConfig(float scale, boolean forceLoadReducedJpeg) {
+ mScale = scale;
+ mForceLoadReducedJpeg = forceLoadReducedJpeg;
+ }
+ }
+
+ /**
+ * When device is upgraded, we might be loading a legacy snapshot. In those cases,
+ * restore the scale based on how it was configured historically. See history of
+ * TaskSnapshotPersister for more information.
+ *
+ * | low_ram=false | low_ram=true
+ * +------------------------------------------------------------------------------+
+ * O | *.jpg = 100%, *_reduced.jpg = 50% |
+ * | +-----------------------------------------|
+ * P | | *.jpg = NONE, *_reduced.jpg = 60% |
+ * +------------------------------------+-----------------------------------------+
+ * Q | *.jpg = proto.scale, | *.jpg = NONE, |
+ * | *_reduced.jpg = 50% * proto.scale | *_reduced.jpg = proto.scale |
+ * +------------------------------------+-----------------------------------------+
+ *
+ * @return null if Android R, otherwise a PreRLegacySnapshotConfig object
+ */
+ PreRLegacySnapshotConfig getLegacySnapshotConfig(int taskWidth, float legacyScale,
+ boolean highResFileExists, boolean loadLowResolutionBitmap) {
+ float preRLegacyScale = 0;
+ boolean forceLoadReducedJpeg = false;
+ boolean isPreRLegacySnapshot = (taskWidth == 0);
+ if (!isPreRLegacySnapshot) {
+ return null;
+ }
+ final boolean isPreQLegacyProto = isPreRLegacySnapshot
+ && (Float.compare(legacyScale, 0f) == 0);
+
+ if (isPreQLegacyProto) {
+ // Android O or Android P
+ if (ActivityManager.isLowRamDeviceStatic() && !highResFileExists) {
+ // Android P w/ low_ram=true
+ preRLegacyScale = 0.6f;
+ // Force bitmapFile to always be *_reduced.jpg
+ forceLoadReducedJpeg = true;
+ } else {
+ // Android O, OR Android P w/ low_ram=false
+ preRLegacyScale = loadLowResolutionBitmap ? 0.5f : 1.0f;
+ }
+ } else if (isPreRLegacySnapshot) {
+ // If not pre-Q but is pre-R, then it must be Android Q
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ preRLegacyScale = legacyScale;
+ // Force bitmapFile to always be *_reduced.jpg
+ forceLoadReducedJpeg = true;
+ } else {
+ preRLegacyScale =
+ loadLowResolutionBitmap ? 0.5f * legacyScale : legacyScale;
+ }
+ }
+ return new PreRLegacySnapshotConfig(preRLegacyScale, forceLoadReducedJpeg);
+ }
+
/**
* Loads a task from the disk.
* <p>
* Do not hold the window manager lock when calling this method, as we directly read data from
* disk here, which might be slow.
*
- * @param taskId The id of the task to load.
- * @param userId The id of the user the task belonged to.
- * @param isLowResolution Whether to load a reduced resolution version of the snapshot.
+ * @param taskId The id of the task to load.
+ * @param userId The id of the user the task belonged to.
+ * @param loadLowResolutionBitmap Whether to load a low resolution resolution version of the
+ * snapshot.
* @return The loaded {@link TaskSnapshot} or {@code null} if it couldn't be loaded.
*/
- TaskSnapshot loadTask(int taskId, int userId, boolean isLowResolution) {
+ TaskSnapshot loadTask(int taskId, int userId, boolean loadLowResolutionBitmap) {
final File protoFile = mPersister.getProtoFile(taskId, userId);
- final File bitmapFile = isLowResolution
- ? mPersister.getLowResolutionBitmapFile(taskId, userId)
- : mPersister.getHighResolutionBitmapFile(taskId, userId);
- if (bitmapFile == null || !protoFile.exists() || !bitmapFile.exists()) {
+ if (!protoFile.exists()) {
return null;
}
try {
final byte[] bytes = Files.readAllBytes(protoFile.toPath());
final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes);
+ final File highResBitmap = mPersister.getHighResolutionBitmapFile(taskId, userId);
+
+ PreRLegacySnapshotConfig legacyConfig = getLegacySnapshotConfig(proto.taskWidth,
+ proto.legacyScale, highResBitmap.exists(), loadLowResolutionBitmap);
+
+ boolean forceLoadReducedJpeg =
+ legacyConfig != null && legacyConfig.mForceLoadReducedJpeg;
+ File bitmapFile = (loadLowResolutionBitmap || forceLoadReducedJpeg)
+ ? mPersister.getLowResolutionBitmapFile(taskId, userId) : highResBitmap;
+
+ if (!bitmapFile.exists()) {
+ return null;
+ }
+
final Options options = new Options();
options.inPreferredConfig = mPersister.use16BitFormat() && !proto.isTranslucent
? Config.RGB_565
@@ -99,13 +183,20 @@
final ComponentName topActivityComponent = ComponentName.unflattenFromString(
proto.topActivityComponent);
- // For legacy snapshots, restore the scale based on the reduced resolution state
- final float legacyScale = isLowResolution ? mPersister.getLowResScale() : 1f;
- final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale;
- return new TaskSnapshot(proto.id, topActivityComponent, buffer, hwBitmap.getColorSpace(),
- proto.orientation, proto.rotation,
+
+ Point taskSize;
+ if (legacyConfig != null) {
+ int taskWidth = (int) ((float) hwBitmap.getWidth() / legacyConfig.mScale);
+ int taskHeight = (int) ((float) hwBitmap.getHeight() / legacyConfig.mScale);
+ taskSize = new Point(taskWidth, taskHeight);
+ } else {
+ taskSize = new Point(proto.taskWidth, proto.taskHeight);
+ }
+
+ return new TaskSnapshot(proto.id, topActivityComponent, buffer,
+ hwBitmap.getColorSpace(), proto.orientation, proto.rotation, taskSize,
new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
- isLowResolution, scale, proto.isRealSnapshot, proto.windowingMode,
+ loadLowResolutionBitmap, proto.isRealSnapshot, proto.windowingMode,
proto.systemUiVisibility, proto.isTranslucent);
} catch (IOException e) {
Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 31212b8..164d3e0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -21,8 +21,8 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.NonNull;
import android.annotation.TestApi;
-import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
@@ -52,8 +52,6 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
private static final String SNAPSHOTS_DIRNAME = "snapshots";
private static final String LOW_RES_FILE_POSTFIX = "_reduced";
- private static final float LOW_RAM_REDUCED_SCALE = .8f;
- static final boolean DISABLE_HIGH_RES_BITMAPS = ActivityManager.isLowRamDeviceStatic();
private static final long DELAY_MS = 100;
private static final int QUALITY = 95;
private static final String PROTO_EXTENSION = ".proto";
@@ -71,7 +69,8 @@
private boolean mStarted;
private final Object mLock = new Object();
private final DirectoryResolver mDirectoryResolver;
- private final float mLowResScale;
+ private final float mLowResScaleFactor;
+ private boolean mEnableLowResSnapshots;
private final boolean mUse16BitFormat;
/**
@@ -83,13 +82,29 @@
TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
mDirectoryResolver = resolver;
+ final float highResTaskSnapshotScale = service.mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_highResTaskSnapshotScale);
+ final float lowResTaskSnapshotScale = service.mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_lowResTaskSnapshotScale);
- if (ActivityManager.isLowRamDeviceStatic()) {
- mLowResScale = LOW_RAM_REDUCED_SCALE;
- } else {
- mLowResScale = service.mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_lowResTaskSnapshotScale);
+ if (lowResTaskSnapshotScale < 0 || 1 <= lowResTaskSnapshotScale) {
+ throw new RuntimeException("Low-res scale must be between 0 and 1");
}
+ if (highResTaskSnapshotScale <= 0 || 1 < highResTaskSnapshotScale) {
+ throw new RuntimeException("High-res scale must be between 0 and 1");
+ }
+ if (highResTaskSnapshotScale <= lowResTaskSnapshotScale) {
+ throw new RuntimeException("High-res scale must be greater than low-res scale");
+ }
+
+ if (lowResTaskSnapshotScale > 0) {
+ mLowResScaleFactor = lowResTaskSnapshotScale / highResTaskSnapshotScale;
+ setEnableLowResSnapshots(true);
+ } else {
+ mLowResScaleFactor = 0;
+ setEnableLowResSnapshots(false);
+ }
+
mUse16BitFormat = service.mContext.getResources().getBoolean(
com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat);
}
@@ -155,13 +170,16 @@
}
}
+ boolean enableLowResSnapshots() {
+ return mEnableLowResSnapshots;
+ }
+
/**
- * Gets the scaling the persister uses for low resolution task snapshots.
- *
- * @return the lowResBitmap scale of task snapshots when they are set to be low res
+ * Not to be used. Only here for testing.
*/
- float getLowResScale() {
- return mLowResScale;
+ @VisibleForTesting
+ void setEnableLowResSnapshots(boolean enabled) {
+ mEnableLowResSnapshots = enabled;
}
/**
@@ -213,14 +231,10 @@
}
File getHighResolutionBitmapFile(int taskId, int userId) {
- // Full sized bitmaps are disabled on low ram devices
- if (DISABLE_HIGH_RES_BITMAPS) {
- Slog.wtf(TAG, "This device does not support full sized resolution bitmaps.");
- return null;
- }
return new File(getDirectory(userId), taskId + BITMAP_EXTENSION);
}
+ @NonNull
File getLowResolutionBitmapFile(int taskId, int userId) {
return new File(getDirectory(userId), taskId + LOW_RES_FILE_POSTFIX + BITMAP_EXTENSION);
}
@@ -234,11 +248,11 @@
final File protoFile = getProtoFile(taskId, userId);
final File bitmapLowResFile = getLowResolutionBitmapFile(taskId, userId);
protoFile.delete();
- bitmapLowResFile.delete();
-
- // Low ram devices do not have a full sized file to delete
- if (!DISABLE_HIGH_RES_BITMAPS) {
- final File bitmapFile = getHighResolutionBitmapFile(taskId, userId);
+ if (bitmapLowResFile.exists()) {
+ bitmapLowResFile.delete();
+ }
+ final File bitmapFile = getHighResolutionBitmapFile(taskId, userId);
+ if (bitmapFile.exists()) {
bitmapFile.delete();
}
}
@@ -343,6 +357,8 @@
final TaskSnapshotProto proto = new TaskSnapshotProto();
proto.orientation = mSnapshot.getOrientation();
proto.rotation = mSnapshot.getRotation();
+ proto.taskWidth = mSnapshot.getTaskSize().x;
+ proto.taskHeight = mSnapshot.getTaskSize().y;
proto.insetLeft = mSnapshot.getContentInsets().left;
proto.insetTop = mSnapshot.getContentInsets().top;
proto.insetRight = mSnapshot.getContentInsets().right;
@@ -352,7 +368,6 @@
proto.systemUiVisibility = mSnapshot.getSystemUiVisibility();
proto.isTranslucent = mSnapshot.isTranslucent();
proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString();
- proto.scale = mSnapshot.getScale();
proto.id = mSnapshot.getId();
final byte[] bytes = TaskSnapshotProto.toByteArray(proto);
final File file = getProtoFile(mTaskId, mUserId);
@@ -379,11 +394,26 @@
}
final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */);
- final Bitmap lowResBitmap = mSnapshot.isLowResolution()
- ? swBitmap
- : Bitmap.createScaledBitmap(swBitmap,
- (int) (bitmap.getWidth() * mLowResScale),
- (int) (bitmap.getHeight() * mLowResScale), true /* filter */);
+
+ final File file = getHighResolutionBitmapFile(mTaskId, mUserId);
+ try {
+ FileOutputStream fos = new FileOutputStream(file);
+ swBitmap.compress(JPEG, QUALITY, fos);
+ fos.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
+ return false;
+ }
+
+ if (!enableLowResSnapshots()) {
+ swBitmap.recycle();
+ return true;
+ }
+
+ final Bitmap lowResBitmap = Bitmap.createScaledBitmap(swBitmap,
+ (int) (bitmap.getWidth() * mLowResScaleFactor),
+ (int) (bitmap.getHeight() * mLowResScaleFactor), true /* filter */);
+ swBitmap.recycle();
final File lowResFile = getLowResolutionBitmapFile(mTaskId, mUserId);
try {
@@ -396,22 +426,6 @@
}
lowResBitmap.recycle();
- // For snapshots with lowResBitmap resolution, do not create or save full sized bitmaps
- if (mSnapshot.isLowResolution()) {
- swBitmap.recycle();
- return true;
- }
-
- final File file = getHighResolutionBitmapFile(mTaskId, mUserId);
- try {
- FileOutputStream fos = new FileOutputStream(file);
- swBitmap.compress(JPEG, QUALITY, fos);
- fos.close();
- } catch (IOException e) {
- Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
- return false;
- }
- swBitmap.recycle();
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index f4e4245..eb005e0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -36,6 +36,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.getColorViewLeftInset;
@@ -53,9 +54,11 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.GraphicBuffer;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -131,6 +134,8 @@
private final Rect mContentInsets = new Rect();
private final Rect mFrame = new Rect();
private TaskSnapshot mSnapshot;
+ private final RectF mTmpSnapshotSize = new RectF();
+ private final RectF mTmpDstFrame = new RectF();
private final CharSequence mTitle;
private boolean mHasDrawn;
private long mShownTime;
@@ -141,6 +146,8 @@
@VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
private final int mOrientationOnCreation;
private final SurfaceControl.Transaction mTransaction;
+ private final Matrix mSnapshotMatrix = new Matrix();
+ private final float[] mTmpFloat9 = new float[9];
static TaskSnapshotSurface create(WindowManagerService service, ActivityRecord activity,
TaskSnapshot snapshot) {
@@ -365,13 +372,17 @@
frame = calculateSnapshotFrame(crop);
mTransaction.setWindowCrop(mChildSurfaceControl, crop);
mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top);
+ mTmpDstFrame.set(frame);
} else {
frame = null;
+ mTmpDstFrame.set(mFrame);
}
// Scale the mismatch dimensions to fill the task bounds
- final float scale = 1 / mSnapshot.getScale();
- mTransaction.setMatrix(mChildSurfaceControl, scale, 0, 0, scale);
+ mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
+ mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
+ mTransaction.setMatrix(mChildSurfaceControl, mSnapshotMatrix, mTmpFloat9);
+
mTransaction.apply();
surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace());
surface.release();
@@ -395,13 +406,17 @@
rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight());
final Rect insets = mSnapshot.getContentInsets();
+ final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x;
+ final float scaleY =
+ (float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y;
+
// Let's remove all system decorations except the status bar, but only if the task is at the
// very top of the screen.
final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
- rect.inset((int) (insets.left * mSnapshot.getScale()),
- isTop ? 0 : (int) (insets.top * mSnapshot.getScale()),
- (int) (insets.right * mSnapshot.getScale()),
- (int) (insets.bottom * mSnapshot.getScale()));
+ rect.inset((int) (insets.left * scaleX),
+ isTop ? 0 : (int) (insets.top * scaleY),
+ (int) (insets.right * scaleX),
+ (int) (insets.bottom * scaleY));
return rect;
}
@@ -412,14 +427,20 @@
*/
@VisibleForTesting
Rect calculateSnapshotFrame(Rect crop) {
- final Rect frame = new Rect(crop);
- final float scale = mSnapshot.getScale();
+ final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x;
+ final float scaleY =
+ (float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y;
// Rescale the frame from snapshot to window coordinate space
- frame.scale(1 / scale);
+ final Rect frame = new Rect(
+ (int) (crop.left / scaleX + 0.5f),
+ (int) (crop.top / scaleY + 0.5f),
+ (int) (crop.right / scaleX + 0.5f),
+ (int) (crop.bottom / scaleY + 0.5f)
+ );
// By default, offset it to to top/left corner
- frame.offsetTo((int) (-crop.left / scale), (int) (-crop.top / scale));
+ frame.offsetTo((int) (-crop.left / scaleX), (int) (-crop.top / scaleY));
// However, we also need to make space for the navigation bar on the left side.
final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left,
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 137d122..669eb78 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -669,8 +669,7 @@
* Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
* the opening apps should be a wallpaper target.
*/
- void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps,
- ArraySet<ActivityRecord> changingApps) {
+ void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps) {
boolean adjust = false;
if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
adjust = true;
@@ -682,15 +681,6 @@
break;
}
}
- if (!adjust) {
- for (int i = changingApps.size() - 1; i >= 0; --i) {
- final ActivityRecord activity = changingApps.valueAt(i);
- if (activity.windowsCanBeWallpaperTarget()) {
- adjust = true;
- break;
- }
- }
- }
}
if (adjust) {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index fd91bc5..b6be386 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -50,7 +50,6 @@
final WindowManagerPolicy mPolicy;
/** Is any window animating? */
- private boolean mAnimating;
private boolean mLastRootAnimating;
final Choreographer.FrameCallback mAnimationFrameCallback;
@@ -135,7 +134,6 @@
synchronized (mService.mGlobalLock) {
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
- mAnimating = false;
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
@@ -160,17 +158,12 @@
final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
dc.checkAppWindowsReadyToShow();
- orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
if (accessibilityController != null) {
accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
mTransaction);
}
}
- if (!mAnimating) {
- cancelAnimation();
- }
-
if (mService.mWatermark != null) {
mService.mWatermark.drawIfNeeded();
}
@@ -220,7 +213,7 @@
executeAfterPrepareSurfacesRunnables();
if (DEBUG_WINDOW_TRACE) {
- Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ Slog.i(TAG, "!!! animate: exit"
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
}
@@ -302,10 +295,6 @@
private class DisplayContentsAnimator {
}
- boolean isAnimating() {
- return mAnimating;
- }
-
boolean isAnimationScheduled() {
return mAnimationFrameCallbackScheduled;
}
@@ -314,14 +303,6 @@
return mChoreographer;
}
- void setAnimating(boolean animating) {
- mAnimating = animating;
- }
-
- void orAnimating(boolean animating) {
- mAnimating |= animating;
- }
-
/**
* Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and
* the corresponding transaction is closed and applied.
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 7a4d0b0..a0a70dc 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -25,9 +25,13 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.os.UserHandle.USER_NULL;
import static android.view.SurfaceControl.Transaction;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
@@ -64,6 +68,7 @@
import android.view.DisplayInfo;
import android.view.IWindowContainer;
import android.view.MagnificationSpec;
+import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
@@ -94,7 +99,7 @@
* changes are made to this class.
*/
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
- implements Comparable<WindowContainer>, Animatable,
+ implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
BLASTSyncEngine.TransactionReadyListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
@@ -169,6 +174,7 @@
* Applied as part of the animation pass in "prepareSurfaces".
*/
protected final SurfaceAnimator mSurfaceAnimator;
+ final SurfaceFreezer mSurfaceFreezer;
protected final WindowManagerService mWmService;
private final Point mTmpPos = new Point();
@@ -252,7 +258,6 @@
* where it represents the starting-state snapshot.
*/
WindowContainerThumbnail mThumbnail;
- final Rect mTransitStartRect = new Rect();
final Point mTmpPoint = new Point();
protected final Rect mTmpRect = new Rect();
final Rect mTmpPrevBounds = new Rect();
@@ -277,6 +282,7 @@
mWmService = wms;
mPendingTransaction = wms.mTransactionFactory.get();
mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
+ mSurfaceFreezer = new SurfaceFreezer(this, wms);
}
@Override
@@ -524,13 +530,6 @@
if (mSurfaceControl != null) {
getPendingTransaction().remove(mSurfaceControl);
-
- // Merge to parent transaction to ensure the transactions on this WindowContainer are
- // applied in native even if WindowContainer is removed.
- if (mParent != null) {
- mParent.getPendingTransaction().merge(getPendingTransaction());
- }
-
setSurfaceControl(null);
mLastSurfacePosition.set(0, 0);
scheduleAnimation();
@@ -841,7 +840,7 @@
* @return {@code true} if the container is in changing app transition.
*/
boolean isChangingAppTransition() {
- return false;
+ return mDisplayContent != null && mDisplayContent.mChangingContainers.contains(this);
}
void sendAppVisibilityToClients() {
@@ -893,6 +892,31 @@
}
/**
+ * Called when the visibility of a child is asked to change. This is before visibility actually
+ * changes (eg. a transition animation might play out first).
+ */
+ void onChildVisibilityRequested(boolean visible) {
+ // If we are changing visibility, then a snapshot isn't necessary and we are no-longer
+ // part of a change transition.
+ mSurfaceFreezer.unfreeze(getPendingTransaction());
+ if (mDisplayContent != null) {
+ mDisplayContent.mChangingContainers.remove(this);
+ }
+ WindowContainer parent = getParent();
+ if (parent != null) {
+ parent.onChildVisibilityRequested(visible);
+ }
+ }
+
+ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ proto.write(USER_ID, USER_NULL);
+ proto.write(TITLE, "WindowContainer");
+ proto.end(token);
+ }
+
+ /**
* Returns {@code true} if this container is focusable. Generally, if a parent is not focusable,
* this will not be focusable either.
*/
@@ -1924,7 +1948,8 @@
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
- mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback);
+ mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
+ mSurfaceFreezer);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@@ -1941,6 +1966,11 @@
}
@Override
+ public SurfaceControl getFreezeSnapshotTarget() {
+ return null;
+ }
+
+ @Override
public Builder makeAnimationLeash() {
return makeSurface().setContainerLayer();
}
@@ -2009,8 +2039,9 @@
getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
if (thumbnailAdapter != null) {
- mThumbnail.startAnimation(
- getPendingTransaction(), thumbnailAdapter, !isVisible());
+ mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(),
+ thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION,
+ (type, anim) -> { });
}
}
} else {
@@ -2056,7 +2087,7 @@
if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
final RemoteAnimationController.RemoteAnimationRecord adapters =
controller.createRemoteAnimationRecord(this, mTmpPoint, mTmpRect,
- (isChanging ? mTransitStartRect : null));
+ (isChanging ? mSurfaceFreezer.mFreezeBounds : null));
resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
} else if (isChanging) {
final float durationScale = mWmService.getTransitionAnimationScaleLocked();
@@ -2064,14 +2095,15 @@
mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y);
final AnimationAdapter adapter = new LocalAnimationAdapter(
- new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, displayInfo,
- durationScale, true /* isAppAnimation */, false /* isThumbnail */),
+ new WindowChangeAnimationSpec(mSurfaceFreezer.mFreezeBounds, mTmpRect,
+ displayInfo, durationScale, true /* isAppAnimation */,
+ false /* isThumbnail */),
getSurfaceAnimationRunner());
- final AnimationAdapter thumbnailAdapter = mThumbnail != null
- ? new LocalAnimationAdapter(new WindowChangeAnimationSpec(mTransitStartRect,
- mTmpRect, displayInfo, durationScale, true /* isAppAnimation */,
- true /* isThumbnail */), getSurfaceAnimationRunner())
+ final AnimationAdapter thumbnailAdapter = mSurfaceFreezer.mSnapshot != null
+ ? new LocalAnimationAdapter(new WindowChangeAnimationSpec(
+ mSurfaceFreezer.mFreezeBounds, mTmpRect, displayInfo, durationScale,
+ true /* isAppAnimation */, true /* isThumbnail */), getSurfaceAnimationRunner())
: null;
resultAdapters = new Pair<>(adapter, thumbnailAdapter);
mTransit = transit;
@@ -2189,6 +2221,7 @@
@Override
public void onAnimationLeashLost(Transaction t) {
mLastLayer = -1;
+ mSurfaceFreezer.unfreeze(t);
reassignLayer(t);
}
@@ -2329,6 +2362,10 @@
mSurfaceControl = sc;
}
+ RemoteAnimationDefinition getRemoteAnimationDefinition() {
+ return null;
+ }
+
/** Cheap way of doing cast and instanceof. */
Task asTask() {
return null;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 240f566..4ac809d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -527,9 +527,9 @@
/**
* Hide IME using imeTargetWindow when requested.
*
- * @param displayId on which IME is shown
+ * @param imeTargetWindowToken token of the (IME target) window on which IME should be hidden.
*/
- public abstract void hideIme(int displayId);
+ public abstract void hideIme(IBinder imeTargetWindowToken);
/**
* Tell window manager about a package that should not be running with high refresh rate
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e0f8f0e..0169a4f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -42,7 +42,6 @@
import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -59,7 +58,6 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
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_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -214,7 +212,6 @@
import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowListener;
import android.view.IDisplayWindowRotationController;
-import android.view.IDockedStackListener;
import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IPinnedStackListener;
@@ -1636,14 +1633,6 @@
}
}
- // If the window is being added to a stack that's currently adjusted for IME,
- // make sure to apply the same adjust to this new window.
- win.applyAdjustForImeIfNeeded();
-
- if (type == TYPE_DOCK_DIVIDER) {
- mRoot.getDisplayContent(displayId).getDockedDividerController().setWindow(win);
- }
-
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
@@ -2816,16 +2805,6 @@
}
}
- void setDockedStackCreateStateLocked(int mode, Rect bounds) {
- mDockedStackCreateMode = mode;
- mDockedStackCreateBounds = bounds;
- }
-
- void checkSplitScreenMinimizedChanged(boolean animate) {
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
- displayContent.getDockedDividerController().checkMinimizeChanged(animate);
- }
-
boolean isValidPictureInPictureAspectRatio(DisplayContent displayContent, float aspectRatio) {
return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio(
aspectRatio);
@@ -3295,10 +3274,6 @@
// Notify whether the docked stack exists for the current user
final DisplayContent displayContent = getDefaultDisplayContentLocked();
- final ActivityStack stack =
- displayContent.getRootSplitScreenPrimaryTask();
- displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(
- stack != null && stack.hasTaskForUser(newUserId));
mRoot.forAllDisplays(dc -> dc.mAppTransition.setCurrentUser(newUserId));
@@ -4687,7 +4662,7 @@
public static final int RESET_ANR_MESSAGE = 38;
public static final int WALLPAPER_DRAW_PENDING_TIMEOUT = 39;
- public static final int UPDATE_DOCKED_STACK_DIVIDER = 41;
+ public static final int UPDATE_MULTI_WINDOW_STACKS = 41;
public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
@@ -4832,7 +4807,7 @@
synchronized (mGlobalLock) {
// Since we're holding both mWindowMap and mAnimator we don't need to
// hold mAnimator.mLayoutToAnim.
- if (mAnimator.isAnimating() || mAnimator.isAnimationScheduled()) {
+ if (mAnimator.isAnimationScheduled()) {
// If we are animating, don't do the gc now but
// delay a bit so we don't interrupt the animation.
sendEmptyMessageDelayed(H.FORCE_GC, 2000);
@@ -4994,11 +4969,10 @@
}
break;
}
- case UPDATE_DOCKED_STACK_DIVIDER: {
+ case UPDATE_MULTI_WINDOW_STACKS: {
synchronized (mGlobalLock) {
final DisplayContent displayContent = getDefaultDisplayContentLocked();
if (displayContent != null) {
- displayContent.getDockedDividerController().reevaluateVisibility(false);
displayContent.adjustForImeIfNeeded();
}
}
@@ -6551,11 +6525,7 @@
@Override
public int getDockedStackSide() {
- synchronized (mGlobalLock) {
- final ActivityStack dockedStack = getDefaultDisplayContentLocked()
- .getRootSplitScreenPrimaryTask();
- return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide();
- }
+ return 0;
}
void setDockedStackResizing(boolean resizing) {
@@ -6572,14 +6542,6 @@
}
}
- @Override
- public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
- synchronized (mGlobalLock) {
- getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer(
- visible, targetWindowingMode, alpha);
- }
- }
-
void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
synchronized (mGlobalLock) {
mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
@@ -6598,17 +6560,6 @@
}
@Override
- public void registerDockedStackListener(IDockedStackListener listener) {
- mAtmInternal.enforceCallerIsRecentsOrHasPermission(REGISTER_WINDOW_MANAGER_LISTENERS,
- "registerDockedStackListener()");
- synchronized (mGlobalLock) {
- // TODO(multi-display): The listener is registered on the default display only.
- getDefaultDisplayContentLocked().mDividerControllerLocked.registerDockedStackListener(
- listener);
- }
- }
-
- @Override
public void registerPinnedStackListener(int displayId, IPinnedStackListener listener) {
if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS,
"registerPinnedStackListener()")) {
@@ -7490,27 +7441,28 @@
return;
}
imeTarget = imeTarget.getImeControlTarget();
-
- final int displayId = imeTarget.getDisplayId();
- mRoot.getDisplayContent(displayId).getInsetsStateController().getImeSourceProvider()
+ imeTarget.getDisplayContent().getInsetsStateController().getImeSourceProvider()
.scheduleShowImePostLayout(imeTarget);
}
}
@Override
- public void hideIme(int displayId) {
+ public void hideIme(IBinder imeTargetWindowToken) {
synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc != null) {
- InsetsControlTarget imeControlTarget = dc.mInputMethodControlTarget;
- if (imeControlTarget == null) {
- return;
- }
- // If there was a pending IME show(), reset it as IME has been
- // requested to be hidden.
- dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
- imeControlTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */);
+ WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
+ if (imeTarget == null) {
+ // The target window no longer exists.
+ return;
}
+ final DisplayContent dc = imeTarget.getImeControlTarget().getDisplayContent();
+ // If there was a pending IME show(), reset it as IME has been
+ // requested to be hidden.
+ dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
+ if (dc.mInputMethodControlTarget == null) {
+ return;
+ }
+ dc.mInputMethodControlTarget.hideInsets(
+ WindowInsets.Type.ime(), true /* fromIme */);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b250083..e452c4a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1078,15 +1078,6 @@
}
final ActivityStack stack = getRootTask();
- if (inPinnedWindowingMode() && stack != null
- && stack.lastAnimatingBoundsWasToFullscreen()) {
- // PIP edge case: When going from pinned to fullscreen, we apply a
- // tempInsetFrame for the full task - but we're still at the start of the animation.
- // To prevent a jump if there's a letterbox, restrict to the parent frame.
- mInsetFrame.intersectUnchecked(windowFrames.mParentFrame);
- windowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame);
- }
-
layoutDisplayFrame = new Rect(windowFrames.mDisplayFrame);
windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame);
layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left;
@@ -1137,7 +1128,6 @@
}
}
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- dc.getDockedDividerController().positionDockedStackedDivider(windowFrames.mFrame);
windowFrames.mContentFrame.set(windowFrames.mFrame);
if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
mMovedByResize = true;
@@ -1343,16 +1333,6 @@
return;
}
- final Task task = getTask();
- // In the case of stack bound animations, the window frames will update (unlike other
- // animations which just modify various transformation properties). We don't want to
- // notify the client of frame changes in this case. Not only is it a lot of churn, but
- // the frame may not correspond to the surface size or the onscreen area at various
- // phases in the animation, and the client will become sad and confused.
- if (task != null && task.getStack().isAnimatingBounds()) {
- return;
- }
-
boolean didFrameInsetsChange = setReportResizeHints();
boolean configChanged = !isLastConfigReportedToClient();
if (DEBUG_CONFIGURATION && configChanged) {
@@ -1956,13 +1936,9 @@
// animating... let's do something.
final int left = mWindowFrames.mFrame.left;
final int top = mWindowFrames.mFrame.top;
- final Task task = getTask();
- final boolean adjustedForMinimizedDockOrIme = task != null
- && (task.getStack().isAdjustedForMinimizedDockedStack()
- || task.getStack().isAdjustedForIme());
if (mToken.okToAnimate()
&& (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
- && !isDragResizing() && !adjustedForMinimizedDockOrIme
+ && !isDragResizing()
&& getWindowConfiguration().hasMovementAnimations()
&& !mWinAnimator.mLastHidden
&& !mSeamlesslyRotated) {
@@ -2269,15 +2245,15 @@
}
final ActivityStack stack = getRootTask();
- if (stack != null && stack.shouldIgnoreInput()) {
+ if (stack != null && !stack.isFocusable()) {
// Ignore when the stack shouldn't receive input event.
// (i.e. the minimized stack in split screen mode.)
return false;
}
- if (PixelFormat.formatHasAlpha(mAttrs.format)) {
- // Support legacy use cases where transparent windows can still be ime target with
- // FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
+ if (PixelFormat.formatHasAlpha(mAttrs.format) && mAttrs.alpha == 0) {
+ // Support legacy use cases where completely transparent windows can still be ime target
+ // with FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
// Certain apps listen for IME insets using transparent windows and ADJUST_NOTHING to
// manually synchronize app content to IME animation b/144619551.
// TODO(b/145812508): remove this once new focus management is complete b/141738570
@@ -2431,13 +2407,6 @@
}
}
- void applyAdjustForImeIfNeeded() {
- final Task task = getTask();
- if (task != null && task.getStack() != null && task.getStack().isAdjustedForIme()) {
- task.getStack().applyAdjustForImeIfNeeded(task);
- }
- }
-
@Override
void switchUser(int userId) {
super.switchUser(userId);
@@ -4327,10 +4296,6 @@
}
}
- if (mAttrs.type == TYPE_INPUT_METHOD) {
- getDisplayContent().mDividerControllerLocked.resetImeHideRequested();
- }
-
return true;
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index a1a9af6..81d0e3e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -902,15 +902,9 @@
boolean allowStretching = false;
task.getStack().getFinalAnimationSourceHintBounds(mTmpSourceBounds);
// If we don't have source bounds, we can attempt to use the content insets
- // in the following scenario:
- // 1. We have content insets.
- // 2. We are not transitioning to full screen
- // We have to be careful to check "lastAnimatingBoundsWasToFullscreen" rather than
- // the mBoundsAnimating state, as we may have already left it and only be here
- // because of the force-scale until resize state.
+ // if we have content insets.
if (mTmpSourceBounds.isEmpty() && (mWin.mLastRelayoutContentInsets.width() > 0
- || mWin.mLastRelayoutContentInsets.height() > 0)
- && !task.getStack().lastAnimatingBoundsWasToFullscreen()) {
+ || mWin.mLastRelayoutContentInsets.height() > 0)) {
mTmpSourceBounds.set(task.getStack().mPreAnimationBounds);
mTmpSourceBounds.inset(mWin.mLastRelayoutContentInsets);
allowStretching = true;
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 5a8e25e4..05aa359 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -355,10 +355,11 @@
}
static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong strength,
- jobject vibration) {
+ jobject vibration, jboolean withCallback) {
if (auto hal = getHal<aidl::IVibrator>()) {
int32_t lengthMs;
- sp<AidlVibratorCallback> effectCallback = new AidlVibratorCallback(env, vibration);
+ sp<AidlVibratorCallback> effectCallback =
+ (withCallback != JNI_FALSE ? new AidlVibratorCallback(env, vibration) : nullptr);
aidl::Effect effectType(static_cast<aidl::Effect>(effect));
aidl::EffectStrength effectStrength(static_cast<aidl::EffectStrength>(strength));
@@ -478,24 +479,24 @@
}
static const JNINativeMethod method_table[] = {
- { "vibratorExists", "()Z", (void*)vibratorExists },
- { "vibratorInit", "()V", (void*)vibratorInit },
- { "vibratorOn", "(J)V", (void*)vibratorOn },
- { "vibratorOff", "()V", (void*)vibratorOff },
- { "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
- { "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
- { "vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;)J",
- (void*)vibratorPerformEffect},
- { "vibratorPerformComposedEffect",
- "([Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/VibratorService$Vibration;)V",
- (void*)vibratorPerformComposedEffect},
- { "vibratorGetSupportedEffects", "()[I",
- (void*)vibratorGetSupportedEffects},
- { "vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
- { "vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
- { "vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities},
- { "vibratorAlwaysOnEnable", "(JJJ)V", (void*)vibratorAlwaysOnEnable},
- { "vibratorAlwaysOnDisable", "(J)V", (void*)vibratorAlwaysOnDisable},
+ {"vibratorExists", "()Z", (void*)vibratorExists},
+ {"vibratorInit", "()V", (void*)vibratorInit},
+ {"vibratorOn", "(J)V", (void*)vibratorOn},
+ {"vibratorOff", "()V", (void*)vibratorOff},
+ {"vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
+ {"vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
+ {"vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;Z)J",
+ (void*)vibratorPerformEffect},
+ {"vibratorPerformComposedEffect",
+ "([Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/"
+ "VibratorService$Vibration;)V",
+ (void*)vibratorPerformComposedEffect},
+ {"vibratorGetSupportedEffects", "()[I", (void*)vibratorGetSupportedEffects},
+ {"vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
+ {"vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
+ {"vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities},
+ {"vibratorAlwaysOnEnable", "(JJJ)V", (void*)vibratorAlwaysOnEnable},
+ {"vibratorAlwaysOnDisable", "(J)V", (void*)vibratorAlwaysOnDisable},
};
int register_android_server_VibratorService(JNIEnv *env) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ff8c209..c1ac55f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -394,6 +394,7 @@
mStartCount = SystemProperties.getInt(SYSPROP_START_COUNT, 0) + 1;
mRuntimeStartElapsedTime = SystemClock.elapsedRealtime();
mRuntimeStartUptime = SystemClock.uptimeMillis();
+ Process.setStartTimes(mRuntimeStartElapsedTime, mRuntimeStartUptime);
// Remember if it's runtime restart(when sys.boot_completed is already set) or reboot
// We don't use "mStartCount > 1" here because it'll be wrong on a FDE device.
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 5d7f4e9..983a639 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -17,6 +17,7 @@
package com.android.server.people;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionSessionId;
@@ -145,14 +146,15 @@
mDataManager.pruneDataForUser(userId, signal);
}
+ @Nullable
@Override
- public byte[] backupConversationInfos(@UserIdInt int userId) {
- return new byte[0];
+ public byte[] getBackupPayload(@UserIdInt int userId) {
+ return mDataManager.getBackupPayload(userId);
}
@Override
- public void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
- @NonNull byte[] payload) {
+ public void restore(@UserIdInt int userId, @NonNull byte[] payload) {
+ mDataManager.restore(userId, payload);
}
@VisibleForTesting
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index 41bc361..dc3fa2a 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -24,6 +24,7 @@
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutInfo.ShortcutFlags;
import android.net.Uri;
+import android.text.TextUtils;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
@@ -31,6 +32,10 @@
import com.android.internal.util.Preconditions;
import com.android.server.people.ConversationInfoProto;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -280,6 +285,25 @@
}
}
+ @Nullable
+ byte[] getBackupPayload() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(baos);
+ try {
+ out.writeUTF(mShortcutId);
+ out.writeUTF(mLocusId != null ? mLocusId.getId() : "");
+ out.writeUTF(mContactUri != null ? mContactUri.toString() : "");
+ out.writeUTF(mNotificationChannelId != null ? mNotificationChannelId : "");
+ out.writeInt(mShortcutFlags);
+ out.writeInt(mConversationFlags);
+ out.writeUTF(mContactPhoneNumber != null ? mContactPhoneNumber : "");
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write fields to backup payload.", e);
+ return null;
+ }
+ return baos.toByteArray();
+ }
+
/** Reads from {@link ProtoInputStream} and constructs a {@link ConversationInfo}. */
@NonNull
static ConversationInfo readFromProto(@NonNull ProtoInputStream protoInputStream)
@@ -331,6 +355,37 @@
return builder.build();
}
+ @Nullable
+ static ConversationInfo readFromBackupPayload(@NonNull byte[] payload) {
+ ConversationInfo.Builder builder = new ConversationInfo.Builder();
+ DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
+ try {
+ builder.setShortcutId(in.readUTF());
+ String locusId = in.readUTF();
+ if (!TextUtils.isEmpty(locusId)) {
+ builder.setLocusId(new LocusId(locusId));
+ }
+ String contactUri = in.readUTF();
+ if (!TextUtils.isEmpty(contactUri)) {
+ builder.setContactUri(Uri.parse(contactUri));
+ }
+ String notificationChannelId = in.readUTF();
+ if (!TextUtils.isEmpty(notificationChannelId)) {
+ builder.setNotificationChannelId(notificationChannelId);
+ }
+ builder.setShortcutFlags(in.readInt());
+ builder.setConversationFlags(in.readInt());
+ String contactPhoneNumber = in.readUTF();
+ if (!TextUtils.isEmpty(contactPhoneNumber)) {
+ builder.setContactPhoneNumber(contactPhoneNumber);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read conversation info fields from backup payload.", e);
+ return null;
+ }
+ return builder.build();
+ }
+
/**
* Builder class for {@link ConversationInfo} objects.
*/
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
index 89c4972..2f2a95c 100644
--- a/services/people/java/com/android/server/people/data/ConversationStore.java
+++ b/services/people/java/com/android/server/people/data/ConversationStore.java
@@ -31,6 +31,10 @@
import com.google.android.collect.Lists;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -48,6 +52,8 @@
private static final String CONVERSATIONS_FILE_NAME = "conversations";
+ private static final int CONVERSATION_INFOS_END_TOKEN = -1;
+
// Shortcut ID -> Conversation Info
@GuardedBy("this")
private final Map<String, ConversationInfo> mConversationInfoMap = new ArrayMap<>();
@@ -195,6 +201,51 @@
mConversationInfosProtoDiskReadWriter.deleteConversationsFile();
}
+ @Nullable
+ synchronized byte[] getBackupPayload() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream conversationInfosOut = new DataOutputStream(baos);
+ for (ConversationInfo conversationInfo : mConversationInfoMap.values()) {
+ byte[] backupPayload = conversationInfo.getBackupPayload();
+ if (backupPayload == null) {
+ continue;
+ }
+ try {
+ conversationInfosOut.writeInt(backupPayload.length);
+ conversationInfosOut.write(backupPayload);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write conversation info to backup payload.", e);
+ return null;
+ }
+ }
+ try {
+ conversationInfosOut.writeInt(CONVERSATION_INFOS_END_TOKEN);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write conversation infos end token to backup payload.", e);
+ return null;
+ }
+ return baos.toByteArray();
+ }
+
+ synchronized void restore(@NonNull byte[] payload) {
+ DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
+ try {
+ for (int conversationInfoSize = in.readInt();
+ conversationInfoSize != CONVERSATION_INFOS_END_TOKEN;
+ conversationInfoSize = in.readInt()) {
+ byte[] conversationInfoPayload = new byte[conversationInfoSize];
+ in.readFully(conversationInfoPayload, 0, conversationInfoSize);
+ ConversationInfo conversationInfo = ConversationInfo.readFromBackupPayload(
+ conversationInfoPayload);
+ if (conversationInfo != null) {
+ addOrUpdate(conversationInfo);
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read conversation info from payload.", e);
+ }
+ }
+
@MainThread
private synchronized void updateConversationsInMemory(
@NonNull ConversationInfo conversationInfo) {
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 3a34c6a..e4ce6ba 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -334,6 +334,25 @@
});
}
+ /** Retrieves a backup payload blob for specified user id. */
+ @Nullable
+ public byte[] getBackupPayload(@UserIdInt int userId) {
+ UserData userData = getUnlockedUserData(userId);
+ if (userData == null) {
+ return null;
+ }
+ return userData.getBackupPayload();
+ }
+
+ /** Attempts to restore data for the specified user id. */
+ public void restore(@UserIdInt int userId, @NonNull byte[] payload) {
+ UserData userData = getUnlockedUserData(userId);
+ if (userData == null) {
+ return;
+ }
+ userData.restore(payload);
+ }
+
private int mimeTypeToShareEventType(String mimeType) {
if (mimeType.startsWith("text/")) {
return Event.TYPE_SHARE_TEXT;
diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java
index 35d245f..3e4c992 100644
--- a/services/people/java/com/android/server/people/data/PackageData.java
+++ b/services/people/java/com/android/server/people/data/PackageData.java
@@ -26,6 +26,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.LocusId;
+import android.os.FileUtils;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -251,5 +252,6 @@
void onDestroy() {
mEventStore.onDestroy();
mConversationStore.onDestroy();
+ FileUtils.deleteContentsAndDir(mPackageDataDir);
}
}
diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java
index 0f8b91b..ed8c595 100644
--- a/services/people/java/com/android/server/people/data/UserData.java
+++ b/services/people/java/com/android/server/people/data/UserData.java
@@ -22,8 +22,14 @@
import android.os.Environment;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.Slog;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.File;
+import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
@@ -31,6 +37,10 @@
/** The data associated with a user profile. */
class UserData {
+ private static final String TAG = UserData.class.getSimpleName();
+
+ private static final int CONVERSATIONS_END_TOKEN = -1;
+
private final @UserIdInt int mUserId;
private final File mPerUserPeopleDataDir;
@@ -125,6 +135,48 @@
return mDefaultSmsApp != null ? getPackageData(mDefaultSmsApp) : null;
}
+ @Nullable
+ byte[] getBackupPayload() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(baos);
+ for (PackageData packageData : mPackageDataMap.values()) {
+ try {
+ byte[] conversationsBackupPayload =
+ packageData.getConversationStore().getBackupPayload();
+ out.writeInt(conversationsBackupPayload.length);
+ out.write(conversationsBackupPayload);
+ out.writeUTF(packageData.getPackageName());
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write conversations to backup payload.", e);
+ return null;
+ }
+ }
+ try {
+ out.writeInt(CONVERSATIONS_END_TOKEN);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write conversations end token to backup payload.", e);
+ return null;
+ }
+ return baos.toByteArray();
+ }
+
+ void restore(@NonNull byte[] payload) {
+ DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
+ try {
+ for (int conversationsPayloadSize = in.readInt();
+ conversationsPayloadSize != CONVERSATIONS_END_TOKEN;
+ conversationsPayloadSize = in.readInt()) {
+ byte[] conversationsPayload = new byte[conversationsPayloadSize];
+ in.readFully(conversationsPayload, 0, conversationsPayloadSize);
+ String packageName = in.readUTF();
+ getOrCreatePackageData(packageName).getConversationStore().restore(
+ conversationsPayload);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to restore conversations from backup payload.", e);
+ }
+ }
+
private PackageData createPackageData(String packageName) {
return new PackageData(packageName, mUserId, this::isDefaultDialer, this::isDefaultSmsApp,
mScheduledExecutorService, mPerUserPeopleDataDir);
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index d2f86ee..602e4e1 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -26,6 +26,8 @@
"services.core",
"services.net",
],
+
+ libs: ["ike-stubs"],
}
//##################################################################
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index ef0ca66..5160eae 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -28,6 +28,8 @@
"services.core",
"services.net",
],
+
+ libs: ["ike-stubs"],
}
//##################################################################
diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
index 5a1ad86..757a2b1 100644
--- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
@@ -696,6 +696,31 @@
mAdbKeyXmlFile.exists());
}
+ @Test
+ public void testAdbKeyStore_removeKey() throws Exception {
+ // Accept the test key with the 'Always allow' option selected.
+ runAdbTest(TEST_KEY_1, true, true, false);
+ runAdbTest(TEST_KEY_2, true, true, false);
+
+ // Set the connection time to 0 to restore the original behavior.
+ setAllowedConnectionTime(0);
+
+ // Verify that the key is in the adb_keys file to ensure subsequent connections are
+ // automatically allowed by adbd.
+ persistKeyStore();
+ assertTrue("The key was not in the adb_keys file after persisting the keystore",
+ isKeyInFile(TEST_KEY_1, mAdbKeyFile));
+ assertTrue("The key was not in the adb_keys file after persisting the keystore",
+ isKeyInFile(TEST_KEY_2, mAdbKeyFile));
+
+ // Now remove one of the keys and make sure the other key is still there
+ mKeyStore.removeKey(TEST_KEY_1);
+ assertFalse("The key was still in the adb_keys file after removing the key",
+ isKeyInFile(TEST_KEY_1, mAdbKeyFile));
+ assertTrue("The key was not in the adb_keys file after removing a different key",
+ isKeyInFile(TEST_KEY_2, mAdbKeyFile));
+ }
+
/**
* Runs an adb test with the provided configuration.
*
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 407f67e..44f4ccf 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
+import android.app.compat.ChangeIdStateCache;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -74,6 +75,7 @@
// Assume userdebug/eng non-final build
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+ ChangeIdStateCache.disable();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 4a686ee..53b90f2 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -62,6 +62,7 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ android.app.compat.ChangeIdStateCache.disable();
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getPackageUid(eq(PACKAGE_NAME), eq(0))).thenThrow(
new PackageManager.NameNotFoundException());
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index feae1e1..08bd1ee 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -144,4 +144,49 @@
Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60);
}
+
+ @Test
+ public void testBrightnessHasLowerPriorityThanUser() {
+ assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
+ assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE);
+
+ int displayId = 0;
+ DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(displayId, votes);
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+
+
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index be873bd..d9101bf 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -60,6 +60,7 @@
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
+import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
@@ -119,7 +120,6 @@
private static final String PLAY_STORE_PKG = "com.android.vending";
private static final String ADB_INSTALLER = "adb";
private static final String PLAY_STORE_CERT = "play_store_cert";
- private static final String ADB_CERT = "";
@org.junit.Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -137,11 +137,12 @@
@Mock
Handler mHandler;
+ private final Context mRealContext = InstrumentationRegistry.getTargetContext();
+
private PackageManager mSpyPackageManager;
private File mTestApk;
private File mTestApkTwoCerts;
- private final Context mRealContext = InstrumentationRegistry.getTargetContext();
// under test
private AppIntegrityManagerServiceImpl mService;
@@ -163,8 +164,7 @@
mPackageManagerInternal,
mRuleEvaluationEngine,
mIntegrityFileManager,
- mHandler,
- /* checkIntegrityForRuleProviders= */ true);
+ mHandler);
mSpyPackageManager = spy(mRealContext.getPackageManager());
// setup mocks to prevent NPE
@@ -172,6 +172,9 @@
when(mMockContext.getResources()).thenReturn(mMockResources);
when(mMockResources.getStringArray(anyInt())).thenReturn(new String[]{});
when(mIntegrityFileManager.initialized()).thenReturn(true);
+ // These are needed to override the Settings.Global.get result.
+ when(mMockContext.getContentResolver()).thenReturn(mRealContext.getContentResolver());
+ setIntegrityCheckIncludesRuleProvider(true);
}
@After
@@ -201,6 +204,7 @@
@Test
public void updateRuleSet_notSystemApp() throws Exception {
whitelistUsAsRuleProvider();
+ makeUsSystemApp(false);
Rule rule =
new Rule(
new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
@@ -411,14 +415,7 @@
public void verifierAsInstaller_skipIntegrityVerification() throws Exception {
whitelistUsAsRuleProvider();
makeUsSystemApp();
- mService =
- new AppIntegrityManagerServiceImpl(
- mMockContext,
- mPackageManagerInternal,
- mRuleEvaluationEngine,
- mIntegrityFileManager,
- mHandler,
- /* checkIntegrityForRuleProviders= */ false);
+ setIntegrityCheckIncludesRuleProvider(false);
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext, atLeastOnce())
@@ -460,12 +457,21 @@
}
private void makeUsSystemApp() throws Exception {
+ makeUsSystemApp(true);
+ }
+
+ private void makeUsSystemApp(boolean isSystemApp) throws Exception {
PackageInfo packageInfo =
mRealContext.getPackageManager().getPackageInfo(TEST_FRAMEWORK_PACKAGE, 0);
- packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ if (isSystemApp) {
+ packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ } else {
+ packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
+ }
doReturn(packageInfo)
.when(mSpyPackageManager)
.getPackageInfo(eq(TEST_FRAMEWORK_PACKAGE), anyInt());
+ when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
}
private Intent makeVerificationIntent() throws Exception {
@@ -492,4 +498,13 @@
intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, VERSION_CODE);
return intent;
}
+
+ private void setIntegrityCheckIncludesRuleProvider(boolean shouldInclude) throws Exception {
+ int value = shouldInclude ? 1 : 0;
+ Settings.Global.putInt(mRealContext.getContentResolver(),
+ Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, value);
+ assertThat(Settings.Global.getInt(mRealContext.getContentResolver(),
+ Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, -1) == 1).isEqualTo(
+ shouldInclude);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
index 233e16c..e08eea2 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
@@ -24,6 +24,7 @@
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
+import java.lang.UnsupportedOperationException
class OverlayActorEnforcerTests {
companion object {
@@ -159,7 +160,7 @@
private val hasPermission: Boolean = false,
private val overlayableInfo: OverlayableInfo? = null,
private vararg val packageNames: String = arrayOf("com.test.actor.one")
- ) : OverlayActorEnforcer.VerifyCallback {
+ ) : OverlayableInfoCallback {
override fun getNamedActors() = if (isActor) {
mapOf(NAMESPACE to mapOf(ACTOR_NAME to ACTOR_PKG_NAME))
@@ -169,7 +170,7 @@
override fun getOverlayableForTarget(
packageName: String,
- targetOverlayableName: String?,
+ targetOverlayableName: String,
userId: Int
) = overlayableInfo
@@ -193,5 +194,9 @@
throw SecurityException()
}
}
+
+ override fun signaturesMatching(pkgName1: String, pkgName2: String, userId: Int): Boolean {
+ throw UnsupportedOperationException()
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index a428a97..cd73432 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -24,30 +24,16 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import android.annotation.NonNull;
import android.content.om.OverlayInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.util.ArraySet;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.content.om.OverlayConfig;
-
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index a753aac..820e61c 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -23,10 +23,13 @@
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
import android.content.om.OverlayInfo.State;
+import android.content.om.OverlayableInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.util.ArraySet;
+import androidx.annotation.Nullable;
+
import com.android.internal.content.om.OverlayConfig;
import org.junit.Before;
@@ -35,6 +38,7 @@
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -281,8 +285,8 @@
}
}
- static final class DummyPackageManagerHelper implements
- OverlayManagerServiceImpl.PackageManagerHelper {
+ static final class DummyPackageManagerHelper implements PackageManagerHelper,
+ OverlayableInfoCallback {
private final DummyDeviceState mState;
private DummyPackageManagerHelper(DummyDeviceState state) {
@@ -320,6 +324,35 @@
.map(p -> getPackageInfo(p.packageName, p.userId))
.collect(Collectors.toList());
}
+
+ @Nullable
+ @Override
+ public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
+ @NonNull String targetOverlayableName, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public String[] getPackagesForUid(int uid) {
+ throw new UnsupportedOperationException();
+ }
+
+ @NonNull
+ @Override
+ public Map<String, Map<String, String>> getNamedActors() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void enforcePermission(String permission, String message) throws SecurityException {
+ throw new UnsupportedOperationException();
+ }
}
static class DummyIdmapManager extends IdmapManager {
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
index d138700..4e63237 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
@@ -284,6 +284,30 @@
assertEquals(in2, out2);
}
+ @Test
+ public void testBackupAndRestore() {
+ ConversationInfo in1 = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+ PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
+ ConversationInfo in2 = buildConversationInfo(SHORTCUT_ID_2, LOCUS_ID_2, CONTACT_URI_2,
+ PHONE_NUMBER_2, NOTIFICATION_CHANNEL_ID_2);
+ mConversationStore.addOrUpdate(in1);
+ mConversationStore.addOrUpdate(in2);
+
+ byte[] backupPayload = mConversationStore.getBackupPayload();
+ assertNotNull(backupPayload);
+
+ ConversationStore conversationStore = new ConversationStore(mFile,
+ mMockScheduledExecutorService);
+ ConversationInfo out1 = conversationStore.getConversation(SHORTCUT_ID);
+ assertNull(out1);
+
+ conversationStore.restore(backupPayload);
+ out1 = conversationStore.getConversation(SHORTCUT_ID);
+ ConversationInfo out2 = conversationStore.getConversation(SHORTCUT_ID_2);
+ assertEquals(in1, out1);
+ assertEquals(in2, out2);
+ }
+
private void resetConversationStore() {
mFile.mkdir();
mMockScheduledExecutorService = new MockScheduledExecutorService();
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index f0b7d20..e57df24 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -653,6 +653,33 @@
assertTrue(activeTimeSlots.isEmpty());
}
+ @Test
+ public void testBackupAndRestoration()
+ throws IntentFilter.MalformedMimeTypeException {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ null);
+ AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
+ .build();
+ AppTargetEvent appTargetEvent =
+ new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
+ .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
+ .build();
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
+
+ mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
+ byte[] payload = mDataManager.getBackupPayload(USER_ID_PRIMARY);
+
+ DataManager dataManager = new DataManager(mContext, mInjector);
+ dataManager.onUserUnlocked(USER_ID_PRIMARY);
+ dataManager.restore(USER_ID_PRIMARY, payload);
+ ConversationInfo conversationInfo = dataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+ .getConversationStore()
+ .getConversation(TEST_SHORTCUT_ID);
+ assertNotNull(conversationInfo);
+ assertEquals(conversationInfo.getShortcutId(), TEST_SHORTCUT_ID);
+ }
+
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
LocalServices.removeServiceForTest(clazz);
LocalServices.addService(clazz, mock);
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 d2ec500..a19d919 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -430,13 +430,6 @@
ParsedProvider b
) {
assertComponentsEqual(a, b);
-
- // Sanity check for ProviderInfo
- ProviderInfo aInfo = PackageInfoUtils.generateProviderInfo(aPkg, a, 0,
- new PackageUserState(), 0, mockPkgSetting(aPkg));
- ProviderInfo bInfo = PackageInfoUtils.generateProviderInfo(bPkg, b, 0,
- new PackageUserState(), 0, mockPkgSetting(bPkg));
- assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
assertEquals(a.getName(), b.getName());
}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index 30ab9cd..dc30add 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -117,7 +117,7 @@
@SmallTest
public void testGetBatterySaverPolicy_PolicyVibration_WithAccessibilityEnabled() {
- mBatterySaverPolicy.setAccessibilityEnabledForTest(true);
+ mBatterySaverPolicy.setAccessibilityEnabled(true);
testServiceDefaultValue_Off(ServiceType.VIBRATION);
}
@@ -339,4 +339,57 @@
Policy.fromSettings(BATTERY_SAVER_CONSTANTS, ""));
verifyBatterySaverConstantsUpdated();
}
+
+ public void testCarModeChanges_Full() {
+ mBatterySaverPolicy.updateConstantsLocked(
+ "gps_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF
+ + ",enable_night_mode=true", "");
+ mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
+ assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+ .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.NIGHT_MODE).batterySaverEnabled);
+
+ mBatterySaverPolicy.setCarModeEnabled(true);
+
+ assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+ .isAnyOf(PowerManager.LOCATION_MODE_NO_CHANGE,
+ PowerManager.LOCATION_MODE_FOREGROUND_ONLY);
+ assertFalse(mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.NIGHT_MODE).batterySaverEnabled);
+
+ mBatterySaverPolicy.setCarModeEnabled(false);
+
+ assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+ .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.NIGHT_MODE).batterySaverEnabled);
+ }
+
+ public void testCarModeChanges_Adaptive() {
+ mBatterySaverPolicy.setAdaptivePolicyLocked(
+ Policy.fromSettings(
+ "gps_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF
+ + ",enable_night_mode=true", ""));
+ mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_ADAPTIVE);
+ assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+ .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.NIGHT_MODE).batterySaverEnabled);
+
+ mBatterySaverPolicy.setCarModeEnabled(true);
+
+ assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+ .isAnyOf(PowerManager.LOCATION_MODE_NO_CHANGE,
+ PowerManager.LOCATION_MODE_FOREGROUND_ONLY);
+ assertFalse(mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.NIGHT_MODE).batterySaverEnabled);
+
+ mBatterySaverPolicy.setCarModeEnabled(false);
+
+ assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+ .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.NIGHT_MODE).batterySaverEnabled);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
new file mode 100644
index 0000000..192c6fe
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright 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.tv.tunerresourcemanager;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.media.tv.ITvInputManager;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService;
+import android.media.tv.tuner.frontend.FrontendSettings;
+import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
+import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
+import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Tests for {@link TunerResourceManagerService} class.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class TunerResourceManagerServiceTest {
+ private static final String TAG = "TunerResourceManagerServiceTest";
+ private Context mContextSpy;
+ @Mock private ITvInputManager mITvInputManagerMock;
+ private TunerResourceManagerService mTunerResourceManagerService;
+ private int mReclaimingId;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ TvInputManager tvInputManager = new TvInputManager(mITvInputManagerMock, 0);
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+ when(mContextSpy.getSystemService(Context.TV_INPUT_SERVICE)).thenReturn(tvInputManager);
+ mTunerResourceManagerService = new TunerResourceManagerService(mContextSpy) {
+ @Override
+ protected void reclaimFrontendResource(int reclaimingId) {
+ mReclaimingId = reclaimingId;
+ }
+ };
+ mTunerResourceManagerService.onStart(true /*isForTesting*/);
+ mReclaimingId = -1;
+ }
+
+ @Test
+ public void setFrontendListTest_addFrontendResources_noExclusiveGroupId() {
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
+ infos[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+ infos[1] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ SparseArray<FrontendResource> resources =
+ mTunerResourceManagerService.getFrontendResources();
+ assertThat(resources.size()).isEqualTo(infos.length);
+ for (int id = 0; id < infos.length; id++) {
+ FrontendResource fe = resources.get(infos[id].getId());
+ assertThat(fe.getId()).isEqualTo(infos[id].getId());
+ assertThat(fe.getType()).isEqualTo(infos[id].getFrontendType());
+ assertThat(fe.getExclusiveGroupId()).isEqualTo(infos[id].getExclusiveGroupId());
+ assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void setFrontendListTest_addFrontendResources_underTheSameExclusiveGroupId() {
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[4];
+ infos[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+ infos[1] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ infos[2] =
+ new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ infos[3] =
+ new TunerFrontendInfo(3 /*id*/, FrontendSettings.TYPE_ATSC, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ SparseArray<FrontendResource> resources =
+ mTunerResourceManagerService.getFrontendResources();
+ assertThat(resources.size()).isEqualTo(infos.length);
+ for (int id = 0; id < infos.length; id++) {
+ FrontendResource fe = resources.get(infos[id].getId());
+ assertThat(fe.getId()).isEqualTo(infos[id].getId());
+ assertThat(fe.getType()).isEqualTo(infos[id].getFrontendType());
+ assertThat(fe.getExclusiveGroupId()).isEqualTo(infos[id].getExclusiveGroupId());
+ }
+
+ assertThat(resources.get(0).getExclusiveGroupMemberFeIds())
+ .isEqualTo(new ArrayList<Integer>());
+ assertThat(resources.get(1).getExclusiveGroupMemberFeIds())
+ .isEqualTo(new ArrayList<Integer>(Arrays.asList(2, 3)));
+ assertThat(resources.get(2).getExclusiveGroupMemberFeIds())
+ .isEqualTo(new ArrayList<Integer>(Arrays.asList(1, 3)));
+ assertThat(resources.get(3).getExclusiveGroupMemberFeIds())
+ .isEqualTo(new ArrayList<Integer>(Arrays.asList(1, 2)));
+ }
+
+ @Test
+ public void setFrontendListTest_updateExistingFrontendResources() {
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
+ infos[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ infos[1] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+ SparseArray<FrontendResource> resources0 =
+ mTunerResourceManagerService.getFrontendResources();
+
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+ SparseArray<FrontendResource> resources1 =
+ mTunerResourceManagerService.getFrontendResources();
+
+ assertThat(resources0).isEqualTo(resources1);
+ }
+
+ @Test
+ public void setFrontendListTest_removeFrontendResources_noExclusiveGroupId() {
+ // Init frontend resources.
+ TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3];
+ infos0[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+ infos0[1] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ infos0[2] =
+ new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 2 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos0);
+
+ TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1];
+ infos1[0] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos1);
+
+ SparseArray<FrontendResource> resources =
+ mTunerResourceManagerService.getFrontendResources();
+ assertThat(resources.size()).isEqualTo(infos1.length);
+ for (int id = 0; id < infos1.length; id++) {
+ FrontendResource fe = resources.get(infos1[id].getId());
+ assertThat(fe.getId()).isEqualTo(infos1[id].getId());
+ assertThat(fe.getType()).isEqualTo(infos1[id].getFrontendType());
+ assertThat(fe.getExclusiveGroupId()).isEqualTo(infos1[id].getExclusiveGroupId());
+ assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void setFrontendListTest_removeFrontendResources_underTheSameExclusiveGroupId() {
+ // Init frontend resources.
+ TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3];
+ infos0[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+ infos0[1] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ infos0[2] =
+ new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos0);
+
+ TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1];
+ infos1[0] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos1);
+
+ SparseArray<FrontendResource> resources =
+ mTunerResourceManagerService.getFrontendResources();
+ assertThat(resources.size()).isEqualTo(infos1.length);
+ for (int id = 0; id < infos1.length; id++) {
+ FrontendResource fe = resources.get(infos1[id].getId());
+ assertThat(fe.getId()).isEqualTo(infos1[id].getId());
+ assertThat(fe.getType()).isEqualTo(infos1[id].getFrontendType());
+ assertThat(fe.getExclusiveGroupId()).isEqualTo(infos1[id].getExclusiveGroupId());
+ assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void requestFrontendTest_ClientNotRegistered() {
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendId = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isFalse();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID);
+ }
+
+ @Test
+ public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() {
+ ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile, null /*listener*/, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[1];
+ infos[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBS, 0 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendId = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isFalse();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID);
+ }
+
+ @Test
+ public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() {
+ ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile, null /*listener*/, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
+ infos[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+ infos[1] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ infos[2] =
+ new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendId = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(0);
+ }
+
+ @Test
+ public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() {
+ ResourceClientProfile profile0 = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ ResourceClientProfile profile1 = new ResourceClientProfile("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId0 = new int[1];
+ int[] clientId1 = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile0, null /*listener*/, clientId0);
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile1, null /*listener*/, clientId1);
+ assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
+ infos[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+ infos[1] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ infos[2] =
+ new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ int[] frontendId = new int[1];
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+
+ request =
+ new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(infos[1].getId());
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId())
+ .isInUse()).isTrue();
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[2].getId())
+ .isInUse()).isTrue();
+ }
+
+ @Test
+ public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority() {
+ // Register clients
+ ResourceClientProfile[] profiles = new ResourceClientProfile[2];
+ profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ profiles[1] = new ResourceClientProfile("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientPriorities = {100, 50};
+ int[] clientId0 = new int[1];
+ int[] clientId1 = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[0], null /*listener*/, clientId0);
+ assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfiles().get(clientId0[0])
+ .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[1], null /*listener*/, clientId1);
+ assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfiles().get(clientId1[0])
+ .setPriority(clientPriorities[1]);
+
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
+ infos[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ infos[1] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendId = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ request =
+ new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isFalse();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(mReclaimingId).isEqualTo(-1);
+
+ request =
+ new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isFalse();
+ assertThat(mReclaimingId).isEqualTo(-1);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Test
+ public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority() {
+ // Register clients
+ ResourceClientProfile[] profiles = new ResourceClientProfile[2];
+ profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ profiles[1] = new ResourceClientProfile("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientPriorities = {100, 500};
+ int[] clientId0 = new int[1];
+ int[] clientId1 = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[0], null /*listener*/, clientId0);
+ assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfiles().get(clientId0[0])
+ .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[1], null /*listener*/, clientId1);
+ assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfiles().get(clientId1[0])
+ .setPriority(clientPriorities[1]);
+
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
+ infos[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ infos[1] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendId = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+
+ request =
+ new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(infos[1].getId());
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId())
+ .isInUse()).isTrue();
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId())
+ .isInUse()).isTrue();
+ assertThat(mTunerResourceManagerService.getFrontendResources()
+ .get(infos[0].getId()).getOwnerClientId()).isEqualTo(clientId1[0]);
+ assertThat(mTunerResourceManagerService.getFrontendResources()
+ .get(infos[1].getId()).getOwnerClientId()).isEqualTo(clientId1[0]);
+ assertThat(mReclaimingId).isEqualTo(clientId0[0]);
+ }
+
+ @Test
+ public void unregisterClientTest_usingFrontend() {
+ // Register client
+ ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile, null /*listener*/, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ // Init frontend resources.
+ TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
+ infos[0] =
+ new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ infos[1] =
+ new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+ TunerFrontendRequest request =
+ new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ int[] frontendId = new int[1];
+ try {
+ assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+ .isTrue();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId())
+ .isInUse()).isTrue();
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId())
+ .isInUse()).isTrue();
+
+ // Unregister client when using frontend
+ mTunerResourceManagerService.unregisterClientProfileInternal(clientId[0]);
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId())
+ .isInUse()).isFalse();
+ assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId())
+ .isInUse()).isFalse();
+
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java
new file mode 100644
index 0000000..ab5665b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 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.tv.tunerresourcemanager;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.media.tv.TvInputService;
+import android.util.Slog;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Tests for {@link UseCasePriorityHints} class.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class UseCasePriorityHintsTest {
+ private static final String TAG = "UseCasePriorityHintsTest";
+ private UseCasePriorityHints mPriorityHints;
+
+ private final String mExampleXML =
+ "<!-- A sample Use Case Priority Hints xml -->"
+ + "<config version=\"1.0\" xmlns:xi=\"http://www.w3.org/2001/XInclude\">"
+ + "<useCaseDefault fgPriority=\"150\" bgPriority=\"50\"/>"
+ + "<useCasePreDefined type=\"USE_CASE_RECORD\" fgPriority=\"600\" bgPriority=\"500\"/>"
+ + "<useCasePreDefined type=\"USE_CASE_LIVE\" fgPriority=\"490\" bgPriority=\"400\"/>"
+ + "<useCasePreDefined type=\"USE_CASE_PLAYBACK\" fgPriority=\"480\""
+ + " bgPriority=\"300\"/>"
+ + "<useCasePreDefined type=\"USE_CASE_BACKGROUND\" fgPriority=\"180\""
+ + " bgPriority=\"100\"/>"
+ + "<useCaseVendor type=\"VENDOR_USE_CASE_1\" id=\"1001\" fgPriority=\"300\""
+ + " bgPriority=\"80\"/>"
+ + "</config>";
+
+ @Before
+ public void setUp() throws Exception {
+ mPriorityHints = new UseCasePriorityHints();
+ try {
+ mPriorityHints.parseInternal(
+ new ByteArrayInputStream(mExampleXML.getBytes(StandardCharsets.UTF_8)));
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "Error parse xml.", e);
+ }
+ }
+
+ @Test
+ public void parseTest_parseSampleXml() {
+ // Pre-defined foreground
+ assertThat(mPriorityHints.getForegroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND)).isEqualTo(180);
+ assertThat(mPriorityHints.getForegroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN)).isEqualTo(150);
+ assertThat(mPriorityHints.getForegroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK)).isEqualTo(480);
+ assertThat(mPriorityHints.getForegroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE)).isEqualTo(490);
+ assertThat(mPriorityHints.getForegroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isEqualTo(600);
+
+ // Pre-defined background
+ assertThat(mPriorityHints.getBackgroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND)).isEqualTo(100);
+ assertThat(mPriorityHints.getBackgroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN)).isEqualTo(50);
+ assertThat(mPriorityHints.getBackgroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK)).isEqualTo(300);
+ assertThat(mPriorityHints.getBackgroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE)).isEqualTo(400);
+ assertThat(mPriorityHints.getBackgroundPriority(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isEqualTo(500);
+
+ // Vendor use case
+ assertThat(mPriorityHints.getForegroundPriority(1001)).isEqualTo(300);
+ assertThat(mPriorityHints.getBackgroundPriority(1001)).isEqualTo(80);
+ }
+
+ @Test
+ public void isDefinedUseCaseTest_invalidUseCase() {
+ assertThat(mPriorityHints.isDefinedUseCase(1992)).isFalse();
+ }
+
+ @Test
+ public void isDefinedUseCaseTest_validUseCase() {
+ assertThat(mPriorityHints.isDefinedUseCase(1001)).isTrue();
+ assertThat(mPriorityHints.isDefinedUseCase(
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isTrue();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 0fdffd5..23613e0 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -76,6 +76,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.SystemService;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import org.junit.Before;
import org.junit.Test;
@@ -87,6 +88,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
@@ -127,6 +129,15 @@
private MyInjector mInjector;
private AppStandbyController mController;
+ private CountDownLatch mStateChangedLatch = new CountDownLatch(1);
+ private AppIdleStateChangeListener mListener = new AppIdleStateChangeListener() {
+ @Override
+ public void onAppIdleStateChanged(String packageName, int userId,
+ boolean idle, int bucket, int reason) {
+ mStateChangedLatch.countDown();
+ }
+ };
+
static class MyContextWrapper extends ContextWrapper {
PackageManager mockPm = mock(PackageManager.class);
@@ -156,6 +167,7 @@
String mBoundWidgetPackage = PACKAGE_EXEMPTED_1;
int[] mRunningUsers = new int[] {USER_ID};
List<UserHandle> mCrossProfileTargets = Collections.emptyList();
+ boolean mDeviceIdleMode = false;
MyInjector(Context context, Looper looper) {
super(context, looper);
@@ -251,7 +263,7 @@
@Override
public boolean isDeviceIdleMode() {
- return false;
+ return mDeviceIdleMode;
}
@Override
@@ -327,6 +339,7 @@
controller.getAppStandbyBucket(PACKAGE_1, USER_ID,
mInjector.mElapsedRealtime, false));
+ controller.addListener(mListener);
return controller;
}
@@ -1055,6 +1068,46 @@
STANDBY_BUCKET_WORKING_SET, getStandbyBucket(USER_ID2, mController, PACKAGE_1));
}
+ @Test
+ public void testUnexemptedSyncScheduled() throws Exception {
+ mStateChangedLatch = new CountDownLatch(1);
+ mController.addListener(mListener);
+ assertEquals("Test package did not start in the Never bucket", STANDBY_BUCKET_NEVER,
+ getStandbyBucket(mController, PACKAGE_1));
+
+ mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false);
+ mStateChangedLatch.await(100, TimeUnit.MILLISECONDS);
+ assertEquals("Unexempted sync scheduled should bring the package out of the Never bucket",
+ STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+
+ setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
+
+ mStateChangedLatch = new CountDownLatch(1);
+ mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false);
+ mStateChangedLatch.await(100, TimeUnit.MILLISECONDS);
+ assertEquals("Unexempted sync scheduled should not elevate a non Never bucket",
+ STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ }
+
+ @Test
+ public void testExemptedSyncScheduled() throws Exception {
+ setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
+ mInjector.mDeviceIdleMode = true;
+ mStateChangedLatch = new CountDownLatch(1);
+ mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true);
+ mStateChangedLatch.await(100, TimeUnit.MILLISECONDS);
+ assertEquals("Exempted sync scheduled in doze should set bucket to working set",
+ STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+
+ setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
+ mInjector.mDeviceIdleMode = false;
+ mStateChangedLatch = new CountDownLatch(1);
+ mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true);
+ mStateChangedLatch.await(100, TimeUnit.MILLISECONDS);
+ assertEquals("Exempted sync scheduled while not in doze should set bucket to active",
+ STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+ }
+
private String getAdminAppsStr(int userId) {
return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
}
@@ -1095,4 +1148,12 @@
private void setActiveAdmins(int userId, String... admins) {
mController.setActiveAdminApps(new ArraySet<>(Arrays.asList(admins)), userId);
}
+
+ private void setAndAssertBucket(String pkg, int user, int bucket, int reason) throws Exception {
+ mStateChangedLatch = new CountDownLatch(1);
+ mController.setAppStandbyBucket(pkg, user, bucket, reason);
+ mStateChangedLatch.await(100, TimeUnit.MILLISECONDS);
+ assertEquals("Failed to set package bucket", bucket,
+ getStandbyBucket(mController, PACKAGE_1));
+ }
}
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index dab0a5f..767857b 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -23,7 +23,6 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
- <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
diff --git a/services/tests/uiservicestests/src/com/android/internal/logging/InstanceIdSequenceFake.java b/services/tests/uiservicestests/src/com/android/internal/logging/InstanceIdSequenceFake.java
index 1629ef0..6e7df05 100644
--- a/services/tests/uiservicestests/src/com/android/internal/logging/InstanceIdSequenceFake.java
+++ b/services/tests/uiservicestests/src/com/android/internal/logging/InstanceIdSequenceFake.java
@@ -17,7 +17,7 @@
package com.android.internal.logging;
/**
- * A fake implementation of InstanceIdSequence that returns 0, 1, 2, ...
+ * A fake implementation of InstanceIdSequence that returns 1, 2, ...
*/
public class InstanceIdSequenceFake extends InstanceIdSequence {
@@ -25,13 +25,13 @@
super(instanceIdMax);
}
- private int mNextId = 0;
+ private int mNextId = 1;
@Override
public InstanceId newInstanceId() {
synchronized (this) {
if (mNextId >= mInstanceIdMax) {
- mNextId = 0;
+ mNextId = 1;
}
return newInstanceIdInternal(mNextId++);
}
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 d0283f7..b6cdbfb 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -153,7 +153,6 @@
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
-import com.android.internal.R;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
@@ -360,7 +359,7 @@
@Before
public void setUp() throws Exception {
// Shell permisssions will override permissions of our app, so add all necessary permissions
- // fo this test here:
+ // for this test here:
InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
"android.permission.WRITE_DEVICE_CONFIG",
"android.permission.READ_DEVICE_CONFIG",
@@ -1164,7 +1163,7 @@
assertEquals(PKG, call.r.getSbn().getPackageName());
assertEquals(0, call.r.getSbn().getId());
assertEquals(tag, call.r.getSbn().getTag());
- assertEquals(0, call.getInstanceId()); // Fake instance IDs are assigned in order
+ assertEquals(1, call.getInstanceId()); // Fake instance IDs are assigned in order
}
@Test
@@ -1186,14 +1185,14 @@
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
mNotificationRecordLogger.get(0).event);
- assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
assertTrue(mNotificationRecordLogger.get(1).shouldLogReported);
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED,
mNotificationRecordLogger.get(1).event);
// Instance ID doesn't change on update of an active notification
- assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
}
@Test
@@ -1248,19 +1247,19 @@
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
mNotificationRecordLogger.get(0).event);
assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
- assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
assertEquals(
NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_APP_CANCEL,
mNotificationRecordLogger.get(1).event);
- assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
mNotificationRecordLogger.get(2).event);
assertTrue(mNotificationRecordLogger.get(2).shouldLogReported);
// New instance ID because notification was canceled before re-post
- assertEquals(1, mNotificationRecordLogger.get(2).getInstanceId());
+ assertEquals(2, mNotificationRecordLogger.get(2).getInstanceId());
}
@Test
@@ -3453,6 +3452,7 @@
@Test
public void testStats_dismissalSurface() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ r.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
mService.addNotification(r);
final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true);
@@ -3470,7 +3470,7 @@
assertEquals(
NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_AOD,
mNotificationRecordLogger.get(0).event);
- assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
}
@Test
@@ -4344,6 +4344,7 @@
final NotificationRecord r = generateNotificationRecord(
mTestNotificationChannel, 1, null, true);
r.setTextChanged(true);
+ r.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
mService.addNotification(r);
mService.mNotificationDelegate.onNotificationVisibilityChanged(new NotificationVisibility[]
@@ -4353,7 +4354,7 @@
assertEquals(1, mNotificationRecordLogger.getCalls().size());
assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_OPEN,
mNotificationRecordLogger.get(0).event);
- assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
mService.mNotificationDelegate.onNotificationVisibilityChanged(
new NotificationVisibility[]{},
@@ -4364,7 +4365,7 @@
assertEquals(2, mNotificationRecordLogger.getCalls().size());
assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLOSE,
mNotificationRecordLogger.get(1).event);
- assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
}
@Test
@@ -6089,7 +6090,9 @@
// Pretend the shortcut exists
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
- shortcutInfos.add(mock(ShortcutInfo.class));
+ ShortcutInfo info = mock(ShortcutInfo.class);
+ when(info.isLongLived()).thenReturn(true);
+ shortcutInfos.add(info);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
// Test: Send the bubble notification
@@ -6116,7 +6119,8 @@
verify(mLauncherApps, times(1)).unregisterCallback(launcherAppsCallback.getValue());
// We're no longer a bubble
- Notification notif2 = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
+ Notification notif2 = mService.getNotificationRecord(
+ nr.getSbn().getKey()).getNotification();
assertFalse(notif2.isBubbleNotification());
}
@@ -6409,11 +6413,6 @@
convos.add(convo2);
when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos);
- // only one valid shortcut
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
- .setPackage(PKG_P)
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
- .setShortcutIds(Arrays.asList(channel1.getConversationId()));
ShortcutInfo si = mock(ShortcutInfo.class);
when(si.getShortLabel()).thenReturn("Hello");
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
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 2d4b5a7..00b9273 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -51,6 +51,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
@@ -203,16 +204,13 @@
return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
}
- private StatusBarNotification getMessagingStyleNotification(@Nullable String shortcutId) {
+ private StatusBarNotification getMessagingStyleNotification() {
final Builder builder = new Builder(mMockContext)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon);
Person person = new Person.Builder().setName("Bob").build();
builder.setStyle(new Notification.MessagingStyle(person));
- if (shortcutId != null) {
- builder.setShortcutId(shortcutId);
- }
Notification n = builder.build();
return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
@@ -1122,16 +1120,18 @@
@Test
public void testIsConversation() {
- StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+ StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(mock(ShortcutInfo.class));
assertTrue(record.isConversation());
}
@Test
- public void testIsConversation_nullShortcutId() {
- StatusBarNotification sbn = getMessagingStyleNotification(null);
+ public void testIsConversation_nullShortcut() {
+ StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(null);
assertFalse(record.isConversation());
}
@@ -1140,25 +1140,28 @@
public void testIsConversation_bypassShortcutFlagEnabled() {
Settings.Global.putString(mContentResolver,
FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
- StatusBarNotification sbn = getMessagingStyleNotification(null);
+ StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(null);
assertTrue(record.isConversation());
}
@Test
public void testIsConversation_channelDemoted() {
- StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+ StatusBarNotification sbn = getMessagingStyleNotification();
channel.setDemoted(true);
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(mock(ShortcutInfo.class));
assertFalse(record.isConversation());
}
@Test
public void testIsConversation_withAdjustmentOverride() {
- StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+ StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(mock(ShortcutInfo.class));
Bundle bundle = new Bundle();
bundle.putBoolean(KEY_NOT_CONVERSATION, true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 27f72a1..e4d50c0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -55,6 +55,8 @@
import android.util.AtomicFile;
import android.util.Pair;
+import androidx.test.InstrumentationRegistry;
+
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
import com.android.server.LocalServices;
@@ -65,6 +67,7 @@
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -116,6 +119,10 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ // Shell permisssions will override permissions of our app, so add all necessary permissions
+ // for this test here:
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+ "android.permission.READ_CONTACTS");
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mock(WindowManagerInternal.class));
@@ -153,6 +160,12 @@
mService.setPreferencesHelper(mPreferencesHelper);
}
+ @After
+ public void tearDown() {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().dropShellPermissionIdentity();
+ }
+
@Test
public void testInit() throws Exception {
List<String> dialer0 = new ArrayList<>();
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 7204a81..e1ce431f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -255,7 +255,7 @@
}
private void notifyTransitionStarting(ActivityRecord activity) {
- final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>();
+ final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>();
reasons.put(activity, ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN);
mActivityMetricsLogger.notifyTransitionStarting(reasons);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index b917e1b..049c8e1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -31,7 +31,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-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.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
@@ -50,7 +49,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -139,39 +137,6 @@
}
@Test
- public void testUpdateLaunchBounds() {
- // When in a non-resizeable stack, the task bounds should be updated.
- final Task task = new TaskBuilder(mService.mStackSupervisor)
- .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
- .build();
- final Rect bounds = new Rect(10, 10, 100, 100);
-
- mStarter.updateBounds(task, bounds);
- assertEquals(bounds, task.getRequestedOverrideBounds());
- assertEquals(new Rect(), task.getStack().getRequestedOverrideBounds());
-
- // When in a resizeable stack, the stack bounds should be updated as well.
- final Task task2 = new TaskBuilder(mService.mStackSupervisor)
- .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
- .build();
- assertThat((Object) task2.getStack()).isInstanceOf(ActivityStack.class);
- mStarter.updateBounds(task2, bounds);
-
- verify(mService, times(1)).animateResizePinnedStack(eq(task2.getRootTaskId()),
- eq(bounds), anyInt());
-
- // In the case of no animation, the stack and task bounds should be set immediately.
- if (!ANIMATE) {
- assertEquals(bounds, task2.getStack().getRequestedOverrideBounds());
- assertEquals(bounds, task2.getRequestedOverrideBounds());
- } else {
- assertEquals(new Rect(), task2.getRequestedOverrideBounds());
- }
- }
-
- @Test
public void testStartActivityPreconditions() {
verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED);
verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT,
@@ -383,7 +348,7 @@
// Never review permissions
doReturn(false).when(mMockPackageManager).isPermissionsReviewRequired(any(), anyInt());
doNothing().when(mMockPackageManager).grantImplicitAccess(
- anyInt(), any(), anyInt(), anyInt());
+ anyInt(), any(), anyInt(), anyInt(), anyBoolean());
doNothing().when(mMockPackageManager).notifyPackageUse(anyString(), anyInt());
final Intent intent = new Intent();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 8c2b293..4cb50c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -103,12 +103,12 @@
setUpOnDisplay(mDisplayContent);
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(1, mDisplayContent.mChangingApps.size());
+ assertEquals(1, mDisplayContent.mChangingContainers.size());
// Verify we are in a change transition, but without a snapshot.
// Though, the test will actually have crashed by now if a snapshot is attempted.
- assertNull(mActivity.getThumbnail());
- assertTrue(mActivity.isInChangeTransition());
+ assertNull(mTask.mSurfaceFreezer.mSnapshot);
+ assertTrue(mTask.isInChangeTransition());
waitUntilHandlersIdle();
mActivity.removeImmediately();
@@ -121,14 +121,14 @@
setUpOnDisplay(mDisplayContent);
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(1, mDisplayContent.mChangingApps.size());
- assertTrue(mActivity.isInChangeTransition());
+ assertEquals(1, mDisplayContent.mChangingContainers.size());
+ assertTrue(mTask.isInChangeTransition());
// Removing the app-token from the display should clean-up the
// the change leash.
mDisplayContent.removeAppToken(mActivity.token);
- assertEquals(0, mDisplayContent.mChangingApps.size());
- assertFalse(mActivity.isInChangeTransition());
+ assertEquals(0, mDisplayContent.mChangingContainers.size());
+ assertFalse(mTask.isInChangeTransition());
waitUntilHandlersIdle();
mActivity.removeImmediately();
@@ -152,8 +152,8 @@
assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode());
// Make sure we're not waiting for a change animation (no leash)
- assertFalse(mActivity.isInChangeTransition());
- assertNull(mActivity.getThumbnail());
+ assertFalse(mTask.isInChangeTransition());
+ assertNull(mActivity.mSurfaceFreezer.mSnapshot);
waitUntilHandlersIdle();
mActivity.removeImmediately();
@@ -165,13 +165,13 @@
setUpOnDisplay(mDisplayContent);
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(1, mDisplayContent.mChangingApps.size());
- assertTrue(mActivity.isInChangeTransition());
+ assertEquals(1, mDisplayContent.mChangingContainers.size());
+ assertTrue(mTask.isInChangeTransition());
// Changing visibility should cancel the change transition and become closing
mActivity.setVisibility(false, false);
- assertEquals(0, mDisplayContent.mChangingApps.size());
- assertFalse(mActivity.isInChangeTransition());
+ assertEquals(0, mDisplayContent.mChangingContainers.size());
+ assertFalse(mTask.isInChangeTransition());
waitUntilHandlersIdle();
mActivity.removeImmediately();
diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
deleted file mode 100644
index 1dda535..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ /dev/null
@@ -1,627 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.server.wm.BoundsAnimationController.BOUNDS;
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
-import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
-import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.filters.SmallTest;
-
-import com.android.server.wm.BoundsAnimationController.BoundsAnimator;
-import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks
- * depending on the various interactions.
- *
- * We are really concerned about only three of the transition states [F = fullscreen, !F = floating]
- * F->!F, !F->!F, and !F->F. Each animation can only be cancelled from the target mid-transition,
- * or if a new animation starts on the same target. The tests below verifies that the target is
- * notified of all the cases where it is animating and cancelled so that it can respond
- * appropriately.
- *
- * Build/Install/Run:
- * atest WmTests:BoundsAnimationControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class BoundsAnimationControllerTests extends WindowTestsBase {
-
- /**
- * Mock value animator to simulate updates with.
- */
- private static class MockValueAnimator extends ValueAnimator {
-
- private float mFraction;
-
- MockValueAnimator getWithValue(float fraction) {
- mFraction = fraction;
- return this;
- }
-
- @Override
- public Object getAnimatedValue() {
- return mFraction;
- }
- }
-
- /**
- * Mock app transition to fire notifications to the bounds animator.
- */
- private static class MockAppTransition extends AppTransition {
-
- private AppTransitionListener mListener;
-
- MockAppTransition(Context context, WindowManagerService wm, DisplayContent displayContent) {
- super(context, wm, displayContent);
- }
-
- @Override
- void registerListenerLocked(AppTransitionListener listener) {
- mListener = listener;
- }
-
- public void notifyTransitionPending() {
- mListener.onAppTransitionPendingLocked();
- }
-
- public void notifyTransitionCancelled(int transit) {
- mListener.onAppTransitionCancelledLocked(transit);
- }
-
- public void notifyTransitionStarting(int transit) {
- mListener.onAppTransitionStartingLocked(transit, 0, 0, 0);
- }
-
- public void notifyTransitionFinished() {
- mListener.onAppTransitionFinishedLocked(null);
- }
- }
-
- /**
- * A test animate bounds user to track callbacks from the bounds animation.
- */
- private static class TestBoundsAnimationTarget implements BoundsAnimationTarget {
-
- boolean mAwaitingAnimationStart;
- boolean mMovedToFullscreen;
- boolean mAnimationStarted;
- boolean mSchedulePipModeChangedOnStart;
- boolean mForcePipModeChangedCallback;
- boolean mAnimationEnded;
- Rect mAnimationEndFinalStackBounds;
- boolean mSchedulePipModeChangedOnEnd;
- boolean mBoundsUpdated;
- boolean mCancelRequested;
- Rect mStackBounds;
- Rect mTaskBounds;
- float mAlpha;
- @BoundsAnimationController.AnimationType int mAnimationType;
-
- void initialize(Rect from) {
- mAwaitingAnimationStart = true;
- mMovedToFullscreen = false;
- mAnimationStarted = false;
- mAnimationEnded = false;
- mAnimationEndFinalStackBounds = null;
- mForcePipModeChangedCallback = false;
- mSchedulePipModeChangedOnStart = false;
- mSchedulePipModeChangedOnEnd = false;
- mStackBounds = from;
- mTaskBounds = null;
- mBoundsUpdated = false;
- }
-
- @Override
- public boolean onAnimationStart(boolean schedulePipModeChangedCallback,
- boolean forceUpdate, @BoundsAnimationController.AnimationType int animationType) {
- mAwaitingAnimationStart = false;
- mAnimationStarted = true;
- mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback;
- mForcePipModeChangedCallback = forceUpdate;
- mAnimationType = animationType;
- return true;
- }
-
- @Override
- public boolean shouldDeferStartOnMoveToFullscreen() {
- return true;
- }
-
- @Override
- public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) {
- // TODO: Once we break the runs apart, we should fail() here if this is called outside
- // of onAnimationStart() and onAnimationEnd()
- if (mCancelRequested) {
- mCancelRequested = false;
- return false;
- } else {
- mBoundsUpdated = true;
- mStackBounds = stackBounds;
- mTaskBounds = taskBounds;
- return true;
- }
- }
-
- @Override
- public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackBounds,
- boolean moveToFullscreen) {
- mAnimationEnded = true;
- mAnimationEndFinalStackBounds = finalStackBounds;
- mSchedulePipModeChangedOnEnd = schedulePipModeChangedCallback;
- mMovedToFullscreen = moveToFullscreen;
- mTaskBounds = null;
- }
-
- @Override
- public boolean setPinnedStackAlpha(float alpha) {
- mAlpha = alpha;
- return true;
- }
- }
-
- /**
- * Drives the animations, makes common assertions along the way.
- */
- private static class BoundsAnimationDriver {
-
- private final BoundsAnimationController mController;
- private final TestBoundsAnimationTarget mTarget;
- private final MockValueAnimator mMockAnimator;
-
- private BoundsAnimator mAnimator;
- private Rect mFrom;
- private Rect mTo;
- private Rect mLargerBounds;
- private Rect mExpectedFinalBounds;
- private @BoundsAnimationController.AnimationType int mAnimationType;
-
- BoundsAnimationDriver(BoundsAnimationController controller,
- TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) {
- mController = controller;
- mTarget = target;
- mMockAnimator = mockValueAnimator;
- }
-
- BoundsAnimationDriver start(Rect from, Rect to,
- @BoundsAnimationController.AnimationType int animationType) {
- if (mAnimator != null) {
- throw new IllegalArgumentException("Call restart() to restart an animation");
- }
-
- boolean fromFullscreen = from.equals(BOUNDS_FULL);
- boolean toFullscreen = to.equals(BOUNDS_FULL);
-
- mTarget.initialize(from);
-
- // Started, not running
- assertTrue(mTarget.mAwaitingAnimationStart);
- assertFalse(mTarget.mAnimationStarted);
-
- startImpl(from, to, animationType);
-
- // Ensure that the animator is paused for the all windows drawn signal when animating
- // to/from fullscreen
- if (fromFullscreen || toFullscreen) {
- assertTrue(mAnimator.isPaused());
- mController.onAllWindowsDrawn();
- } else {
- assertTrue(!mAnimator.isPaused());
- }
-
- // Started and running
- assertFalse(mTarget.mAwaitingAnimationStart);
- assertTrue(mTarget.mAnimationStarted);
-
- return this;
- }
-
- BoundsAnimationDriver restart(Rect to, boolean expectStartedAndPipModeChangedCallback) {
- if (mAnimator == null) {
- throw new IllegalArgumentException("Call start() to start a new animation");
- }
-
- BoundsAnimator oldAnimator = mAnimator;
- boolean toSameBounds = mAnimator.isStarted() && to.equals(mTo);
-
- // Reset the animation start state
- mTarget.mAnimationStarted = false;
-
- // Start animation
- startImpl(mTarget.mStackBounds, to, BOUNDS);
-
- if (toSameBounds) {
- // Same animator if same final bounds
- assertSame(oldAnimator, mAnimator);
- }
-
- if (expectStartedAndPipModeChangedCallback) {
- // Replacing animation with pending pip mode changed callback, ensure we update
- assertTrue(mTarget.mAnimationStarted);
- assertTrue(mTarget.mSchedulePipModeChangedOnStart);
- assertTrue(mTarget.mForcePipModeChangedCallback);
- } else {
- // No animation start for replacing animation
- assertFalse(mTarget.mAnimationStarted);
- }
- mTarget.mAnimationStarted = true;
- return this;
- }
-
- private BoundsAnimationDriver startImpl(Rect from, Rect to,
- @BoundsAnimationController.AnimationType int animationType) {
- boolean fromFullscreen = from.equals(BOUNDS_FULL);
- boolean toFullscreen = to.equals(BOUNDS_FULL);
- mFrom = new Rect(from);
- mTo = new Rect(to);
- mExpectedFinalBounds = new Rect(to);
- mLargerBounds = getLargerBounds(mFrom, mTo);
- mAnimationType = animationType;
-
- // Start animation
- final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen
- ? SCHEDULE_PIP_MODE_CHANGED_ON_START
- : fromFullscreen
- ? SCHEDULE_PIP_MODE_CHANGED_ON_END
- : NO_PIP_MODE_CHANGED_CALLBACKS;
- mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION,
- schedulePipModeChangedState, fromFullscreen, toFullscreen, animationType);
-
- if (animationType == BOUNDS) {
- // Original stack bounds, frozen task bounds
- assertEquals(mFrom, mTarget.mStackBounds);
- assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds);
-
- // Animating to larger size
- if (mFrom.equals(mLargerBounds)) {
- assertFalse(mAnimator.animatingToLargerSize());
- } else if (mTo.equals(mLargerBounds)) {
- assertTrue(mAnimator.animatingToLargerSize());
- }
- }
-
- return this;
- }
-
- BoundsAnimationDriver expectStarted(boolean schedulePipModeChanged) {
- // Callback made
- assertTrue(mTarget.mAnimationStarted);
-
- assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnStart);
- return this;
- }
-
- BoundsAnimationDriver update(float t) {
- mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t));
-
- if (mAnimationType == BOUNDS) {
- // Temporary stack bounds, frozen task bounds
- if (t == 0f) {
- assertEquals(mFrom, mTarget.mStackBounds);
- } else if (t == 1f) {
- assertEquals(mTo, mTarget.mStackBounds);
- } else {
- assertNotEquals(mFrom, mTarget.mStackBounds);
- assertNotEquals(mTo, mTarget.mStackBounds);
- }
- assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds);
- } else {
- assertEquals((float) mMockAnimator.getAnimatedValue(), mTarget.mAlpha, 0.01f);
- }
- return this;
- }
-
- BoundsAnimationDriver cancel() {
- // Cancel
- mTarget.mCancelRequested = true;
- mTarget.mBoundsUpdated = false;
- mExpectedFinalBounds = null;
-
- // Update
- mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(0.5f));
-
- // Not started, not running, cancel reset
- assertFalse(mTarget.mCancelRequested);
-
- // Stack/task bounds not updated
- assertFalse(mTarget.mBoundsUpdated);
-
- // Callback made
- assertTrue(mTarget.mAnimationEnded);
- assertNull(mTarget.mAnimationEndFinalStackBounds);
-
- return this;
- }
-
- BoundsAnimationDriver end() {
- mAnimator.end();
-
- if (mAnimationType == BOUNDS) {
- // Final stack bounds
- assertEquals(mTo, mTarget.mStackBounds);
- assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds);
- assertNull(mTarget.mTaskBounds);
- } else {
- assertEquals(mTarget.mAlpha, 1f, 0.01f);
- }
-
- return this;
- }
-
- BoundsAnimationDriver expectEnded(boolean schedulePipModeChanged,
- boolean moveToFullscreen) {
- // Callback made
- assertTrue(mTarget.mAnimationEnded);
-
- assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnEnd);
- assertEquals(moveToFullscreen, mTarget.mMovedToFullscreen);
- return this;
- }
-
- private static Rect getLargerBounds(Rect r1, Rect r2) {
- int r1Area = r1.width() * r1.height();
- int r2Area = r2.width() * r2.height();
- return (r1Area <= r2Area) ? r2 : r1;
- }
- }
-
- // Constants
- private static final boolean SCHEDULE_PIP_MODE_CHANGED = true;
- private static final boolean MOVE_TO_FULLSCREEN = true;
- private static final int DURATION = 100;
-
- // Some dummy bounds to represent fullscreen and floating bounds
- private static final Rect BOUNDS_FULL = new Rect(0, 0, 100, 100);
- private static final Rect BOUNDS_FLOATING = new Rect(60, 60, 95, 95);
- private static final Rect BOUNDS_SMALLER_FLOATING = new Rect(80, 80, 95, 95);
-
- // Common
- private MockAppTransition mMockAppTransition;
- private TestBoundsAnimationTarget mTarget;
- private BoundsAnimationController mController;
- private BoundsAnimationDriver mDriver;
-
- // Temp
- private static final Rect sTmpRect = new Rect();
-
- @Before
- public void setUp() throws Exception {
- final Context context = getInstrumentation().getTargetContext();
- final Handler handler = new Handler(Looper.getMainLooper());
- mMockAppTransition = new MockAppTransition(context, mWm, mDisplayContent);
- mTarget = new TestBoundsAnimationTarget();
- mController = new BoundsAnimationController(context, mMockAppTransition, handler, null);
- final MockValueAnimator mockValueAnimator = new MockValueAnimator();
- mDriver = new BoundsAnimationDriver(mController, mTarget, mockValueAnimator);
- }
-
- /** BASE TRANSITIONS **/
-
- @UiThreadTest
- @Test
- public void testFullscreenToFloatingTransition() {
- mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
- .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
- .update(0f)
- .update(0.5f)
- .update(1f)
- .end()
- .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
- }
-
- @UiThreadTest
- @Test
- public void testFloatingToFullscreenTransition() {
- mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
- .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
- .update(0f)
- .update(0.5f)
- .update(1f)
- .end()
- .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
- }
-
- @UiThreadTest
- @Test
- public void testFloatingToSmallerFloatingTransition() {
- mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS)
- .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
- .update(0f)
- .update(0.5f)
- .update(1f)
- .end()
- .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
- }
-
- @UiThreadTest
- @Test
- public void testFloatingToLargerFloatingTransition() {
- mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS)
- .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
- .update(0f)
- .update(0.5f)
- .update(1f)
- .end()
- .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
- }
-
- /** F->!F w/ CANCEL **/
-
- @UiThreadTest
- @Test
- public void testFullscreenToFloatingCancelFromTarget() {
- mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
- .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
- .update(0.25f)
- .cancel()
- .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
- }
-
- @UiThreadTest
- @Test
- public void testFullscreenToFloatingCancelFromAnimationToSameBounds() {
- mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
- .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
- .update(0.25f)
- .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */)
- .end()
- .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
- }
-
- @UiThreadTest
- @Test
- public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() {
- mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
- .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
- .update(0.25f)
- .restart(BOUNDS_SMALLER_FLOATING,
- false /* expectStartedAndPipModeChangedCallback */)
- .end()
- .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
- }
-
- @UiThreadTest
- @Test
- public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() {
- // When animating from fullscreen and the animation is interruped, we expect the animation
- // start callback to be made, with a forced pip mode change callback
- mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
- .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
- .update(0.25f)
- .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */)
- .end()
- .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
- }
-
- /** !F->F w/ CANCEL **/
-
- @UiThreadTest
- @Test
- public void testFloatingToFullscreenCancelFromTarget() {
- mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
- .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
- .update(0.25f)
- .cancel()
- .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
- }
-
- @UiThreadTest
- @Test
- public void testFloatingToFullscreenCancelFromAnimationToSameBounds() {
- mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
- .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
- .update(0.25f)
- .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */)
- .end()
- .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
- }
-
- @UiThreadTest
- @Test
- public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() {
- mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
- .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
- .update(0.25f)
- .restart(BOUNDS_SMALLER_FLOATING,
- false /* expectStartedAndPipModeChangedCallback */)
- .end()
- .expectEnded(SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
- }
-
- /** !F->!F w/ CANCEL **/
-
- @UiThreadTest
- @Test
- public void testFloatingToSmallerFloatingCancelFromTarget() {
- mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS)
- .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
- .update(0.25f)
- .cancel()
- .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
- }
-
- @UiThreadTest
- @Test
- public void testFloatingToLargerFloatingCancelFromTarget() {
- mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS)
- .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
- .update(0.25f)
- .cancel()
- .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
- }
-
- @UiThreadTest
- @Test
- public void testFadeIn() {
- mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, FADE_IN)
- .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
- .update(0f)
- .update(0.5f)
- .update(1f)
- .end()
- .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
- }
-
- /** MISC **/
-
- @UiThreadTest
- @Test
- public void testBoundsAreCopied() {
- Rect from = new Rect(0, 0, 100, 100);
- Rect to = new Rect(25, 25, 75, 75);
- mDriver.start(from, to, BOUNDS)
- .update(0.25f)
- .end();
- assertEquals(new Rect(0, 0, 100, 100), from);
- assertEquals(new Rect(25, 25, 75, 75), to);
- }
-
- /**
- * @return whether the task and stack bounds would be the same if they were at the same offset.
- */
- private static boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) {
- sTmpRect.set(taskBounds);
- sTmpRect.offsetTo(stackBounds.left, stackBounds.top);
- return stackBounds.equals(sTmpRect);
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
deleted file mode 100644
index 3206208..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-@SmallTest
-@Presubmit
-public class DockedStackDividerControllerTests {
-
- @Test
- public void testIsDockSideAllowedDockTop() {
- // Docked top is always allowed
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT,
- NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- }
-
- @Test
- public void testIsDockSideAllowedDockBottom() {
- // Cannot dock bottom
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT,
- NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
- }
-
- @Test
- public void testIsDockSideAllowedNavigationBarMovable() {
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
- NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
- NAV_BAR_LEFT, true /* navigationBarCanMove */));
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
- NAV_BAR_RIGHT, true /* navigationBarCanMove */));
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
- NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
- NAV_BAR_RIGHT, true /* navigationBarCanMove */));
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
- NAV_BAR_LEFT, true /* navigationBarCanMove */));
- }
-
- @Test
- public void testIsDockSideAllowedNavigationBarNotMovable() {
- // Navigation bar is not movable such as tablets
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- }
-}
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 4449069..9a898fd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -978,10 +977,7 @@
() -> mService.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
false/* toTop */));
assertNotRestoreTask(
- () -> mService.setTaskWindowingModeSplitScreenPrimary(taskId,
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
- false /* toTop */, false /* animate */, null /* initialBounds */,
- true /* showRecents */));
+ () -> mService.setTaskWindowingModeSplitScreenPrimary(taskId, false /* toTop */));
}
@Test
@@ -1096,18 +1092,10 @@
assertSecurityException(expectCallable,
() -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
assertSecurityException(expectCallable,
- () -> mService.setTaskWindowingModeSplitScreenPrimary(0,
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
+ () -> mService.setTaskWindowingModeSplitScreenPrimary(0, true));
assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
assertSecurityException(expectCallable,
() -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
- assertSecurityException(expectCallable,
- () -> mService.animateResizePinnedStack(INVALID_STACK_ID, new Rect(), -1));
- assertSecurityException(expectCallable,
- () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
- new Rect()));
- assertSecurityException(expectCallable,
- () -> mService.resizePinnedStack(new Rect(), new Rect()));
assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
assertSecurityException(expectCallable,
() -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
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 c9efb70..5c36f5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -333,7 +333,7 @@
assertTrue(mController.isAnimatingTask(activity.getTask()));
// Assume activity transition should animate when no
- // IRecentsAnimationController#setCancelWithDeferredScreenshot called.
+ // IRecentsAnimationController#setDeferCancelUntilNextTransition called.
assertFalse(mController.shouldDeferCancelWithScreenshot());
assertTrue(activity.shouldAnimate(TRANSIT_ACTIVITY_CLOSE));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index b78107e..b3a25302 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -208,7 +208,6 @@
@Test
public void testSetLaunchTaskBehindOfTargetActivity() {
DisplayContent display = mRootWindowContainer.getDefaultDisplay();
- display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class);
ActivityStack homeStack = display.getRootHomeTask();
// Assume the home activity support recents.
ActivityRecord targetActivity = homeStack.getTopNonFinishingActivity();
@@ -254,7 +253,6 @@
@Test
public void testCancelAnimationOnVisibleStackOrderChange() {
DisplayContent display = mService.mRootWindowContainer.getDefaultDisplay();
- display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class);
ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
new ActivityBuilder(mService)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 3a724a1..c7f94ef 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -247,7 +247,7 @@
@Test
public void testChange() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- mDisplayContent.mChangingApps.add(win.mActivityRecord);
+ mDisplayContent.mChangingContainers.add(win.mActivityRecord);
try {
final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(50, 100), new Rect(50, 100, 150, 150),
@@ -290,7 +290,7 @@
verify(mThumbnailFinishedCallback).onAnimationFinished(
eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter));
} finally {
- mDisplayContent.mChangingApps.clear();
+ mDisplayContent.mChangingContainers.clear();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index ec20262..0ef2582 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -52,7 +52,6 @@
import static org.mockito.ArgumentMatchers.refEq;
import android.app.ActivityOptions;
-import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -251,13 +250,12 @@
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(stack).build();
- // Under split screen primary we should be focusable when not minimized
- mRootWindowContainer.setDockedStackMinimized(false);
+ // Created stacks are focusable by default.
assertTrue(stack.isTopActivityFocusable());
assertTrue(activity.isFocusable());
- // Under split screen primary we should not be focusable when minimized
- mRootWindowContainer.setDockedStackMinimized(true);
+ // If the stack is made unfocusable, its activities should inherit that.
+ stack.setFocusable(false);
assertFalse(stack.isTopActivityFocusable());
assertFalse(activity.isFocusable());
@@ -308,33 +306,6 @@
}
/**
- * Verify split-screen primary stack & task can resized by
- * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
- */
- @Test
- public void testResizeDockedStackForSplitScreenPrimary() {
- final Rect configSize = new Rect(0, 0, 1000, 1000);
- final Rect displayedSize = new Rect(0, 0, 300, 300);
-
- // Create primary split-screen stack with a task.
- final ActivityStack primaryStack = new StackBuilder(mRootWindowContainer)
- .setActivityType(ACTIVITY_TYPE_STANDARD)
- .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
- .setOnTop(true)
- .build();
- final Task task = primaryStack.getTopMostTask();
-
- // Resize dock stack.
- mService.resizeDockedStack(displayedSize, configSize, null, null, null);
-
- // Verify dock stack & its task bounds if is equal as resized result.
- assertEquals(displayedSize, primaryStack.getDisplayedBounds());
- assertEquals(displayedSize, primaryStack.getDisplayedBounds());
- assertEquals(configSize, primaryStack.getBounds());
- assertEquals(configSize, task.getBounds());
- }
-
- /**
* Verify that home stack would be moved to front when the top activity is Recents.
*/
@Test
@@ -517,7 +488,7 @@
*/
@Test
public void testStartHomeOnAllDisplays() {
- mockResolveHomeActivity();
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
mockResolveSecondaryHomeActivity();
// Create secondary displays.
@@ -644,30 +615,26 @@
}
/**
- * Tests that secondary home should be selected if default home not set.
+ * Tests that secondary home should be selected if primary home not set.
*/
@Test
- public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() {
- final Intent defaultHomeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = ResolverActivity.class.getName();
- doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(defaultHomeIntent));
+ public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() {
+ // Setup: primary home not set.
+ final Intent primaryHomeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoPrimary = new ActivityInfo();
+ aInfoPrimary.name = ResolverActivity.class.getName();
+ doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+ refEq(primaryHomeIntent));
+ // Setup: set secondary home.
+ mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
- final String secondaryHomeComponent = mService.mContext.getResources().getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
- final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = comp.getClassName();
- doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(secondaryHomeIntent));
-
- // Should fallback to secondary home if default home not set.
+ // Run the test.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
.resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
-
- assertEquals(comp.getClassName(), resolvedInfo.first.name);
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
}
/**
@@ -675,103 +642,60 @@
* config_useSystemProvidedLauncherForSecondary.
*/
@Test
- public void testResolveSecondaryHomeActivityForced() throws Exception {
- Resources resources = mContext.getResources();
- spyOn(resources);
- try {
- // setUp: set secondary launcher and force it.
- final String defaultSecondaryHome =
- "com.android.test/com.android.test.TestDefaultSecondaryHome";
- final ComponentName secondaryComp = ComponentName.unflattenFromString(
- defaultSecondaryHome);
- doReturn(defaultSecondaryHome).when(resources).getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- doReturn(true).when(resources).getBoolean(
- com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
- final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
- assertEquals(secondaryComp, secondaryHomeIntent.getComponent());
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = secondaryComp.getClassName();
- aInfoSecondary.applicationInfo = new ApplicationInfo();
- aInfoSecondary.applicationInfo.packageName = secondaryComp.getPackageName();
- doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(secondaryHomeIntent));
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = "fakeHomeActivity";
- aInfoDefault.applicationInfo = new ApplicationInfo();
- aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
- doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(homeIntent));
- // Let resolveActivities call to validate both main launcher and second launcher so that
- // resolveActivities call does not work as enabler for secondary.
- final List<ResolveInfo> resolutions1 = new ArrayList<>();
- final ResolveInfo resolveInfo1 = new ResolveInfo();
- resolveInfo1.activityInfo = new ActivityInfo();
- resolveInfo1.activityInfo.name = aInfoDefault.name;
- resolveInfo1.activityInfo.applicationInfo = aInfoDefault.applicationInfo;
- resolutions1.add(resolveInfo1);
- doReturn(resolutions1).when(mRootWindowContainer).resolveActivities(anyInt(),
- refEq(homeIntent));
- final List<ResolveInfo> resolutions2 = new ArrayList<>();
- final ResolveInfo resolveInfo2 = new ResolveInfo();
- resolveInfo2.activityInfo = new ActivityInfo();
- resolveInfo2.activityInfo.name = aInfoSecondary.name;
- resolveInfo2.activityInfo.applicationInfo = aInfoSecondary.applicationInfo;
- resolutions2.add(resolveInfo2);
- doReturn(resolutions2).when(mRootWindowContainer).resolveActivities(anyInt(),
- refEq(secondaryHomeIntent));
- doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
- any(), anyInt(), anyBoolean());
-
- // Run the test
- final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
- .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
- assertEquals(secondaryComp.getClassName(), resolvedInfo.first.name);
- assertEquals(secondaryComp.getPackageName(),
- resolvedInfo.first.applicationInfo.packageName);
- assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
- } finally {
- // tearDown
- reset(resources);
- }
- }
-
- /**
- * Tests that secondary home should be selected if default home not support secondary displays
- * or there is no matched activity in the same package as selected default home.
- */
- @Test
- public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() {
- mockResolveHomeActivity();
-
+ public void testResolveSecondaryHomeActivityForced() {
+ // SetUp: set primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ // SetUp: set secondary home and force it.
+ mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */);
+ final Intent secondaryHomeIntent =
+ mService.getSecondaryHomeIntent(null /* preferredPackage */);
final List<ResolveInfo> resolutions = new ArrayList<>();
- doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
-
- final String secondaryHomeComponent = mService.mContext.getResources().getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
- final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = comp.getClassName();
- doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+ resolveInfo.activityInfo = aInfoSecondary;
+ resolutions.add(resolveInfo);
+ doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(),
refEq(secondaryHomeIntent));
+ doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
+ any(), anyInt(), anyBoolean());
- // Should fallback to secondary home if selected default home not support secondary displays
- // or there is no matched activity in the same package as selected default home.
+ // Run the test.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
.resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
-
- assertEquals(comp.getClassName(), resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
}
/**
- * Tests that default home activity should be selected if it already support secondary displays.
+ * Tests that secondary home should be selected if primary home not support secondary displays
+ * or there is no matched activity in the same package as selected primary home.
*/
@Test
- public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() {
- final ActivityInfo aInfoDefault = mockResolveHomeActivity();
+ public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() {
+ // Setup: there is no matched activity in the same package as selected primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
+ // Setup: set secondary home.
+ mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
+ // Run the test.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
+ }
+ /**
+ * Tests that primary home activity should be selected if it already support secondary displays.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() {
+ // SetUp: set primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ // SetUp: put primary home info on 2nd item
final List<ResolveInfo> resolutions = new ArrayList<>();
final ResolveInfo infoFake1 = new ResolveInfo();
infoFake1.activityInfo = new ActivityInfo();
@@ -779,7 +703,8 @@
infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
final ResolveInfo infoFake2 = new ResolveInfo();
- infoFake2.activityInfo = aInfoDefault;
+ final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */);
+ infoFake2.activityInfo = aInfoPrimary;
resolutions.add(infoFake1);
resolutions.add(infoFake2);
doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
@@ -787,13 +712,12 @@
doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
any(), anyInt(), anyBoolean());
- // Use default home activity if it support secondary displays.
+ // Run the test.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
.resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
-
- assertEquals(aInfoDefault.applicationInfo.packageName,
+ assertEquals(aInfoPrimary.name, resolvedInfo.first.name);
+ assertEquals(aInfoPrimary.applicationInfo.packageName,
resolvedInfo.first.applicationInfo.packageName);
- assertEquals(aInfoDefault.name, resolvedInfo.first.name);
}
/**
@@ -801,8 +725,9 @@
*/
@Test
public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
- mockResolveHomeActivity();
-
+ // SetUp: set primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ // Setup: prepare two eligible activity info.
final List<ResolveInfo> resolutions = new ArrayList<>();
final ResolveInfo infoFake1 = new ResolveInfo();
infoFake1.activityInfo = new ActivityInfo();
@@ -821,7 +746,7 @@
doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
any(), anyInt(), anyBoolean());
- // Use the first one of matched activities in the same package as selected default home.
+ // Use the first one of matched activities in the same package as selected primary home.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
.resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
@@ -884,32 +809,48 @@
/**
* Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
- * info for test cases (the original implementation will resolve from the real package manager).
+ * info for test cases.
+ *
+ * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use
+ * secondary home intent.
+ * @param forceSystemProvided Indicate to force using system provided home activity.
*/
- private ActivityInfo mockResolveHomeActivity() {
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = "fakeHomeActivity";
- aInfoDefault.applicationInfo = new ApplicationInfo();
- aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
- doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(homeIntent));
- return aInfoDefault;
+ private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) {
+ ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome);
+ Intent targetIntent;
+ if (primaryHome) {
+ targetIntent = mService.getHomeIntent();
+ } else {
+ Resources resources = mContext.getResources();
+ spyOn(resources);
+ doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString(
+ com.android.internal.R.string.config_secondaryHomePackage);
+ doReturn(forceSystemProvided).when(resources).getBoolean(
+ com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
+ targetIntent = mService.getSecondaryHomeIntent(null /* preferredPackage */);
+ }
+ doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+ refEq(targetIntent));
}
/**
* Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent
- * activity info for test cases (the original implementation will resolve from the real package
- * manager).
+ * activity info for test cases.
*/
private void mockResolveSecondaryHomeActivity() {
final Intent secondaryHomeIntent = mService
.getSecondaryHomeIntent(null /* preferredPackage */);
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = "fakeSecondaryHomeActivity";
- aInfoSecondary.applicationInfo = new ApplicationInfo();
- aInfoSecondary.applicationInfo.packageName = "fakeSecondaryHomePackage";
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false);
doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer)
.resolveSecondaryHomeActivity(anyInt(), anyInt());
}
+
+ private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) {
+ final ActivityInfo aInfo = new ActivityInfo();
+ aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity";
+ aInfo.applicationInfo = new ApplicationInfo();
+ aInfo.applicationInfo.packageName =
+ primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage";
+ return aInfo;
+ }
}
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 bd8aacb..20d9aff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -25,6 +25,7 @@
import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@@ -36,6 +37,7 @@
import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
@@ -138,6 +140,7 @@
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);
try {
ActivityManager.TaskSnapshot.Builder builder =
@@ -147,14 +150,13 @@
builder.setSystemUiVisibility(systemUiVisibility);
builder.setWindowingMode(windowingMode);
builder.setColorSpace(sRGB);
- builder.setIsLowResolution(true);
builder.setOrientation(orientation);
builder.setContentInsets(contentInsets);
builder.setIsTranslucent(true);
- builder.setScaleFraction(0.25f);
builder.setSnapshot(buffer);
builder.setIsRealSnapshot(true);
builder.setPixelFormat(pixelFormat);
+ builder.setTaskSize(taskSize);
// Not part of TaskSnapshot itself, used in screenshot process
assertEquals(pixelFormat, builder.getPixelFormat());
@@ -165,13 +167,15 @@
assertEquals(systemUiVisibility, snapshot.getSystemUiVisibility());
assertEquals(windowingMode, snapshot.getWindowingMode());
assertEquals(sRGB, snapshot.getColorSpace());
- assertTrue(snapshot.isLowResolution());
+ // Snapshots created with the Builder class are always high-res. The only way to get a
+ // low-res snapshot is to load it from the disk in TaskSnapshotLoader.
+ assertFalse(snapshot.isLowResolution());
assertEquals(orientation, snapshot.getOrientation());
assertEquals(contentInsets, snapshot.getContentInsets());
assertTrue(snapshot.isTranslucent());
- assertEquals(scaleFraction, builder.getScaleFraction(), 0.001f);
assertSame(buffer, snapshot.getSnapshot());
assertTrue(snapshot.isRealSnapshot());
+ assertEquals(taskSize, snapshot.getTaskSize());
} finally {
if (buffer != null) {
buffer.destroy();
@@ -188,11 +192,9 @@
final ActivityManager.TaskSnapshot.Builder builder =
new ActivityManager.TaskSnapshot.Builder();
- final float scaleFraction = 0.8f;
mWm.mTaskSnapshotController.prepareTaskSnapshot(mAppWindow.mActivityRecord.getTask(),
- scaleFraction, PixelFormat.UNKNOWN, builder);
+ PixelFormat.UNKNOWN, builder);
- assertEquals(scaleFraction, builder.getScaleFraction(), 0 /* delta */);
// The pixel format should be selected automatically.
assertNotEquals(PixelFormat.UNKNOWN, builder.getPixelFormat());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 0b16e5c..40f15b7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -19,12 +19,16 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -36,10 +40,12 @@
import androidx.test.filters.MediumTest;
+import com.android.server.wm.TaskSnapshotLoader.PreRLegacySnapshotConfig;
import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
import java.io.File;
import java.util.function.Predicate;
@@ -55,6 +61,8 @@
@RunWith(WindowTestRunner.class)
public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase {
+ private static final float DELTA = 0.00001f;
+
private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
@Test
@@ -148,29 +156,172 @@
}
@Test
- public void testLowResolutionPersistAndLoadSnapshot() {
+ public void testLegacyPLowRamConfig() throws Exception {
+ MockitoSession mockSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(ActivityManager.class)
+ .startMocking();
+
+ when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true);
+
+ // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+ // for any P low_ram device
+ final int taskWidth = 0;
+ final float legacyScale = 0f;
+ final boolean hasHighResFile = false;
+
+ PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+ assertNotNull(highResConf);
+ assertEquals(highResConf.mScale, 0.6f, DELTA);
+ assertTrue(highResConf.mForceLoadReducedJpeg);
+
+ PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+ assertNotNull(lowResConf);
+ assertEquals(lowResConf.mScale, 0.6f, DELTA);
+ assertTrue(lowResConf.mForceLoadReducedJpeg);
+
+ mockSession.finishMocking();
+ }
+
+ @Test
+ public void testLegacyPNonLowRamConfig() throws Exception {
+ MockitoSession mockSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(ActivityManager.class)
+ .startMocking();
+
+ when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false);
+
+ // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+ // for any O device, or a P non-low_ram device
+ final int taskWidth = 0;
+ final float legacyScale = 0f;
+ final boolean hasHighResFile = true;
+
+ PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+ assertNotNull(highResConf);
+ assertEquals(highResConf.mScale, 1.0f, DELTA);
+ assertFalse(highResConf.mForceLoadReducedJpeg);
+
+ PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+ assertNotNull(lowResConf);
+ assertEquals(lowResConf.mScale, 0.5f, DELTA);
+ assertFalse(lowResConf.mForceLoadReducedJpeg);
+
+ mockSession.finishMocking();
+ }
+
+ @Test
+ public void testLegacyQLowRamConfig() throws Exception {
+ MockitoSession mockSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(ActivityManager.class)
+ .startMocking();
+
+ when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true);
+
+ // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+ // for any Q low_ram device
+ final int taskWidth = 0;
+ final float legacyScale = 0.6f;
+ final boolean hasHighResFile = false;
+
+ PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+ assertNotNull(highResConf);
+ assertEquals(highResConf.mScale, legacyScale, DELTA);
+ assertEquals(highResConf.mScale, 0.6f, DELTA);
+ assertTrue(highResConf.mForceLoadReducedJpeg);
+
+ PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+ assertNotNull(lowResConf);
+ assertEquals(lowResConf.mScale, legacyScale, DELTA);
+ assertEquals(lowResConf.mScale, 0.6f, DELTA);
+ assertTrue(lowResConf.mForceLoadReducedJpeg);
+
+ mockSession.finishMocking();
+ }
+
+ @Test
+ public void testLegacyQNonLowRamConfig() throws Exception {
+ MockitoSession mockSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(ActivityManager.class)
+ .startMocking();
+
+ when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false);
+
+ // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+ // for any Q non-low_ram device
+ final int taskWidth = 0;
+ final float legacyScale = 0.8f;
+ final boolean hasHighResFile = true;
+
+ PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+ assertNotNull(highResConf);
+ assertEquals(highResConf.mScale, legacyScale, DELTA);
+ assertEquals(highResConf.mScale, 0.8f, DELTA);
+ assertFalse(highResConf.mForceLoadReducedJpeg);
+
+ PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+ assertNotNull(lowResConf);
+ assertEquals(lowResConf.mScale, 0.5f * legacyScale, DELTA);
+ assertEquals(lowResConf.mScale, 0.5f * 0.8f, DELTA);
+ assertFalse(lowResConf.mForceLoadReducedJpeg);
+
+ mockSession.finishMocking();
+ }
+
+ @Test
+ public void testNonLegacyRConfig() throws Exception {
+ // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+ // for any R device
+ final int taskWidth = 1440;
+ final float legacyScale = 0f;
+ final boolean hasHighResFile = true;
+
+ PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+ assertNull(highResConf);
+
+ PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+ assertNull(lowResConf);
+ }
+
+ @Test
+ public void testDisabledLowResolutionPersistAndLoadSnapshot() {
+ mPersister.setEnableLowResSnapshots(false);
+
TaskSnapshot a = new TaskSnapshotBuilder()
- .setScale(0.5f)
+ .setScaleFraction(0.5f)
.setIsLowResolution(true)
.build();
assertTrue(a.isLowResolution());
mPersister.persistSnapshot(1, mTestUserId, a);
mPersister.waitForQueueEmpty();
final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
+ new File(FILES_DIR.getPath() + "/snapshots/1.jpg")};
final File[] nonExistsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
+ new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
};
assertTrueForFiles(files, File::exists, " must exist");
assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
- final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, true /* isLowResolution */);
+ final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */);
assertNotNull(snapshot);
assertEquals(TEST_INSETS, snapshot.getContentInsets());
assertNotNull(snapshot.getSnapshot());
assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId,
- false /* isLowResolution */);
+ true /* isLowResolution */);
assertNull(snapshotNotExist);
}
@@ -271,13 +422,11 @@
@Test
public void testScalePersistAndLoadSnapshot() {
TaskSnapshot a = new TaskSnapshotBuilder()
- .setScale(0.25f)
+ .setScaleFraction(0.25f)
.build();
TaskSnapshot b = new TaskSnapshotBuilder()
- .setScale(0.75f)
+ .setScaleFraction(0.75f)
.build();
- assertEquals(0.25f, a.getScale(), 1E-5);
- assertEquals(0.75f, b.getScale(), 1E-5);
mPersister.persistSnapshot(1, mTestUserId, a);
mPersister.persistSnapshot(2, mTestUserId, b);
mPersister.waitForQueueEmpty();
@@ -287,8 +436,6 @@
false /* isLowResolution */);
assertNotNull(snapshotA);
assertNotNull(snapshotB);
- assertEquals(0.25f, snapshotA.getScale(), 1E-5);
- assertEquals(0.75f, snapshotB.getScale(), 1E-5);
}
@Test
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 4612dba..fa6663c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -30,6 +30,7 @@
import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.UserManager;
import android.view.Surface;
@@ -87,8 +88,10 @@
* Builds a TaskSnapshot.
*/
static class TaskSnapshotBuilder {
+ private static final int SNAPSHOT_WIDTH = 100;
+ private static final int SNAPSHOT_HEIGHT = 100;
- private float mScale = 1f;
+ private float mScaleFraction = 1f;
private boolean mIsLowResolution = false;
private boolean mIsRealSnapshot = true;
private boolean mIsTranslucent = false;
@@ -96,8 +99,11 @@
private int mSystemUiVisibility = 0;
private int mRotation = Surface.ROTATION_0;
- TaskSnapshotBuilder setScale(float scale) {
- mScale = scale;
+ TaskSnapshotBuilder() {
+ }
+
+ TaskSnapshotBuilder setScaleFraction(float scale) {
+ mScaleFraction = scale;
return this;
}
@@ -132,15 +138,20 @@
}
TaskSnapshot build() {
- final GraphicBuffer buffer = GraphicBuffer.create(100, 100, PixelFormat.RGBA_8888,
+ // To satisfy existing tests, ensure the graphics buffer is always 100x100, and
+ // compute the ize of the task according to mScaleFraction.
+ Point taskSize = new Point((int) (SNAPSHOT_WIDTH / mScaleFraction),
+ (int) (SNAPSHOT_HEIGHT / mScaleFraction));
+ final GraphicBuffer buffer = GraphicBuffer.create(SNAPSHOT_WIDTH, SNAPSHOT_HEIGHT,
+ PixelFormat.RGBA_8888,
USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY);
Canvas c = buffer.lockCanvas();
c.drawColor(Color.RED);
buffer.unlockCanvasAndPost(c);
return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer,
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
- mRotation, TEST_INSETS,
- mIsLowResolution, mScale, mIsRealSnapshot,
+ mRotation, taskSize, TEST_INSETS,
+ mIsLowResolution, mIsRealSnapshot,
mWindowingMode, mSystemUiVisibility, mIsTranslucent);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index bb0e5ae..2164de9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -38,6 +38,7 @@
import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.Surface;
@@ -67,12 +68,22 @@
int windowFlags, Rect taskBounds) {
final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888,
GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER);
+
+ // Previously when constructing TaskSnapshots for this test, scale was 1.0f, so to mimic
+ // this behavior set the taskSize to be the same as the taskBounds width and height. The
+ // taskBounds passed here are assumed to be the same task bounds as when the snapshot was
+ // taken. We assume there is no aspect ratio mismatch between the screenshot and the
+ // taskBounds
+ assertEquals(width, taskBounds.width());
+ assertEquals(height, taskBounds.height());
+ Point taskSize = new Point(taskBounds.width(), taskBounds.height());
+
final TaskSnapshot snapshot = new TaskSnapshot(
System.currentTimeMillis(),
new ComponentName("", ""), buffer,
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
- Surface.ROTATION_0, contentInsets, false,
- 1.0f, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
+ Surface.ROTATION_0, taskSize, contentInsets, false,
+ true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
0 /* systemUiVisibility */, false /* isTranslucent */);
mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0,
@@ -152,7 +163,7 @@
@Test
public void testCalculateSnapshotCrop_taskNotOnTop() {
- setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100));
+ setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 150));
assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 2c68cc7..85e4a16 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -251,9 +251,11 @@
// b/145812508: special legacy use-case for transparent/translucent windows.
appWindow.mAttrs.format = PixelFormat.TRANSPARENT;
+ appWindow.mAttrs.alpha = 0;
assertTrue(appWindow.canBeImeTarget());
appWindow.mAttrs.format = PixelFormat.OPAQUE;
+ appWindow.mAttrs.alpha = 1;
appWindow.mAttrs.flags &= ~FLAG_ALT_FOCUSABLE_IM;
assertFalse(appWindow.canBeImeTarget());
appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
@@ -276,8 +278,7 @@
spyOn(appWindow);
spyOn(controller);
spyOn(stack);
- doReturn(true).when(controller).isMinimizedDock();
- doReturn(true).when(controller).isHomeStackResizable();
+ stack.setFocusable(false);
doReturn(stack).when(appWindow).getRootTask();
// Make sure canBeImeTarget is false due to shouldIgnoreInput is true;
@@ -619,9 +620,10 @@
}
@Test
- public void testCantReceiveTouchWhenShouldIgnoreInput() {
+ public void testCantReceiveTouchWhenNotFocusable() {
final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
- win0.mActivityRecord.getStack().setAdjustedForMinimizedDock(1 /* Any non 0 value works */);
+ win0.mActivityRecord.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ win0.mActivityRecord.getStack().setFocusable(false);
assertTrue(win0.cantReceiveTouchInput());
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 07cc2d4..8e85bb2 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1917,7 +1917,19 @@
return;
}
try {
- // Adbd will be started by AdbService once Global.ADB_ENABLED is set.
+ if ((config & UsbManager.FUNCTION_ADB) != 0) {
+ /**
+ * Start adbd if ADB function is included in the configuration.
+ */
+ LocalServices.getService(AdbManagerInternal.class)
+ .startAdbdForTransport(AdbTransportType.USB);
+ } else {
+ /**
+ * Stop adbd otherwise
+ */
+ LocalServices.getService(AdbManagerInternal.class)
+ .stopAdbdForTransport(AdbTransportType.USB);
+ }
UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest,
config, chargingFunctions);
mGadgetProxy.setCurrentUsbFunctions(config, usbGadgetCallback,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 3c0e0af..0b24dd2 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -60,7 +60,6 @@
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.provider.Settings;
-import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionService;
@@ -684,9 +683,9 @@
}
@Override
- public void showSession(IVoiceInteractionService service, Bundle args, int flags) {
+ public void showSession(Bundle args, int flags) {
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
final long caller = Binder.clearCallingIdentity();
try {
@@ -928,12 +927,10 @@
}
//----------------- Model management APIs --------------------------------//
- // TODO: add check to only allow active voice interaction service or keyphrase enrollment
- // application to manage voice models
@Override
public KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, String bcp47Locale) {
- enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
+ enforceCallerAllowedToEnrollVoiceModel();
if (bcp47Locale == null) {
throw new IllegalArgumentException("Illegal argument(s) in getKeyphraseSoundModel");
@@ -950,7 +947,7 @@
@Override
public int updateKeyphraseSoundModel(KeyphraseSoundModel model) {
- enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
+ enforceCallerAllowedToEnrollVoiceModel();
if (model == null) {
throw new IllegalArgumentException("Model must not be null");
}
@@ -975,7 +972,7 @@
@Override
public int deleteKeyphraseSoundModel(int keyphraseId, String bcp47Locale) {
- enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
+ enforceCallerAllowedToEnrollVoiceModel();
if (bcp47Locale == null) {
throw new IllegalArgumentException(
@@ -1008,10 +1005,9 @@
//----------------- SoundTrigger APIs --------------------------------//
@Override
- public boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId,
- String bcp47Locale) {
+ public boolean isEnrolledForKeyphrase(int keyphraseId, String bcp47Locale) {
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
}
if (bcp47Locale == null) {
@@ -1030,10 +1026,10 @@
}
@Nullable
- public KeyphraseMetadata getEnrolledKeyphraseMetadata(IVoiceInteractionService service,
- String keyphrase, String bcp47Locale) {
+ public KeyphraseMetadata getEnrolledKeyphraseMetadata(String keyphrase,
+ String bcp47Locale) {
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
}
if (bcp47Locale == null) {
@@ -1065,10 +1061,10 @@
}
@Override
- public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) {
+ public ModuleProperties getDspModuleProperties() {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
final long caller = Binder.clearCallingIdentity();
try {
@@ -1080,12 +1076,11 @@
}
@Override
- public int startRecognition(IVoiceInteractionService service, int keyphraseId,
- String bcp47Locale, IRecognitionStatusCallback callback,
- RecognitionConfig recognitionConfig) {
+ public int startRecognition(int keyphraseId, String bcp47Locale,
+ IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
if (callback == null || recognitionConfig == null || bcp47Locale == null) {
throw new IllegalArgumentException("Illegal argument(s) in startRecognition");
@@ -1117,11 +1112,10 @@
}
@Override
- public int stopRecognition(IVoiceInteractionService service, int keyphraseId,
- IRecognitionStatusCallback callback) {
+ public int stopRecognition(int keyphraseId, IRecognitionStatusCallback callback) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
}
final long caller = Binder.clearCallingIdentity();
@@ -1133,11 +1127,10 @@
}
@Override
- public int setParameter(IVoiceInteractionService service, int keyphraseId,
- @ModelParams int modelParam, int value) {
+ public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
}
final long caller = Binder.clearCallingIdentity();
@@ -1149,11 +1142,10 @@
}
@Override
- public int getParameter(IVoiceInteractionService service, int keyphraseId,
- @ModelParams int modelParam) {
+ public int getParameter(int keyphraseId, @ModelParams int modelParam) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
}
final long caller = Binder.clearCallingIdentity();
@@ -1166,11 +1158,10 @@
@Override
@Nullable
- public ModelParamRange queryParameter(IVoiceInteractionService service,
- int keyphraseId, @ModelParams int modelParam) {
+ public ModelParamRange queryParameter(int keyphraseId, @ModelParams int modelParam) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
}
final long caller = Binder.clearCallingIdentity();
@@ -1400,9 +1391,9 @@
}
@Override
- public void setUiHints(IVoiceInteractionService service, Bundle hints) {
+ public void setUiHints(Bundle hints) {
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
final int size = mVoiceInteractionSessionListeners.beginBroadcast();
for (int i = 0; i < size; ++i) {
@@ -1425,14 +1416,32 @@
}
}
- private void enforceIsCurrentVoiceInteractionService(IVoiceInteractionService service) {
- if (mImpl == null || mImpl.mService == null
- || service.asBinder() != mImpl.mService.asBinder()) {
+ private void enforceIsCurrentVoiceInteractionService() {
+ if (!isCallerCurrentVoiceInteractionService()) {
throw new
SecurityException("Caller is not the current voice interaction service");
}
}
+ private void enforceCallerAllowedToEnrollVoiceModel() {
+ enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
+ if (!isCallerCurrentVoiceInteractionService()
+ && !isCallerTrustedEnrollmentApplication()) {
+ throw new SecurityException("Caller is required to be the current voice interaction"
+ + " service or a system enrollment application to enroll voice models");
+ }
+ }
+
+ private boolean isCallerCurrentVoiceInteractionService() {
+ return mImpl != null
+ && mImpl.mInfo.getServiceInfo().applicationInfo.uid == Binder.getCallingUid();
+ }
+
+ private boolean isCallerTrustedEnrollmentApplication() {
+ return mImpl.mEnrollmentApplicationInfo.isUidSupportedEnrollmentApplication(
+ Binder.getCallingUid());
+ }
+
private void setImplLocked(VoiceInteractionManagerServiceImpl impl) {
mImpl = impl;
mAtmInternal.notifyActiveVoiceInteractionServiceChanged(
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index a62b03c..b813f87 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -36,6 +36,7 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -78,6 +79,7 @@
final IActivityManager mAm;
final IActivityTaskManager mAtm;
final VoiceInteractionServiceInfo mInfo;
+ final KeyphraseEnrollmentInfo mEnrollmentApplicationInfo;
final ComponentName mSessionComponentName;
final IWindowManager mIWindowManager;
boolean mBound = false;
@@ -133,6 +135,7 @@
mComponent = service;
mAm = ActivityManager.getService();
mAtm = ActivityTaskManager.getService();
+ mEnrollmentApplicationInfo = new KeyphraseEnrollmentInfo(context.getPackageManager());
VoiceInteractionServiceInfo info;
try {
info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser);
@@ -403,6 +406,7 @@
pw.println(" Active session:");
mActiveSession.dump(" ", pw);
}
+ pw.println(" " + mEnrollmentApplicationInfo.toString());
}
void startLocked() {
diff --git a/startop/scripts/app_startup/parse_metrics b/startop/scripts/app_startup/parse_metrics
index 036609f..3fa1462 100755
--- a/startop/scripts/app_startup/parse_metrics
+++ b/startop/scripts/app_startup/parse_metrics
@@ -42,12 +42,14 @@
-h, --help usage information (this)
-v, --verbose enable extra verbose printing
-t, --timeout <sec> how many seconds to timeout when trying to wait for logcat to change
+ -rfd, --reportfullydrawn wait for report fully drawn (default: off)
EOF
}
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$DIR/lib/common"
+report_fully_drawn="n"
package=""
activity=""
timeout=5
@@ -81,6 +83,11 @@
-s|--simulate)
simulate="y"
;;
+ -rfd|--reportfullydrawn)
+ report_fully_drawn="y"
+ ;;
+
+
*)
echo "Invalid argument: $1" >&2
exit 1
@@ -190,12 +197,15 @@
parse_metric_from_logcat "Displayed_ms" "$pattern" "$re_pattern"
-# 01-16 17:31:44.550 11172 11204 I ActivityTaskManager: Fully drawn com.google.android.GoogleCamera/com.android.camera.CameraLauncher: +10s897ms
-pattern="ActivityTaskManager: Fully drawn ${package}"
-#re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+\).*'
-re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+ms\|[[:digit:]]\+s[[:digit:]]\+ms\).*'
+# Only track ReportFullyDrawn with --reportfullydrawn/-rfd flags
+if [[ $report_fully_drawn == y ]]; then
+ # 01-16 17:31:44.550 11172 11204 I ActivityTaskManager: Fully drawn com.google.android.GoogleCamera/com.android.camera.CameraLauncher: +10s897ms
+ pattern="ActivityTaskManager: Fully drawn ${package}"
+ #re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+\).*'
+ re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+ms\|[[:digit:]]\+s[[:digit:]]\+ms\).*'
-parse_metric_from_logcat "Fully_drawn_ms" "$pattern" "$re_pattern"
+ parse_metric_from_logcat "Fully_drawn_ms" "$pattern" "$re_pattern"
+fi
# also call into package-specific scripts if there are additional metrics
if [[ -x "$DIR/metrics/$package" ]]; then
diff --git a/startop/scripts/app_startup/run_app_with_prefetch b/startop/scripts/app_startup/run_app_with_prefetch
index 92a31c3..31f6253 100755
--- a/startop/scripts/app_startup/run_app_with_prefetch
+++ b/startop/scripts/app_startup/run_app_with_prefetch
@@ -35,6 +35,7 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$DIR/../iorap/common"
+report_fully_drawn="n"
needs_trace_file="n"
input_file=""
package=""
@@ -70,6 +71,10 @@
mode="$2"
shift
;;
+ -rfd|--reportfullydrawn)
+ report_fully_drawn="y"
+ shift
+ ;;
-c|--count)
count="$2"
((count+=1))
@@ -403,7 +408,11 @@
join_by ',' "${all_metrics[@]}"
}
-metrics_header="$("$DIR/parse_metrics" --package "$package" --activity "$activity" --simulate | parse_metrics_header)"
+if [[ $report_fully_drawn == y ]]; then
+ metrics_header="$("$DIR/parse_metrics" --package "$package" --activity "$activity" --simulate --reportfullydrawn | parse_metrics_header)"
+else
+ metrics_header="$("$DIR/parse_metrics" --package "$package" --activity "$activity" --simulate | parse_metrics_header)"
+fi
# TODO: This loop logic could probably be moved into app_startup_runner.py
for ((i=0;i<count;++i)) do
@@ -411,6 +420,9 @@
verbose_print "==== ITERATION $i ===="
verbose_print "=========================================="
if [[ $mode != "warm" ]]; then
+ # The package must be killed **before** we drop caches, otherwise pages will stay resident.
+ verbose_print "Kill package for non-warm start."
+ remote_pkill "$package"
verbose_print "Drop caches for non-warm start."
# Drop all caches to get cold starts.
adb shell "echo 3 > /proc/sys/vm/drop_caches"
@@ -423,7 +435,12 @@
pre_launch_timestamp="$(logcat_save_timestamp)"
# TODO: multiple metrics output.
+
+if [[ $report_fully_drawn == y ]]; then
+ total_time="$(timeout $timeout "$DIR/launch_application" "$package" "$activity" | "$DIR/parse_metrics" --package "$package" --activity "$activity" --timestamp "$pre_launch_timestamp" --reportfullydrawn | parse_metrics_output)"
+else
total_time="$(timeout $timeout "$DIR/launch_application" "$package" "$activity" | "$DIR/parse_metrics" --package "$package" --activity "$activity" --timestamp "$pre_launch_timestamp" | parse_metrics_output)"
+fi
if [[ $? -ne 0 ]]; then
echo "WARNING: Skip bad result, try iteration again." >&2
diff --git a/startop/scripts/app_startup/run_app_with_prefetch.py b/startop/scripts/app_startup/run_app_with_prefetch.py
old mode 100644
new mode 100755
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 4604cd2..5f33a3d 100755
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3358,7 +3358,6 @@
private boolean mImmutable = false;
public FailureSignalingConnection(DisconnectCause disconnectCause) {
setDisconnected(disconnectCause);
- mImmutable = true;
}
public void checkImmutable() {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 5d7d649..7f4fcc0 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -330,6 +330,14 @@
"android.telecom.extra.CALL_CREATED_TIME_MILLIS";
/**
+ * Optional extra for incoming and outgoing calls containing a long which specifies the Epoch
+ * time the call was created.
+ * @hide
+ */
+ public static final String EXTRA_CALL_CREATED_EPOCH_TIME_MILLIS =
+ "android.telecom.extra.CALL_CREATED_EPOCH_TIME_MILLIS";
+
+ /**
* Optional extra for incoming and outgoing calls containing a long which specifies the time
* telecom began routing the call. This value is in milliseconds since boot.
* @hide
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a7e52ea..6a40487 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3549,6 +3549,30 @@
public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL =
"show_forwarded_number_bool";
+ /**
+ * The list of originating address of missed incoming call SMS. If the SMS has originator
+ * matched, the SMS will be treated as special SMS for notifying missed incoming call to the
+ * user.
+ *
+ * @hide
+ */
+ public static final String KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY =
+ "missed_incoming_call_sms_originator_string_array";
+
+ /**
+ * The patterns of missed incoming call sms. This is the regular expression used for
+ * matching the missed incoming call's date, time, and caller id. The pattern should match
+ * fields for at least month, day, hour, and minute. Year is optional although it is encouraged.
+ *
+ * An usable pattern should look like this:
+ * ^(?<month>0[1-9]|1[012])\/(?<day>0[1-9]|1[0-9]|2[0-9]|3[0-1]) (?<hour>[0-1][0-9]|2[0-3]):
+ * (?<minute>[0-5][0-9])\s*(?<callerId>[0-9]+)\s*$
+ *
+ * @hide
+ */
+ public static final String KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY =
+ "missed_incoming_call_sms_pattern_string_array";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -4057,6 +4081,9 @@
sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false);
sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1));
+ sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY,
+ new String[0]);
+ sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
}
/**
@@ -4069,7 +4096,8 @@
/** Prefix of all Wifi.KEY_* constants. */
public static final String KEY_PREFIX = "wifi.";
/**
- * It contains the maximum client count definition that the carrier owns.
+ * It contains the maximum client count definition that the carrier sets.
+ * The default is 0, which means that the carrier hasn't set a requirement.
*/
public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT =
KEY_PREFIX + "hotspot_maximum_client_count";
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0660776..31d9654 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2788,39 +2788,22 @@
/**
* Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current
* registered operator or the cell nearby, if available.
- * <p>
- * The ISO-3166 country code is provided in lowercase 2 character format.
- * <p>
- * Note: In multi-sim, this returns a shared emergency network country iso from other
- * subscription if the subscription used to create the TelephonyManager doesn't camp on
- * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding
- * slot.
+ *
* Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
* if on a CDMA network).
* <p>
* @return the lowercase 2 character ISO-3166 country code, or empty string if not available.
*/
public String getNetworkCountryIso() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony == null) return "";
- return telephony.getNetworkCountryIsoForPhone(getPhoneId(),
- null /* no permission check */, null);
- } catch (RemoteException ex) {
- return "";
- }
+ return getNetworkCountryIso(getSlotIndex());
}
/**
* Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current
- * registered operator or the cell nearby, if available.
- * <p>
- * The ISO-3166 country code is provided in lowercase 2 character format.
- * <p>
- * Note: In multi-sim, this returns a shared emergency network country iso from other
- * subscription if the subscription used to create the TelephonyManager doesn't camp on
- * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding
- * slot.
+ * registered operator or the cell nearby, if available. This is same as
+ * {@link #getNetworkCountryIso()} but allowing specifying the SIM slot index. This is used for
+ * accessing network country info from the SIM slot that does not have SIM inserted.
+ *
* Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
* if on a CDMA network).
* <p>
@@ -2831,22 +2814,18 @@
*
* @throws IllegalArgumentException when the slotIndex is invalid.
*
- * {@hide}
*/
- @SystemApi
- @TestApi
@NonNull
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getNetworkCountryIso(int slotIndex) {
try {
- if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
+ if (slotIndex != SubscriptionManager.DEFAULT_SIM_SLOT_INDEX
+ && !SubscriptionManager.isValidSlotIndex(slotIndex)) {
throw new IllegalArgumentException("invalid slot index " + slotIndex);
}
ITelephony telephony = getITelephony();
if (telephony == null) return "";
- return telephony.getNetworkCountryIsoForPhone(slotIndex, getOpPackageName(),
- getFeatureId());
+ return telephony.getNetworkCountryIsoForPhone(slotIndex);
} catch (RemoteException ex) {
return "";
}
@@ -11092,7 +11071,6 @@
* @param enabled True if enabling the data, otherwise disabling.
* @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setPolicyDataEnabled(boolean enabled) {
try {
@@ -11195,7 +11173,6 @@
* @param isEnabled {@code true} for enabling; {@code false} for disabling.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setAlwaysReportSignalStrength(boolean isEnabled) {
try {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 861925f..af5089f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -281,7 +281,7 @@
* operator's MCC (Mobile Country Code).
* @see android.telephony.TelephonyManager#getNetworkCountryIso
*/
- String getNetworkCountryIsoForPhone(int phoneId, String callingPkg, String callingFeatureId);
+ String getNetworkCountryIsoForPhone(int phoneId);
/**
* Returns the neighboring cell information of the device.
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index 1c8b6be..61f3dba 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -30,6 +30,7 @@
private ITestDevice mTestDevice;
private static final String SYSTEM_SERVER_PROFILE =
"/data/misc/profiles/cur/0/android/primary.prof";
+ private static final boolean USE_PHENOTYPE = false;
@Override
public void setDevice(ITestDevice testDevice) {
@@ -41,16 +42,33 @@
return mTestDevice;
}
+ private String getProperty(String property) throws Exception {
+ if (USE_PHENOTYPE) {
+ return mTestDevice.getProperty("persist.device_config.runtime_native_boot."
+ + property);
+ } else {
+ return mTestDevice.executeShellCommand("getprop dalvik.vm." + property).trim();
+ }
+ }
+
+ private String setProperty(String property, String value) throws Exception {
+ if (USE_PHENOTYPE) {
+ return mTestDevice.executeShellCommand(
+ "device_config put runtime_native_boot " + property + " " + value);
+ } else {
+ return mTestDevice.executeShellCommand(
+ "setprop dalvik.vm." + property + " " + value);
+ }
+ }
+
/**
* Validate that the boot image profile properties are set.
*/
public void validateProperties() throws Exception {
- String res = mTestDevice.getProperty(
- "persist.device_config.runtime_native_boot.profilebootclasspath");
- assertTrue("profile boot class path not enabled", res != null && res.equals("true"));
- res = mTestDevice.getProperty(
- "persist.device_config.runtime_native_boot.profilesystemserver");
- assertTrue("profile system server not enabled", res != null && res.equals("true"));
+ String res = getProperty("profilebootclasspath");
+ assertTrue("profile boot class path not enabled: " + res, "true".equals(res));
+ res = getProperty("profilesystemserver");
+ assertTrue("profile system server not enabled: " + res, "true".equals(res));
}
private boolean forceSaveProfile(String pkg) throws Exception {
@@ -67,33 +85,48 @@
@Test
public void testSystemServerProfile() throws Exception {
final int numIterations = 20;
+ String res;
+ // Set properties and wait for them to be readable.
for (int i = 1; i <= numIterations; ++i) {
- String res;
- res = mTestDevice.getProperty(
- "persist.device_config.runtime_native_boot.profilebootclasspath");
- boolean profileBootClassPath = res != null && res.equals("true");
- res = mTestDevice.getProperty(
- "persist.device_config.runtime_native_boot.profilesystemserver");
- boolean profileSystemServer = res != null && res.equals("true");
+ String pbcp = getProperty("profilebootclasspath");
+ boolean profileBootClassPath = "true".equals(pbcp);
+ String pss = getProperty("profilesystemserver");
+ boolean profileSystemServer = "true".equals(pss);
if (profileBootClassPath && profileSystemServer) {
break;
}
if (i == numIterations) {
- assertTrue("profile system server not enabled", profileSystemServer);
- assertTrue("profile boot class path not enabled", profileSystemServer);
+ assertTrue("profile system server not enabled: " + pss, profileSystemServer);
+ assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath);
}
- res = mTestDevice.executeShellCommand(
- "device_config put runtime_native_boot profilebootclasspath true");
- res = mTestDevice.executeShellCommand(
- "device_config put runtime_native_boot profilesystemserver true");
- res = mTestDevice.executeShellCommand("stop");
- res = mTestDevice.executeShellCommand("start");
- Thread.sleep(5000);
+ setProperty("profilebootclasspath", "true");
+ setProperty("profilesystemserver", "true");
+ Thread.sleep(1000);
}
+
+ // Restart shell and wait for system boot.
+ res = mTestDevice.executeShellCommand("stop");
+ assertTrue("stop shell: " + res, res.length() == 0);
+ res = mTestDevice.executeShellCommand("start");
+ assertTrue("start shell: " + res, res.length() == 0);
+ for (int i = 1; i <= numIterations; ++i) {
+ String pbcp = getProperty("profilebootclasspath");
+ boolean profileBootClassPath = "true".equals(pbcp);
+ String pss = getProperty("profilesystemserver");
+ boolean profileSystemServer = "true".equals(pss);
+ if (profileBootClassPath && profileSystemServer) {
+ break;
+ }
+ if (i == numIterations) {
+ assertTrue("profile system server not enabled: " + pss, profileSystemServer);
+ assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath);
+ }
+ Thread.sleep(1000);
+ }
+
// Trunacte the profile before force it to be saved to prevent previous profiles
// causing the test to pass.
- String res;
res = mTestDevice.executeShellCommand("truncate -s 0 " + SYSTEM_SERVER_PROFILE).trim();
assertTrue(res, res.length() == 0);
// Wait up to 20 seconds for the profile to be saved.
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 6f44230..c1d05e4 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -59,6 +59,7 @@
"libprotobuf-cpp-full",
"libz",
"libbuildversion",
+ "libidmap2_policies",
],
stl: "libc++_static",
group_static_libs: true,
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 137fbd6..1eb7d95 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -32,10 +32,16 @@
#include "text/Printer.h"
#include "util/Util.h"
+#include "idmap2/Policies.h"
+
using ::aapt::text::Printer;
using ::android::StringPiece;
using ::android::base::StringPrintf;
+using android::idmap2::policy::kPolicyStringToFlag;
+
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
namespace {
@@ -246,32 +252,25 @@
Printer* printer_;
};
-std::string OverlayablePoliciesToString(OverlayableItem::PolicyFlags policies) {
- static const std::map<OverlayableItem::PolicyFlags, std::string> kFlagToString = {
- {OverlayableItem::kPublic, "public"},
- {OverlayableItem::kSystem, "system"},
- {OverlayableItem::kVendor, "vendor"},
- {OverlayableItem::kProduct, "product"},
- {OverlayableItem::kSignature, "signature"},
- {OverlayableItem::kOdm, "odm"},
- {OverlayableItem::kOem, "oem"},
- };
+std::string OverlayablePoliciesToString(PolicyFlags policies) {
std::string str;
- for (auto const& policy : kFlagToString) {
- if ((policies & policy.first) != policy.first) {
+
+ uint32_t remaining = policies;
+ for (auto const& policy : kPolicyStringToFlag) {
+ if ((policies & policy.second) != policy.second) {
continue;
}
if (!str.empty()) {
str.append("|");
}
- str.append(policy.second);
- policies &= ~policy.first;
+ str.append(policy.first.data());
+ remaining &= ~policy.second;
}
- if (policies != 0) {
+ if (remaining != 0) {
if (!str.empty()) {
str.append("|");
}
- str.append(StringPrintf("0x%08x", policies));
+ str.append(StringPrintf("0x%08x", remaining));
}
return !str.empty() ? str : "none";
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 74e2a098..234cbc4 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -32,11 +32,15 @@
#include "util/Util.h"
#include "xml/XmlPullParser.h"
+#include "idmap2/Policies.h"
+
using ::aapt::ResourceUtils::StringBuilder;
using ::aapt::text::Utf8Iterator;
using ::android::ConfigDescription;
using ::android::StringPiece;
+using android::idmap2::policy::kPolicyStringToFlag;
+
namespace aapt {
constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
@@ -1063,7 +1067,7 @@
bool error = false;
std::string comment;
- OverlayableItem::PolicyFlags current_policies = OverlayableItem::Policy::kNone;
+ PolicyFlags current_policies = PolicyFlags::NONE;
const size_t start_depth = parser->depth();
while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
xml::XmlPullParser::Event event = parser->event();
@@ -1073,7 +1077,7 @@
} else if (event == xml::XmlPullParser::Event::kEndElement
&& parser->depth() == start_depth + 1) {
// Clear the current policies when exiting the <policy> tags
- current_policies = OverlayableItem::Policy::kNone;
+ current_policies = PolicyFlags::NONE;
continue;
} else if (event == xml::XmlPullParser::Event::kComment) {
// Retrieve the comment of individual <item> tags
@@ -1088,7 +1092,7 @@
const std::string& element_name = parser->element_name();
const std::string& element_namespace = parser->element_namespace();
if (element_namespace.empty() && element_name == "item") {
- if (current_policies == OverlayableItem::Policy::kNone) {
+ if (current_policies == PolicyFlags::NONE) {
diag_->Error(DiagMessage(element_source)
<< "<item> within an <overlayable> must be inside a <policy> block");
error = true;
@@ -1133,7 +1137,7 @@
out_resource->child_resources.push_back(std::move(child_resource));
} else if (element_namespace.empty() && element_name == "policy") {
- if (current_policies != OverlayableItem::Policy::kNone) {
+ if (current_policies != PolicyFlags::NONE) {
// If the policy list is not empty, then we are currently inside a policy element
diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested");
error = true;
@@ -1141,21 +1145,14 @@
} else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
// Parse the polices separated by vertical bar characters to allow for specifying multiple
// policies. Items within the policy tag will have the specified policy.
- static const auto kPolicyMap =
- ImmutableMap<StringPiece, OverlayableItem::Policy>::CreatePreSorted({
- {"odm", OverlayableItem::Policy::kOdm},
- {"oem", OverlayableItem::Policy::kOem},
- {"product", OverlayableItem::Policy::kProduct},
- {"public", OverlayableItem::Policy::kPublic},
- {"signature", OverlayableItem::Policy::kSignature},
- {"system", OverlayableItem::Policy::kSystem},
- {"vendor", OverlayableItem::Policy::kVendor},
- });
-
for (const StringPiece& part : util::Tokenize(maybe_type.value(), '|')) {
StringPiece trimmed_part = util::TrimWhitespace(part);
- const auto policy = kPolicyMap.find(trimmed_part);
- if (policy == kPolicyMap.end()) {
+ const auto policy = std::find_if(kPolicyStringToFlag.begin(),
+ kPolicyStringToFlag.end(),
+ [trimmed_part](const auto& it) {
+ return trimmed_part == it.first;
+ });
+ if (policy == kPolicyStringToFlag.end()) {
diag_->Error(DiagMessage(element_source)
<< "<policy> has unsupported type '" << trimmed_part << "'");
error = true;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 24531bc..9b70079 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -41,6 +41,8 @@
using ::testing::SizeIs;
using ::testing::StrEq;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
@@ -959,7 +961,7 @@
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::SIGNATURE));
search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
ASSERT_TRUE(search_result);
@@ -968,7 +970,7 @@
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::SIGNATURE));
}
TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
@@ -1005,6 +1007,9 @@
<policy type="oem">
<item type="string" name="buz" />
</policy>
+ <policy type="actor">
+ <item type="string" name="actor" />
+ </policy>
</overlayable>)";
ASSERT_TRUE(TestParse(input));
@@ -1014,7 +1019,7 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION));
search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
ASSERT_TRUE(search_result);
@@ -1022,7 +1027,7 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::SYSTEM_PARTITION));
search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
ASSERT_TRUE(search_result);
@@ -1030,7 +1035,7 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::VENDOR_PARTITION));
search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
ASSERT_TRUE(search_result);
@@ -1038,7 +1043,7 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PUBLIC));
search_result = table_.FindResource(test::ParseNameOrDie("string/foz"));
ASSERT_TRUE(search_result);
@@ -1046,7 +1051,7 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::SIGNATURE));
search_result = table_.FindResource(test::ParseNameOrDie("string/biz"));
ASSERT_TRUE(search_result);
@@ -1054,7 +1059,7 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kOdm));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::ODM_PARTITION));
search_result = table_.FindResource(test::ParseNameOrDie("string/buz"));
ASSERT_TRUE(search_result);
@@ -1062,7 +1067,15 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kOem));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::OEM_PARTITION));
+
+ search_result = table_.FindResource(test::ParseNameOrDie("string/actor"));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::ACTOR_SIGNATURE));
}
TEST_F(ResourceParserTest, ParseOverlayableNoPolicyError) {
@@ -1125,8 +1138,8 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor
- | OverlayableItem::Policy::kPublic));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::VENDOR_PARTITION
+ | PolicyFlags::PUBLIC));
search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(search_result);
@@ -1134,8 +1147,8 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
- | OverlayableItem::Policy::kSystem));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION
+ | PolicyFlags::SYSTEM_PARTITION));
}
TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 30ba1ae..93a7a31 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -36,6 +36,8 @@
#include <unordered_map>
#include <vector>
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
// The Public status of a resource.
@@ -75,36 +77,8 @@
struct OverlayableItem {
explicit OverlayableItem(const std::shared_ptr<Overlayable>& overlayable)
: overlayable(overlayable) {}
-
- // Represents the types overlays that are allowed to overlay the resource.
- typedef uint32_t PolicyFlags;
- enum Policy : uint32_t {
- kNone = 0x00000000,
-
- // The resource can be overlaid by any overlay.
- kPublic = 0x00000001,
-
- // The resource can be overlaid by any overlay on the system partition.
- kSystem = 0x00000002,
-
- // The resource can be overlaid by any overlay on the vendor partition.
- kVendor = 0x00000004,
-
- // The resource can be overlaid by any overlay on the product partition.
- kProduct = 0x00000008,
-
- // The resource can be overlaid by any overlay signed with the same signature as its actor.
- kSignature = 0x00000010,
-
- // The resource can be overlaid by any overlay on the odm partition.
- kOdm = 0x00000020,
-
- // The resource can be overlaid by any overlay on the oem partition.
- kOem = 0x00000040,
- };
-
std::shared_ptr<Overlayable> overlayable;
- PolicyFlags policies = Policy::kNone;
+ PolicyFlags policies = PolicyFlags::NONE;
std::string comment;
Source source;
};
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index b97dc6b..9271a7e 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -30,6 +30,8 @@
using ::testing::NotNull;
using ::testing::StrEq;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
TEST(ResourceTableTest, FailToAddResourceWithBadName) {
@@ -247,8 +249,8 @@
auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme",
Source("res/values/overlayable.xml", 40));
OverlayableItem overlayable_item(overlayable);
- overlayable_item.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item.policies |= PolicyFlags::VENDOR_PARTITION;
overlayable_item.comment = "comment";
overlayable_item.source = Source("res/values/overlayable.xml", 42);
@@ -264,8 +266,8 @@
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
EXPECT_THAT(result_overlayable_item.overlayable->source.line, 40);
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
- | OverlayableItem::Policy::kVendor));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION
+ | PolicyFlags::VENDOR_PARTITION));
ASSERT_THAT(result_overlayable_item.comment, StrEq("comment"));
EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
EXPECT_THAT(result_overlayable_item.source.line, 42);
@@ -277,17 +279,17 @@
const ResourceName foo = test::ParseNameOrDie("android:string/foo");
auto group = std::make_shared<Overlayable>("Name", "overlay://theme");
OverlayableItem overlayable(group);
- overlayable.policies = OverlayableItem::Policy::kProduct;
+ overlayable.policies = PolicyFlags::PRODUCT_PARTITION;
ASSERT_TRUE(table.SetOverlayable(foo, overlayable, test::GetDiagnostics()));
const ResourceName bar = test::ParseNameOrDie("android:string/bar");
OverlayableItem overlayable2(group);
- overlayable2.policies = OverlayableItem::Policy::kProduct;
+ overlayable2.policies = PolicyFlags::PRODUCT_PARTITION;
ASSERT_TRUE(table.SetOverlayable(bar, overlayable2, test::GetDiagnostics()));
const ResourceName baz = test::ParseNameOrDie("android:string/baz");
OverlayableItem overlayable3(group);
- overlayable3.policies = OverlayableItem::Policy::kVendor;
+ overlayable3.policies = PolicyFlags::VENDOR_PARTITION;
ASSERT_TRUE(table.SetOverlayable(baz, overlayable3, test::GetDiagnostics()));
}
@@ -296,12 +298,12 @@
const ResourceName foo = test::ParseNameOrDie("android:string/foo");
OverlayableItem overlayable_item(std::make_shared<Overlayable>("Name", "overlay://theme"));
- overlayable_item.policies = OverlayableItem::Policy::kProduct;
+ overlayable_item.policies = PolicyFlags::PRODUCT_PARTITION;
ASSERT_TRUE(table.SetOverlayable(foo, overlayable_item, test::GetDiagnostics()));
const ResourceName bar = test::ParseNameOrDie("android:string/bar");
OverlayableItem overlayable_item2(std::make_shared<Overlayable>("Name2", "overlay://theme"));
- overlayable_item2.policies = OverlayableItem::Policy::kProduct;
+ overlayable_item2.policies = PolicyFlags::PRODUCT_PARTITION;
ASSERT_TRUE(table.SetOverlayable(bar, overlayable_item2, test::GetDiagnostics()));
}
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 8a2f5af..ab9ce66 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -167,6 +167,7 @@
SIGNATURE = 5;
ODM = 6;
OEM = 7;
+ ACTOR = 8;
}
// The location of the <item> declaration in source.
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 5b6935b..bf886c2 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1766,9 +1766,12 @@
return 1;
}
- // First extract the Package name without modifying it (via --rename-manifest-package).
- if (Maybe<AppInfo> maybe_app_info =
+ // Determine the package name under which to merge resources.
+ if (options_.rename_resources_package) {
+ context_->SetCompilationPackage(options_.rename_resources_package.value());
+ } else if (Maybe<AppInfo> maybe_app_info =
ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) {
+ // Extract the package name from the manifest ignoring the value of --rename-manifest-package.
const AppInfo& app_info = maybe_app_info.value();
context_->SetCompilationPackage(app_info.package);
}
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 4722358..e7be434 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -45,6 +45,7 @@
bool auto_add_overlay = false;
bool override_styles_instead_of_overlaying = false;
OutputFormat output_format = OutputFormat::kApk;
+ Maybe<std::string> rename_resources_package;
// Java/Proguard options.
Maybe<std::string> generate_java_class_path;
@@ -256,6 +257,8 @@
&options_.override_styles_instead_of_overlaying);
AddOptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.",
&options_.manifest_fixer_options.rename_manifest_package);
+ AddOptionalFlag("--rename-resources-package", "Renames the package in resources table",
+ &options_.rename_resources_package);
AddOptionalFlag("--rename-instrumentation-target-package",
"Changes the name of the target package for instrumentation. Most useful\n"
"when used in conjunction with --rename-manifest-package.",
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index fcd6aaa..f362744 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -455,35 +455,6 @@
const ResTable_overlayable_policy_header* policy_header =
ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
- OverlayableItem::PolicyFlags policies = OverlayableItem::Policy::kNone;
- if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
- policies |= OverlayableItem::Policy::kPublic;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
- policies |= OverlayableItem::Policy::kSystem;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
- policies |= OverlayableItem::Policy::kVendor;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
- policies |= OverlayableItem::Policy::kProduct;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_SIGNATURE) {
- policies |= OverlayableItem::Policy::kSignature;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_ODM_PARTITION) {
- policies |= OverlayableItem::Policy::kOdm;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_OEM_PARTITION) {
- policies |= OverlayableItem::Policy::kOem;
- }
-
const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize));
const ResTable_ref* const ref_end = ref_begin
@@ -501,7 +472,7 @@
}
OverlayableItem overlayable_item(overlayable);
- overlayable_item.policies = policies;
+ overlayable_item.policies = policy_header->policy_flags;
if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) {
return false;
}
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index cbce8a5..4784ecf 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -233,7 +233,7 @@
struct OverlayableChunk {
std::string actor;
Source source;
- std::map<OverlayableItem::PolicyFlags, std::set<ResourceId>> policy_ids;
+ std::map<PolicyFlags, std::set<ResourceId>> policy_ids;
};
class PackageFlattener {
@@ -493,35 +493,12 @@
return false;
}
- uint32_t policy_flags = 0;
- if (item.policies & OverlayableItem::Policy::kPublic) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
- }
- if (item.policies & OverlayableItem::Policy::kSystem) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
- }
- if (item.policies & OverlayableItem::Policy::kVendor) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
- }
- if (item.policies & OverlayableItem::Policy::kProduct) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
- }
- if (item.policies & OverlayableItem::Policy::kSignature) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_SIGNATURE;
- }
- if (item.policies & OverlayableItem::Policy::kOdm) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_ODM_PARTITION;
- }
- if (item.policies & OverlayableItem::Policy::kOem) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_OEM_PARTITION;
- }
-
- auto policy = overlayable_chunk->policy_ids.find(policy_flags);
+ auto policy = overlayable_chunk->policy_ids.find(item.policies);
if (policy != overlayable_chunk->policy_ids.end()) {
policy->second.insert(id);
} else {
overlayable_chunk->policy_ids.insert(
- std::make_pair(policy_flags, std::set<ResourceId>{id}));
+ std::make_pair(item.policies, std::set<ResourceId>{id}));
}
}
}
@@ -559,7 +536,8 @@
ChunkWriter policy_writer(buffer);
auto* policy_type = policy_writer.StartChunk<ResTable_overlayable_policy_header>(
RES_TABLE_OVERLAYABLE_POLICY_TYPE);
- policy_type->policy_flags = util::HostToDevice32(static_cast<uint32_t>(policy_ids.first));
+ policy_type->policy_flags =
+ static_cast<PolicyFlags>(util::HostToDevice32(static_cast<uint32_t>(policy_ids.first)));
policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(
policy_ids.second.size()));
// Write the ids after the policy header
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index af2293f..59627ce 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -32,6 +32,8 @@
using ::testing::IsNull;
using ::testing::NotNull;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
class TableFlattenerTest : public ::testing::Test {
@@ -671,9 +673,9 @@
TEST_F(TableFlattenerTest, FlattenOverlayable) {
OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
- overlayable_item.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item.policies |= OverlayableItem::Policy::kSystem;
- overlayable_item.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item.policies |= PolicyFlags::SYSTEM_PARTITION;
+ overlayable_item.policies |= PolicyFlags::VENDOR_PARTITION;
std::string name = "com.app.test:integer/overlayable";
std::unique_ptr<ResourceTable> table =
@@ -691,27 +693,27 @@
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
- EXPECT_EQ(result_overlayable_item.policies, OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kVendor
- | OverlayableItem::Policy::kProduct);
+ EXPECT_EQ(result_overlayable_item.policies, PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::VENDOR_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION);
}
TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
auto overlayable = std::make_shared<Overlayable>("TestName", "overlay://theme");
std::string name_zero = "com.app.test:integer/overlayable_zero_item";
OverlayableItem overlayable_item_zero(overlayable);
- overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item_zero.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item_zero.policies |= PolicyFlags::SYSTEM_PARTITION;
std::string name_one = "com.app.test:integer/overlayable_one_item";
OverlayableItem overlayable_item_one(overlayable);
- overlayable_item_one.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_item_one.policies |= PolicyFlags::PUBLIC;
std::string name_two = "com.app.test:integer/overlayable_two_item";
OverlayableItem overlayable_item_two(overlayable);
- overlayable_item_two.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
- overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item_two.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item_two.policies |= PolicyFlags::SYSTEM_PARTITION;
+ overlayable_item_two.policies |= PolicyFlags::VENDOR_PARTITION;
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
@@ -732,47 +734,48 @@
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
- EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kProduct);
+ EXPECT_EQ(overlayable_item.policies, PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION);
search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
overlayable_item = search_result.value().entry->overlayable_item.value();
- EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
+ EXPECT_EQ(overlayable_item.policies, PolicyFlags::PUBLIC);
search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
overlayable_item = search_result.value().entry->overlayable_item.value();
- EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kProduct
- | OverlayableItem::Policy::kVendor);
+ EXPECT_EQ(overlayable_item.policies, PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION
+ | PolicyFlags::VENDOR_PARTITION);
}
TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
auto group = std::make_shared<Overlayable>("TestName", "overlay://theme");
std::string name_zero = "com.app.test:integer/overlayable_zero";
OverlayableItem overlayable_item_zero(group);
- overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item_zero.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item_zero.policies |= PolicyFlags::SYSTEM_PARTITION;
auto group_one = std::make_shared<Overlayable>("OtherName", "overlay://customization");
std::string name_one = "com.app.test:integer/overlayable_one";
OverlayableItem overlayable_item_one(group_one);
- overlayable_item_one.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_item_one.policies |= PolicyFlags::PUBLIC;
std::string name_two = "com.app.test:integer/overlayable_two";
OverlayableItem overlayable_item_two(group);
- overlayable_item_two.policies |= OverlayableItem::Policy::kOdm;
- overlayable_item_two.policies |= OverlayableItem::Policy::kOem;
- overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item_two.policies |= PolicyFlags::ODM_PARTITION;
+ overlayable_item_two.policies |= PolicyFlags::OEM_PARTITION;
+ overlayable_item_two.policies |= PolicyFlags::VENDOR_PARTITION;
std::string name_three = "com.app.test:integer/overlayable_three";
OverlayableItem overlayable_item_three(group_one);
- overlayable_item_three.policies |= OverlayableItem::Policy::kSignature;
+ overlayable_item_three.policies |= PolicyFlags::SIGNATURE;
+ overlayable_item_three.policies |= PolicyFlags::ACTOR_SIGNATURE;
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
@@ -796,8 +799,8 @@
OverlayableItem& result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
- EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kProduct);
+ EXPECT_EQ(result_overlayable.policies, PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION);
search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
ASSERT_TRUE(search_result);
@@ -806,7 +809,7 @@
result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
- EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic);
+ EXPECT_EQ(result_overlayable.policies, PolicyFlags::PUBLIC);
search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
ASSERT_TRUE(search_result);
@@ -815,9 +818,9 @@
result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
- EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kOdm
- | OverlayableItem::Policy::kOem
- | OverlayableItem::Policy::kVendor);
+ EXPECT_EQ(result_overlayable.policies, PolicyFlags::ODM_PARTITION
+ | PolicyFlags::OEM_PARTITION
+ | PolicyFlags::VENDOR_PARTITION);
search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
ASSERT_TRUE(search_result);
@@ -826,7 +829,8 @@
result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
- EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSignature);
+ EXPECT_EQ(result_overlayable.policies, PolicyFlags::SIGNATURE
+ | PolicyFlags::ACTOR_SIGNATURE);
}
TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) {
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 4cd6e93..2fd01d7 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -30,6 +30,8 @@
using ::android::LocaleValue;
using ::android::ResStringPool;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
namespace {
@@ -379,25 +381,28 @@
for (const int policy : pb_overlayable.policy()) {
switch (policy) {
case pb::OverlayableItem::PUBLIC:
- out_overlayable->policies |= OverlayableItem::Policy::kPublic;
+ out_overlayable->policies |= PolicyFlags::PUBLIC;
break;
case pb::OverlayableItem::SYSTEM:
- out_overlayable->policies |= OverlayableItem::Policy::kSystem;
+ out_overlayable->policies |= PolicyFlags::SYSTEM_PARTITION;
break;
case pb::OverlayableItem::VENDOR:
- out_overlayable->policies |= OverlayableItem::Policy::kVendor;
+ out_overlayable->policies |= PolicyFlags::VENDOR_PARTITION;
break;
case pb::OverlayableItem::PRODUCT:
- out_overlayable->policies |= OverlayableItem::Policy::kProduct;
+ out_overlayable->policies |= PolicyFlags::PRODUCT_PARTITION;
break;
case pb::OverlayableItem::SIGNATURE:
- out_overlayable->policies |= OverlayableItem::Policy::kSignature;
+ out_overlayable->policies |= PolicyFlags::SIGNATURE;
break;
case pb::OverlayableItem::ODM:
- out_overlayable->policies |= OverlayableItem::Policy::kOdm;
+ out_overlayable->policies |= PolicyFlags::ODM_PARTITION;
break;
case pb::OverlayableItem::OEM:
- out_overlayable->policies |= OverlayableItem::Policy::kOem;
+ out_overlayable->policies |= PolicyFlags::OEM_PARTITION;
+ break;
+ case pb::OverlayableItem::ACTOR:
+ out_overlayable->policies |= PolicyFlags::ACTOR_SIGNATURE;
break;
default:
*out_error = "unknown overlayable policy";
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index d9f6c19..ba6df22 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -21,6 +21,8 @@
using android::ConfigDescription;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool, IDiagnostics* diag) {
@@ -299,27 +301,30 @@
pb::OverlayableItem* pb_overlayable_item = pb_entry->mutable_overlayable_item();
pb_overlayable_item->set_overlayable_idx(i);
- if (overlayable_item.policies & OverlayableItem::Policy::kPublic) {
+ if (overlayable_item.policies & PolicyFlags::PUBLIC) {
pb_overlayable_item->add_policy(pb::OverlayableItem::PUBLIC);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kProduct) {
+ if (overlayable_item.policies & PolicyFlags::PRODUCT_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kSystem) {
+ if (overlayable_item.policies & PolicyFlags::SYSTEM_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::SYSTEM);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kVendor) {
+ if (overlayable_item.policies & PolicyFlags::VENDOR_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kSignature) {
+ if (overlayable_item.policies & PolicyFlags::SIGNATURE) {
pb_overlayable_item->add_policy(pb::OverlayableItem::SIGNATURE);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kOdm) {
+ if (overlayable_item.policies & PolicyFlags::ODM_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::ODM);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kOem) {
+ if (overlayable_item.policies & PolicyFlags::OEM_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::OEM);
}
+ if (overlayable_item.policies & PolicyFlags::ACTOR_SIGNATURE) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::ACTOR);
+ }
if (source_pool != nullptr) {
SerializeSourceToPb(overlayable_item.source, source_pool,
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 61a8335..1a7de6d 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -28,6 +28,8 @@
using ::testing::SizeIs;
using ::testing::StrEq;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
class MockFileCollection : public io::IFileCollection {
@@ -171,7 +173,7 @@
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
EXPECT_THAT(result_overlayable_item.overlayable->source.line, Eq(40));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::NONE));
EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
EXPECT_THAT(result_overlayable_item.source.line, Eq(42));
}
@@ -516,23 +518,28 @@
TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
OverlayableItem overlayable_item_foo(std::make_shared<Overlayable>(
"CustomizableResources", "overlay://customization"));
- overlayable_item_foo.policies |= OverlayableItem::Policy::kSystem;
- overlayable_item_foo.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_foo.policies |= PolicyFlags::SYSTEM_PARTITION;
+ overlayable_item_foo.policies |= PolicyFlags::PRODUCT_PARTITION;
OverlayableItem overlayable_item_bar(std::make_shared<Overlayable>(
"TaskBar", "overlay://theme"));
- overlayable_item_bar.policies |= OverlayableItem::Policy::kPublic;
- overlayable_item_bar.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item_bar.policies |= PolicyFlags::PUBLIC;
+ overlayable_item_bar.policies |= PolicyFlags::VENDOR_PARTITION;
OverlayableItem overlayable_item_baz(std::make_shared<Overlayable>(
"FontPack", "overlay://theme"));
- overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_item_baz.policies |= PolicyFlags::PUBLIC;
OverlayableItem overlayable_item_boz(std::make_shared<Overlayable>(
"IconPack", "overlay://theme"));
- overlayable_item_boz.policies |= OverlayableItem::Policy::kSignature;
- overlayable_item_boz.policies |= OverlayableItem::Policy::kOdm;
- overlayable_item_boz.policies |= OverlayableItem::Policy::kOem;
+ overlayable_item_boz.policies |= PolicyFlags::SIGNATURE;
+ overlayable_item_boz.policies |= PolicyFlags::ODM_PARTITION;
+ overlayable_item_boz.policies |= PolicyFlags::OEM_PARTITION;
+
+ OverlayableItem overlayable_item_actor_config(std::make_shared<Overlayable>(
+ "ActorConfig", "overlay://theme"));
+ overlayable_item_actor_config.policies |= PolicyFlags::SIGNATURE;
+ overlayable_item_actor_config.policies |= PolicyFlags::ACTOR_SIGNATURE;
OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
"Other", "overlay://customization"));
@@ -546,6 +553,7 @@
.SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
.SetOverlayable("com.app.a:bool/boz", overlayable_item_boz)
.SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
+ .SetOverlayable("com.app.a:bool/actor_config", overlayable_item_actor_config)
.AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
.Build();
@@ -565,8 +573,8 @@
OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("CustomizableResources"));
EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://customization"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kProduct));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
ASSERT_TRUE(search_result);
@@ -574,8 +582,8 @@
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("TaskBar"));
EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
- | OverlayableItem::Policy::kVendor));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::PUBLIC
+ | PolicyFlags::VENDOR_PARTITION));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
ASSERT_TRUE(search_result);
@@ -583,7 +591,7 @@
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("FontPack"));
EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::PUBLIC));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/boz"));
ASSERT_TRUE(search_result);
@@ -591,16 +599,25 @@
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("IconPack"));
EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature
- | OverlayableItem::Policy::kOdm
- | OverlayableItem::Policy::kOem));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::SIGNATURE
+ | PolicyFlags::ODM_PARTITION
+ | PolicyFlags::OEM_PARTITION));
+
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/actor_config"));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(overlayable_item.overlayable->name, Eq("ActorConfig"));
+ EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::SIGNATURE
+ | PolicyFlags::ACTOR_SIGNATURE));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
ASSERT_TRUE(search_result);
ASSERT_TRUE(search_result.value().entry->overlayable_item);
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("Other"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::NONE));
EXPECT_THAT(overlayable_item.comment, Eq("comment"));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 0be4ccf..69cf5ee 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -29,6 +29,8 @@
using ::testing::StrEq;
using ::testing::UnorderedElementsAreArray;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
struct TableMergerTest : public ::testing::Test {
@@ -487,8 +489,8 @@
auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item(overlayable);
- overlayable_item.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item.policies |= PolicyFlags::VENDOR_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
@@ -516,8 +518,8 @@
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
- | OverlayableItem::Policy::kVendor));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION
+ | PolicyFlags::VENDOR_PARTITION));
}
TEST_F(TableMergerTest, SetOverlayableLater) {
@@ -530,8 +532,8 @@
.Build();
OverlayableItem overlayable_item(overlayable);
- overlayable_item.policies |= OverlayableItem::Policy::kPublic;
- overlayable_item.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item.policies |= PolicyFlags::PUBLIC;
+ overlayable_item.policies |= PolicyFlags::SYSTEM_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -552,15 +554,15 @@
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
- | OverlayableItem::Policy::kSystem));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PUBLIC
+ | PolicyFlags::SYSTEM_PARTITION));
}
TEST_F(TableMergerTest, SameResourceDifferentNameFail) {
auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item_first(overlayable_first);
- overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -570,7 +572,7 @@
auto overlayable_second = std::make_shared<Overlayable>("ThemeResources",
"overlay://customization");
OverlayableItem overlayable_item_second(overlayable_second);
- overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_second.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -589,7 +591,7 @@
auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item_first(overlayable_first);
- overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -599,7 +601,7 @@
auto overlayable_second = std::make_shared<Overlayable>("CustomizableResources",
"overlay://theme");
OverlayableItem overlayable_item_second(overlayable_second);
- overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_second.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -618,7 +620,7 @@
auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item_first(overlayable_first);
- overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -628,7 +630,7 @@
auto overlayable_second = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item_second(overlayable_second);
- overlayable_item_second.policies |= OverlayableItem::Policy::kSignature;
+ overlayable_item_second.policies |= PolicyFlags::SIGNATURE;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -648,7 +650,7 @@
"overlay://customization");
OverlayableItem overlayable_item_first(overlayable);
- overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -656,7 +658,7 @@
.Build();
OverlayableItem overlayable_item_second(overlayable);
- overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_second.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
diff --git a/wifi/Android.bp b/wifi/Android.bp
index e253d6d..91174d3 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -47,7 +47,7 @@
// framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
// to a separate package.
"java/android/net/wifi/WifiNetworkScoreCache.java",
- "java/android/net/wifi/WifiOemMigrationHook.java",
+ "java/android/net/wifi/WifiMigration.java",
"java/android/net/wifi/nl80211/*.java",
":libwificond_ipc_aidl",
],
diff --git a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
index 462a978..d691f41 100644
--- a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
+++ b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
@@ -16,16 +16,14 @@
package android.net.wifi;
-import android.net.NetworkScore;
-
/**
- * Interface for Wi-Fi network score callback.
+ * Interface for Wi-Fi score callback.
*
* @hide
*/
oneway interface IScoreChangeCallback
{
- void onScoreChange(int sessionId, in NetworkScore score);
+ void onScoreChange(int sessionId, int score);
void onTriggerUpdateOfWifiUsabilityStats(int sessionId);
}
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 51927af..9256c57 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -24,8 +24,6 @@
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
@@ -861,19 +859,7 @@
}
}
- /**
- * Construct an empty scan result.
- *
- * Test code has a need to construct a ScanResult in a specific state.
- * (Note that mocking using Mockito does not work if the object needs to be parceled and
- * unparceled.)
- * Export a @SystemApi default constructor to allow tests to construct an empty ScanResult
- * object. The test can then directly set the fields it cares about.
- *
- * @hide
- */
- @SystemApi
- @VisibleForTesting
+ /** Construct an empty scan result. */
public ScanResult() {
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index c48e895..0229b84 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1713,7 +1713,7 @@
* @return network disable reason string, or null if the reason is invalid.
*/
@Nullable
- public static String getNetworkDisableReasonString(
+ public static String getNetworkSelectionDisableReasonString(
@NetworkSelectionDisableReason int reason) {
DisableReasonInfo info = DISABLE_REASON_INFOS.get(reason);
if (info == null) {
@@ -1727,8 +1727,8 @@
* @return current network disable reason in String (for debug purpose)
* @hide
*/
- public String getNetworkDisableReasonString() {
- return getNetworkDisableReasonString(mNetworkSelectionDisableReason);
+ public String getNetworkSelectionDisableReasonString() {
+ return getNetworkSelectionDisableReasonString(mNetworkSelectionDisableReason);
}
/**
@@ -2189,17 +2189,21 @@
sbuf.append(" NetworkSelectionStatus ")
- .append(mNetworkSelectionStatus.getNetworkStatusString() + "\n");
+ .append(mNetworkSelectionStatus.getNetworkStatusString())
+ .append("\n");
if (mNetworkSelectionStatus.getNetworkSelectionDisableReason() > 0) {
sbuf.append(" mNetworkSelectionDisableReason ")
- .append(mNetworkSelectionStatus.getNetworkDisableReasonString() + "\n");
+ .append(mNetworkSelectionStatus.getNetworkSelectionDisableReasonString())
+ .append("\n");
for (int index = NetworkSelectionStatus.DISABLED_NONE;
index < NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; index++) {
if (mNetworkSelectionStatus.getDisableReasonCounter(index) != 0) {
- sbuf.append(NetworkSelectionStatus.getNetworkDisableReasonString(index)
- + " counter:" + mNetworkSelectionStatus.getDisableReasonCounter(index)
- + "\n");
+ sbuf.append(
+ NetworkSelectionStatus.getNetworkSelectionDisableReasonString(index))
+ .append(" counter:")
+ .append(mNetworkSelectionStatus.getDisableReasonCounter(index))
+ .append("\n");
}
}
}
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 0c306b4..142854a 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -28,8 +28,6 @@
import android.os.Parcelable;
import android.text.TextUtils;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -380,20 +378,7 @@
}
}
- /**
- * WifiInfo exports an immutable public API.
- * However, test code has a need to construct a WifiInfo in a specific state.
- * (Note that mocking using Mockito does not work if the object needs to be parceled and
- * unparceled.)
- * Export a @SystemApi Builder to allow tests to construct a WifiInfo object
- * in the desired state, without sacrificing WifiInfo's immutability.
- *
- * @hide
- */
- // This builder was not made public to reduce confusion for external developers as there are
- // no legitimate uses for this builder except for testing.
- @SystemApi
- @VisibleForTesting
+ /** Builder for WifiInfo */
public static final class Builder {
private final WifiInfo mWifiInfo = new WifiInfo();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7d4b632..48526ab 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -39,7 +39,6 @@
import android.net.DhcpInfo;
import android.net.MacAddress;
import android.net.Network;
-import android.net.NetworkScore;
import android.net.NetworkStack;
import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.wifi.hotspot2.OsuProvider;
@@ -1881,9 +1880,11 @@
* NOTE:
* <li> These networks are just a suggestion to the platform. The platform will ultimately
* decide on which network the device connects to. </li>
- * <li> When an app is uninstalled, all its suggested networks are discarded. If the device is
- * currently connected to a suggested network which is being removed then the device will
- * disconnect from that network.</li>
+ * <li> When an app is uninstalled or disabled, all its suggested networks are discarded.
+ * If the device is currently connected to a suggested network which is being removed then the
+ * device will disconnect from that network.</li>
+ * <li> If user reset network settings, all added suggestions will be discarded. Apps can use
+ * {@link #getNetworkSuggestions()} to check if their suggestions are in the device.</li>
* <li> In-place modification of existing suggestions are allowed.
* If the provided suggestions {@link WifiNetworkSuggestion#equals(Object)} any previously
* provided suggestions by the app. Previous suggestions will be updated</li>
@@ -3769,11 +3770,19 @@
}
/**
- * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the current
- * soft AP state and number of connected devices immediately after a successful call to this API
- * via callback. Note that receiving an immediate WIFI_AP_STATE_FAILED value for soft AP state
- * indicates that the latest attempt to start soft AP has failed. Caller can unregister a
- * previously registered callback using {@link #unregisterSoftApCallback}
+ * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the
+ * following callbacks on registration:
+ * <ul>
+ * <li> {@link SoftApCallback#onStateChanged(int, int)}</li>
+ * <li> {@link SoftApCallback#onConnectedClientsChanged(List<WifiClient>)}</li>
+ * <li> {@link SoftApCallback#onInfoChanged(SoftApInfo)}</li>
+ * <li> {@link SoftApCallback#onCapabilityChanged(SoftApCapability)}</li>
+ * </ul>
+ * These will be dispatched on registration to provide the caller with the current state
+ * (and are not an indication of any current change). Note that receiving an immediate
+ * WIFI_AP_STATE_FAILED value for soft AP state indicates that the latest attempt to start
+ * soft AP has failed. Caller can unregister a previously registered callback using
+ * {@link #unregisterSoftApCallback}
* <p>
* Applications should have the
* {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers
@@ -5075,7 +5084,7 @@
}
/**
- * Returns soft ap config from the backed up data.
+ * Returns soft ap config from the backed up data or null if data is invalid.
* @param data byte stream in the same format produced by {@link #retrieveSoftApBackupData()}
*
* @hide
@@ -5986,11 +5995,10 @@
*
* @param sessionId The ID to indicate current Wi-Fi network connection obtained from
* {@link WifiConnectedNetworkScorer#start(int)}.
- * @param score The {@link android.net.NetworkScore} object representing the
- * characteristics of current Wi-Fi network. Populated by connected network
- * scorer in applications.
+ * @param score The score representing link quality of current Wi-Fi network connection.
+ * Populated by connected network scorer in applications..
*/
- void onScoreChange(int sessionId, @NonNull NetworkScore score);
+ void onScoreChange(int sessionId, int score);
/**
* Called by applications to trigger an update of {@link WifiUsabilityStatsEntry}.
@@ -6016,7 +6024,7 @@
}
@Override
- public void onScoreChange(int sessionId, @NonNull NetworkScore score) {
+ public void onScoreChange(int sessionId, int score) {
try {
mScoreChangeCallback.onScoreChange(sessionId, score);
} catch (RemoteException e) {
diff --git a/wifi/java/android/net/wifi/WifiOemMigrationHook.java b/wifi/java/android/net/wifi/WifiMigration.java
similarity index 93%
rename from wifi/java/android/net/wifi/WifiOemMigrationHook.java
rename to wifi/java/android/net/wifi/WifiMigration.java
index 5301dd0..a3482d7 100755
--- a/wifi/java/android/net/wifi/WifiOemMigrationHook.java
+++ b/wifi/java/android/net/wifi/WifiMigration.java
@@ -34,9 +34,9 @@
* @hide
*/
@SystemApi
-public final class WifiOemMigrationHook {
+public final class WifiMigration {
- private WifiOemMigrationHook() { }
+ private WifiMigration() { }
/**
* Container for all the wifi config data to migrate.
@@ -161,16 +161,16 @@
* Load data from OEM's config store.
* <p>
* Note:
- * <li> OEM's need to implement {@link #loadFromConfigStore()} ()} only if their
- * existing config store format or file locations differs from the vanilla AOSP implementation (
- * which is what the wifi mainline module understands).
+ * <li>OEMs need to implement {@link #loadFromConfigStore()} ()} only if their
+ * existing config store format or file locations differs from the vanilla AOSP implementation.
* </li>
- * <li> The wifi mainline module will invoke {@link #loadFromConfigStore()} method on every
+ * <li>The wifi mainline module will invoke {@link #loadFromConfigStore()} method on every
* bootup, its the responsibility of the OEM implementation to ensure that this method returns
* non-null data only on the first bootup. Once the migration is done, the OEM can safely delete
- * their config store files and then return null on any subsequent reboots. The first & only
- * relevant invocation of {@link #loadFromConfigStore()} occurs when a previously released
- * device upgrades to the wifi mainline module from an OEM implementation of the wifi stack.
+ * their config store files when {@link #removeConfigStore()} is invoked.
+ * <li>The first & only relevant invocation of {@link #loadFromConfigStore()} occurs when a
+ * previously released device upgrades to the wifi mainline module from an OEM implementation
+ * of the wifi stack.
* </li>
*
* @return Instance of {@link ConfigStoreMigrationData} for migrating data, null if no
@@ -178,11 +178,27 @@
*/
@Nullable
public static ConfigStoreMigrationData loadFromConfigStore() {
- // Note: OEM's should add code to parse data from their config store format here!
+ // Note: OEMs should add code to parse data from their config store format here!
return null;
}
/**
+ * Remove OEM's config store.
+ * <p>
+ * Note:
+ * <li>OEMs need to implement {@link #removeConfigStore()} only if their
+ * existing config store format or file locations differs from the vanilla AOSP implementation (
+ * which is what the wifi mainline module understands).
+ * </li>
+ * <li> The wifi mainline module will invoke {@link #removeConfigStore()} after it migrates
+ * all the existing data retrieved from {@link #loadFromConfigStore()}.
+ * </li>
+ */
+ public static void removeConfigStore() {
+ // Note: OEMs should remove their custom config store files here!
+ }
+
+ /**
* Container for all the wifi settings data to migrate.
*/
public static final class SettingsMigrationData implements Parcelable {
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index a854a4b..6dbb0bd 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -753,7 +753,7 @@
boolean isUserInteractionRequired,
boolean isUserAllowedToManuallyConnect,
boolean isInitialAutoJoinEnabled,
- boolean isNetworkUntrusted) {
+ boolean isNetworkUntrusted) {
checkNotNull(networkConfiguration);
this.wifiConfiguration = networkConfiguration;
this.passpointConfiguration = passpointConfiguration;
@@ -858,13 +858,106 @@
}
/**
+ * Get the BSSID, or null if unset.
+ * @see Builder#setBssid(MacAddress)
+ */
+ @Nullable
+ public MacAddress getBssid() {
+ if (wifiConfiguration.BSSID == null) {
+ return null;
+ }
+ return MacAddress.fromString(wifiConfiguration.BSSID);
+ }
+
+ /** @see Builder#setCredentialSharedWithUser(boolean) */
+ public boolean isCredentialSharedWithUser() {
+ return isUserAllowedToManuallyConnect;
+ }
+
+ /** @see Builder#setIsAppInteractionRequired(boolean) */
+ public boolean isAppInteractionRequired() {
+ return isAppInteractionRequired;
+ }
+
+ /** @see Builder#setIsEnhancedOpen(boolean) */
+ public boolean isEnhancedOpen() {
+ return wifiConfiguration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE);
+ }
+
+ /** @see Builder#setIsHiddenSsid(boolean) */
+ public boolean isHiddenSsid() {
+ return wifiConfiguration.hiddenSSID;
+ }
+
+ /** @see Builder#setIsInitialAutojoinEnabled(boolean) */
+ public boolean isInitialAutojoinEnabled() {
+ return isInitialAutoJoinEnabled;
+ }
+
+ /** @see Builder#setIsMetered(boolean) */
+ public boolean isMetered() {
+ return wifiConfiguration.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED;
+ }
+
+ /** @see Builder#setIsUserInteractionRequired(boolean) */
+ public boolean isUserInteractionRequired() {
+ return isUserInteractionRequired;
+ }
+
+ /**
* Get the {@link PasspointConfiguration} associated with this Suggestion, or null if this
* Suggestion is not for a Passpoint network.
- * @hide
*/
- @SystemApi
@Nullable
- public PasspointConfiguration getPasspointConfiguration() {
+ public PasspointConfiguration getPasspointConfig() {
return passpointConfiguration;
}
+
+ /** @see Builder#setPriority(int) */
+ @IntRange(from = 0)
+ public int getPriority() {
+ return wifiConfiguration.priority;
+ }
+
+ /**
+ * Return the SSID of the network, or null if this is a Passpoint network.
+ * @see Builder#setSsid(String)
+ */
+ @Nullable
+ public String getSsid() {
+ if (wifiConfiguration.SSID == null) {
+ return null;
+ }
+ return WifiInfo.sanitizeSsid(wifiConfiguration.SSID);
+ }
+
+ /** @see Builder#setUntrusted(boolean) */
+ public boolean isUntrusted() {
+ return isNetworkUntrusted;
+ }
+
+ /**
+ * Get the WifiEnterpriseConfig, or null if unset.
+ * @see Builder#setWapiEnterpriseConfig(WifiEnterpriseConfig)
+ * @see Builder#setWpa2EnterpriseConfig(WifiEnterpriseConfig)
+ * @see Builder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)
+ */
+ @Nullable
+ public WifiEnterpriseConfig getEnterpriseConfig() {
+ return wifiConfiguration.enterpriseConfig;
+ }
+
+ /**
+ * Get the passphrase, or null if unset.
+ * @see Builder#setWapiPassphrase(String)
+ * @see Builder#setWpa2Passphrase(String)
+ * @see Builder#setWpa3Passphrase(String)
+ */
+ @Nullable
+ public String getPassphrase() {
+ if (wifiConfiguration.preSharedKey == null) {
+ return null;
+ }
+ return WifiInfo.removeDoubleQuotes(wifiConfiguration.preSharedKey);
+ }
}
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index b4eb30b..5e48919 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -36,6 +36,7 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.WorkSource;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
@@ -744,6 +745,25 @@
public PnoNetwork(String ssid) {
this.ssid = ssid;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ssid, flags, authBitField);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof PnoNetwork)) {
+ return false;
+ }
+ PnoNetwork lhs = (PnoNetwork) obj;
+ return TextUtils.equals(this.ssid, lhs.ssid)
+ && this.flags == lhs.flags
+ && this.authBitField == lhs.authBitField;
+ }
}
/** Connected vs Disconnected PNO flag {@hide} */