Merge "Reduce allocations in hot paths"
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 50d23ad2..4616ced 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -197,20 +197,17 @@
api_file: "api/module-lib-current.txt",
removed_api_file: "api/module-lib-removed.txt",
},
- // TODO(b/147559833) enable the compatibility check against the last release API
- // and the API lint
- //last_released: {
- // api_file: ":last-released-module-lib-api",
- // removed_api_file: "api/module-lib-removed.txt",
- // baseline_file: ":module-lib-api-incompatibilities-with-last-released"
- //},
- //api_lint: {
- // enabled: true,
- // new_since: ":last-released-module-lib-api",
- // baseline_file: "api/module-lib-lint-baseline.txt",
- //},
+ last_released: {
+ api_file: ":last-released-module-lib-api",
+ removed_api_file: "api/module-lib-removed.txt",
+ baseline_file: ":module-lib-api-incompatibilities-with-last-released"
+ },
+ api_lint: {
+ enabled: true,
+ new_since: ":last-released-module-lib-api",
+ baseline_file: "api/module-lib-lint-baseline.txt",
+ },
},
- //jdiff_enabled: true,
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 088cadb..8a9c774 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -896,7 +896,7 @@
* @param flags Flags for the observer.
*/
public TriggerContentUri(@NonNull Uri uri, @Flags int flags) {
- mUri = uri;
+ mUri = Objects.requireNonNull(uri);
mFlags = flags;
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index 08b1c2b..abf78c6 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -56,6 +56,12 @@
* instantiate this class directly; instead, retrieve it through
* {@link android.content.Context#getSystemService
* Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)}.
+ *
+ * <p class="caution"><strong>Note:</strong> Beginning with API 30
+ * ({@link android.os.Build.VERSION_CODES#R}), JobScheduler will throttle runaway applications.
+ * Calling {@link #schedule(JobInfo)} and other such methods with very high frequency is indicative
+ * of an app bug and so, to make sure the system doesn't get overwhelmed, JobScheduler will begin
+ * to throttle apps that show buggy behavior, regardless of target SDK version.
*/
@SystemService(Context.JOB_SCHEDULER_SERVICE)
public abstract class JobScheduler {
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index 4ffcf8a..4b4fb96 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -80,27 +80,22 @@
}
/**
- * Add the specified package to the power save whitelist.
- *
- * @return true if the package was successfully added to the whitelist
+ * Add the specified package to the permanent power save whitelist.
*/
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
- public boolean addToWhitelist(@NonNull String packageName) {
- return addToWhitelist(Collections.singletonList(packageName)) == 1;
+ public void addToWhitelist(@NonNull String packageName) {
+ addToWhitelist(Collections.singletonList(packageName));
}
/**
- * Add the specified packages to the power save whitelist.
- *
- * @return the number of packages that were successfully added to the whitelist
+ * Add the specified packages to the permanent power save whitelist.
*/
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
- public int addToWhitelist(@NonNull List<String> packageNames) {
+ public void addToWhitelist(@NonNull List<String> packageNames) {
try {
- return mService.addPowerSaveWhitelistApps(packageNames);
+ mService.addPowerSaveWhitelistApps(packageNames);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
- return 0;
}
}
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
index 1072406..7833a03 100644
--- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
@@ -16,6 +16,7 @@
package com.android.server.job;
+import android.annotation.NonNull;
import android.app.job.JobInfo;
import android.util.proto.ProtoOutputStream;
@@ -44,6 +45,10 @@
void removeBackingUpUid(int uid);
void clearAllBackingUpUids();
+ /** Returns the package responsible for backing up media on the device. */
+ @NonNull
+ String getMediaBackupPackage();
+
/**
* The user has started interacting with the app. Take any appropriate action.
*/
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index d2d942a..dc72d6d 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -85,6 +85,7 @@
/**
* Checks if an app has been idle for a while and filters out apps that are excluded.
* It returns false if the current system state allows all apps to be considered active.
+ * This happens if the device is plugged in or otherwise temporarily allowed to make exceptions.
* Called by interface impls.
*/
boolean isAppIdleFiltered(String packageName, int appId, int userId,
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 b516279..ff7944d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -37,8 +37,6 @@
import android.app.job.JobWorkItem;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -57,7 +55,6 @@
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Binder;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -80,6 +77,7 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.ArrayUtils;
@@ -90,7 +88,6 @@
import com.android.server.DeviceIdleInternal;
import com.android.server.FgThread;
import com.android.server.LocalServices;
-import com.android.server.compat.PlatformCompat;
import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
import com.android.server.job.controllers.BackgroundJobsController;
@@ -155,16 +152,6 @@
/** The maximum number of jobs that we allow an unprivileged app to schedule */
private static final int MAX_JOBS_PER_APP = 100;
- /**
- * {@link #schedule(JobInfo)}, {@link #scheduleAsPackage(JobInfo, String, int, String)}, and
- * {@link #enqueue(JobInfo, JobWorkItem)} will throw a {@link IllegalStateException} if the app
- * calls the APIs too frequently.
- */
- @ChangeId
- // This means the change will be enabled for target SDK larger than 29 (Q), meaning R and up.
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
- protected static final long CRASH_ON_EXCEEDED_LIMIT = 144363383L;
-
@VisibleForTesting
public static Clock sSystemClock = Clock.systemUTC();
@@ -262,9 +249,11 @@
*/
private final List<JobRestriction> mJobRestrictions;
+ @NonNull
+ private final String mSystemGalleryPackage;
+
private final CountQuotaTracker mQuotaTracker;
private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()";
- private final PlatformCompat mPlatformCompat;
/**
* Queue of pending jobs. The JobServiceContext class will receive jobs from this list
@@ -986,9 +975,7 @@
Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times");
mAppStandbyInternal.restrictApp(
pkg, userId, UsageStatsManager.REASON_SUB_RESTRICT_BUGGY);
- if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION
- && mPlatformCompat.isChangeEnabledByPackageName(
- CRASH_ON_EXCEEDED_LIMIT, pkg, userId)) {
+ if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) {
final boolean isDebuggable;
synchronized (mLock) {
if (!mDebuggableApps.containsKey(packageName)) {
@@ -1370,8 +1357,6 @@
// Set up the app standby bucketing tracker
mStandbyTracker = new StandbyTracker();
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
- mPlatformCompat =
- (PlatformCompat) ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
mQuotaTracker = new CountQuotaTracker(context, Categorizer.SINGLE_CATEGORIZER);
mQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY,
mConstants.API_QUOTA_SCHEDULE_COUNT,
@@ -1413,6 +1398,9 @@
mJobRestrictions = new ArrayList<>();
mJobRestrictions.add(new ThermalStatusRestriction(this));
+ mSystemGalleryPackage = Objects.requireNonNull(
+ context.getString(R.string.config_systemGallery));
+
// If the job store determined that it can't yet reschedule persisted jobs,
// we need to start watching the clock.
if (!mJobs.jobTimesInflatedValid()) {
@@ -2378,6 +2366,11 @@
}
@Override
+ public String getMediaBackupPackage() {
+ return mSystemGalleryPackage;
+ }
+
+ @Override
public void reportAppUsage(String packageName, int userId) {
JobSchedulerService.this.reportAppUsage(packageName, userId);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
index a775cf5..5fcd774 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
@@ -344,7 +344,7 @@
mContext.getContentResolver().unregisterContentObserver(obs);
ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observerOfUser =
mObservers.get(obs.mUserId);
- if (observerOfUser != null) {
+ if (observerOfUser != null) {
observerOfUser.remove(obs.mUri);
}
}
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 1e89158..cf7f380 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
@@ -19,6 +19,7 @@
import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
+import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.app.AppGlobals;
@@ -30,6 +31,7 @@
import android.net.Uri;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.MediaStore;
import android.text.format.DateFormat;
import android.util.ArraySet;
import android.util.Pair;
@@ -207,6 +209,18 @@
*/
private int mDynamicConstraints = 0;
+ /**
+ * Indicates whether the job is responsible for backing up media, so we can be lenient in
+ * applying standby throttling.
+ *
+ * Doesn't exempt jobs with a deadline constraint, as they can be started without any content or
+ * network changes, in which case this exemption does not make sense.
+ *
+ * TODO(b/149519887): Use a more explicit signal, maybe an API flag, that the scheduling package
+ * needs to provide at the time of scheduling a job.
+ */
+ private final boolean mHasMediaBackupExemption;
+
// Set to true if doze constraint was satisfied due to app being whitelisted.
public boolean dozeWhitelisted;
@@ -415,9 +429,11 @@
this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
this.numFailures = numFailures;
+ boolean requiresNetwork = false;
int requiredConstraints = job.getConstraintFlags();
if (job.getRequiredNetwork() != null) {
requiredConstraints |= CONSTRAINT_CONNECTIVITY;
+ requiresNetwork = true;
}
if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
requiredConstraints |= CONSTRAINT_TIMING_DELAY;
@@ -425,8 +441,16 @@
if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
requiredConstraints |= CONSTRAINT_DEADLINE;
}
+ boolean mediaOnly = false;
if (job.getTriggerContentUris() != null) {
requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
+ mediaOnly = true;
+ for (JobInfo.TriggerContentUri uri : job.getTriggerContentUris()) {
+ if (!MediaStore.AUTHORITY.equals(uri.getUri().getAuthority())) {
+ mediaOnly = false;
+ break;
+ }
+ }
}
this.requiredConstraints = requiredConstraints;
mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
@@ -450,6 +474,9 @@
// our source UID into place.
job.getRequiredNetwork().networkCapabilities.setSingleUid(this.sourceUid);
}
+ final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
+ mHasMediaBackupExemption = !job.hasLateConstraint() && mediaOnly && requiresNetwork
+ && this.sourcePackageName.equals(jsi.getMediaBackupPackage());
}
/** Copy constructor: used specifically when cloning JobStatus objects for persistence,
@@ -545,7 +572,6 @@
int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
sourceUserId, elapsedNow);
- JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
return new JobStatus(job, callingUid, sourcePkg, sourceUserId,
standbyBucket, tag, 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
@@ -734,7 +760,14 @@
// like other ACTIVE apps.
return ACTIVE_INDEX;
}
- return getStandbyBucket();
+ final int actualBucket = getStandbyBucket();
+ if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX
+ && mHasMediaBackupExemption) {
+ // Cap it at WORKING_INDEX as media back up jobs are important to the user, and the
+ // source package may not have been used directly in a while.
+ return Math.min(WORKING_INDEX, actualBucket);
+ }
+ return actualBucket;
}
/** Returns the real standby bucket of the job. */
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 f1bfa04..e343478 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -48,6 +48,7 @@
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import android.annotation.NonNull;
@@ -71,9 +72,8 @@
import android.database.ContentObserver;
import android.hardware.display.DisplayManager;
import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkRequest;
import android.net.NetworkScoreManager;
+import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Build;
import android.os.Environment;
@@ -285,6 +285,7 @@
long mInitialForegroundServiceStartTimeoutMillis;
private volatile boolean mAppIdleEnabled;
+ private boolean mIsCharging;
private boolean mSystemServicesReady = false;
// There was a system update, defaults need to be initialized after services are ready
private boolean mPendingInitializeDefaults;
@@ -360,6 +361,11 @@
mHandler = new AppStandbyHandler(mInjector.getLooper());
mPackageManager = mContext.getPackageManager();
+ DeviceStateReceiver deviceStateReceiver = new DeviceStateReceiver();
+ IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING);
+ deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
+ mContext.registerReceiver(deviceStateReceiver, deviceStates);
+
synchronized (mAppIdleLock) {
mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(),
mInjector.elapsedRealtime());
@@ -417,6 +423,8 @@
if (mPendingOneTimeCheckIdleStates) {
postOneTimeCheckIdleStates();
}
+ } else if (phase == PHASE_BOOT_COMPLETED) {
+ setChargingState(mInjector.isCharging());
}
}
@@ -515,6 +523,16 @@
appUsage.bucketingReason, false);
}
+ @VisibleForTesting
+ void setChargingState(boolean isCharging) {
+ synchronized (mAppIdleLock) {
+ if (mIsCharging != isCharging) {
+ if (DEBUG) Slog.d(TAG, "Setting mIsCharging to " + isCharging);
+ mIsCharging = isCharging;
+ }
+ }
+ }
+
@Override
public void postCheckIdleStates(int userId) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
@@ -977,6 +995,11 @@
if (isAppSpecial(packageName, appId, userId)) {
return false;
} else {
+ synchronized (mAppIdleLock) {
+ if (!mAppIdleEnabled || mIsCharging) {
+ return false;
+ }
+ }
return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
}
}
@@ -1543,6 +1566,8 @@
pw.println();
pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
+ pw.print(" mIsCharging=");
+ pw.print(mIsCharging);
pw.println();
pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds));
pw.print("mElapsedThresholds="); pw.println(Arrays.toString(mAppStandbyElapsedThresholds));
@@ -1560,6 +1585,7 @@
private final Looper mLooper;
private IDeviceIdleController mDeviceIdleController;
private IBatteryStats mBatteryStats;
+ private BatteryManager mBatteryManager;
private PackageManagerInternal mPackageManagerInternal;
private DisplayManager mDisplayManager;
private PowerManager mPowerManager;
@@ -1593,6 +1619,7 @@
mDisplayManager = (DisplayManager) mContext.getSystemService(
Context.DISPLAY_SERVICE);
mPowerManager = mContext.getSystemService(PowerManager.class);
+ mBatteryManager = mContext.getSystemService(BatteryManager.class);
final ActivityManager activityManager =
(ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
@@ -1630,6 +1657,10 @@
return buildFlag && runtimeFlag;
}
+ boolean isCharging() {
+ return mBatteryManager.isCharging();
+ }
+
boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
return mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName);
}
@@ -1766,15 +1797,19 @@
}
};
- private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder().build();
-
- private final ConnectivityManager.NetworkCallback mNetworkCallback
- = new ConnectivityManager.NetworkCallback() {
+ private class DeviceStateReceiver extends BroadcastReceiver {
@Override
- public void onAvailable(Network network) {
- mConnectivityManager.unregisterNetworkCallback(this);
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case BatteryManager.ACTION_CHARGING:
+ setChargingState(true);
+ break;
+ case BatteryManager.ACTION_DISCHARGING:
+ setChargingState(false);
+ break;
+ }
}
- };
+ }
private final DisplayManager.DisplayListener mDisplayListener
= new DisplayManager.DisplayListener() {
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 3eed26b..7d18578 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -50,6 +50,7 @@
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.util.ParsableByteArray;
+import com.google.android.exoplayer2.video.ColorInfo;
import java.io.EOFException;
import java.io.IOException;
@@ -810,19 +811,17 @@
// Private static methods.
private static MediaFormat toMediaFormat(Format format) {
-
MediaFormat result = new MediaFormat();
- if (format.bitrate != Format.NO_VALUE) {
- result.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate);
- }
- if (format.channelCount != Format.NO_VALUE) {
- result.setInteger(MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
- }
+ setOptionalMediaFormatInt(result, MediaFormat.KEY_BIT_RATE, format.bitrate);
+ setOptionalMediaFormatInt(result, MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
- if (format.colorInfo != null) {
- result.setInteger(MediaFormat.KEY_COLOR_TRANSFER, format.colorInfo.colorTransfer);
- result.setInteger(MediaFormat.KEY_COLOR_RANGE, format.colorInfo.colorRange);
- result.setInteger(MediaFormat.KEY_COLOR_STANDARD, format.colorInfo.colorSpace);
+ ColorInfo colorInfo = format.colorInfo;
+ if (colorInfo != null) {
+ setOptionalMediaFormatInt(
+ result, MediaFormat.KEY_COLOR_TRANSFER, colorInfo.colorTransfer);
+ setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_RANGE, colorInfo.colorRange);
+ setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_STANDARD, colorInfo.colorSpace);
+
if (format.colorInfo.hdrStaticInfo != null) {
result.setByteBuffer(
MediaFormat.KEY_HDR_STATIC_INFO,
@@ -830,63 +829,50 @@
}
}
- if (format.sampleMimeType != null) {
- result.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
- }
- if (format.codecs != null) {
- result.setString(MediaFormat.KEY_CODECS_STRING, format.codecs);
- }
+ setOptionalMediaFormatString(result, MediaFormat.KEY_MIME, format.sampleMimeType);
+ setOptionalMediaFormatString(result, MediaFormat.KEY_CODECS_STRING, format.codecs);
if (format.frameRate != Format.NO_VALUE) {
result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate);
}
- if (format.width != Format.NO_VALUE) {
- result.setInteger(MediaFormat.KEY_WIDTH, format.width);
- }
- if (format.height != Format.NO_VALUE) {
- result.setInteger(MediaFormat.KEY_HEIGHT, format.height);
- }
+ setOptionalMediaFormatInt(result, MediaFormat.KEY_WIDTH, format.width);
+ setOptionalMediaFormatInt(result, MediaFormat.KEY_HEIGHT, format.height);
+
List<byte[]> initData = format.initializationData;
if (initData != null) {
for (int i = 0; i < initData.size(); i++) {
result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i)));
}
}
- if (format.language != null) {
- result.setString(MediaFormat.KEY_LANGUAGE, format.language);
- }
- if (format.maxInputSize != Format.NO_VALUE) {
- result.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
- }
- if (format.pcmEncoding != Format.NO_VALUE) {
- result.setInteger(MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding);
- }
- if (format.rotationDegrees != Format.NO_VALUE) {
- result.setInteger(MediaFormat.KEY_ROTATION, format.rotationDegrees);
- }
- if (format.sampleRate != Format.NO_VALUE) {
- result.setInteger(MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
- }
+ setOptionalMediaFormatString(result, MediaFormat.KEY_LANGUAGE, format.language);
+ setOptionalMediaFormatInt(result, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
+ setOptionalMediaFormatInt(result, MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding);
+ setOptionalMediaFormatInt(result, MediaFormat.KEY_ROTATION, format.rotationDegrees);
+ setOptionalMediaFormatInt(result, MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
+
int selectionFlags = format.selectionFlags;
- if ((selectionFlags & C.SELECTION_FLAG_AUTOSELECT) != 0) {
- result.setInteger(MediaFormat.KEY_IS_AUTOSELECT, 1);
+ result.setInteger(
+ MediaFormat.KEY_IS_AUTOSELECT, selectionFlags & C.SELECTION_FLAG_AUTOSELECT);
+ result.setInteger(MediaFormat.KEY_IS_DEFAULT, selectionFlags & C.SELECTION_FLAG_DEFAULT);
+ result.setInteger(
+ MediaFormat.KEY_IS_FORCED_SUBTITLE, selectionFlags & C.SELECTION_FLAG_FORCED);
+
+ setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_DELAY, format.encoderDelay);
+ setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_PADDING, format.encoderPadding);
+
+ if (format.pixelWidthHeightRatio != Format.NO_VALUE && format.pixelWidthHeightRatio != 0) {
+ int parWidth = 1;
+ int parHeight = 1;
+ if (format.pixelWidthHeightRatio < 1.0f) {
+ parHeight = 1 << 30;
+ parWidth = (int) (format.pixelWidthHeightRatio * parHeight);
+ } else if (format.pixelWidthHeightRatio > 1.0f) {
+ parWidth = 1 << 30;
+ parHeight = (int) (parWidth / format.pixelWidthHeightRatio);
+ }
+ result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, parWidth);
+ result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, parHeight);
+ result.setFloat("pixel-width-height-ratio-float", format.pixelWidthHeightRatio);
}
- if ((selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0) {
- result.setInteger(MediaFormat.KEY_IS_DEFAULT, 1);
- }
- if ((selectionFlags & C.SELECTION_FLAG_FORCED) != 0) {
- result.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, 1);
- }
- if (format.encoderDelay != Format.NO_VALUE) {
- result.setInteger(MediaFormat.KEY_ENCODER_DELAY, format.encoderDelay);
- }
- if (format.encoderPadding != Format.NO_VALUE) {
- result.setInteger(MediaFormat.KEY_ENCODER_PADDING, format.encoderPadding);
- }
- // TODO: Implement float to fraction conversion.
- // if (format.pixelWidthHeightRatio != Format.NO_VALUE) {
- // result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, );
- // result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, );
- // }
// LACK OF SUPPORT FOR:
// format.accessibilityChannel;
@@ -899,6 +885,19 @@
return result;
}
+ private static void setOptionalMediaFormatInt(MediaFormat mediaFormat, String key, int value) {
+ if (value != Format.NO_VALUE) {
+ mediaFormat.setInteger(key, value);
+ }
+ }
+
+ private static void setOptionalMediaFormatString(
+ MediaFormat mediaFormat, String key, @Nullable String value) {
+ if (value != null) {
+ mediaFormat.setString(key, value);
+ }
+ }
+
private static DrmInitData toFrameworkDrmInitData(
com.google.android.exoplayer2.drm.DrmInitData drmInitData) {
// TODO: Implement.
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index 1f9f18c..baa1c25 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -20,6 +20,8 @@
apex_defaults {
native_shared_libs: [
+ "libstatssocket",
+ "libstatspull",
"libstats_jni",
],
// binaries: ["vold"],
@@ -28,6 +30,7 @@
"service-statsd",
],
// prebuilts: ["my_prebuilt"],
+ compile_multilib: "both",
name: "com.android.os.statsd-defaults",
key: "com.android.os.statsd.key",
certificate: ":com.android.os.statsd.certificate",
@@ -74,4 +77,4 @@
//TODO (b/148620413): remove platform.
"//apex_available:platform",
],
-}
\ No newline at end of file
+}
diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp
index 7c93bc7..4ccdd7e 100644
--- a/apex/statsd/aidl/Android.bp
+++ b/apex/statsd/aidl/Android.bp
@@ -38,6 +38,10 @@
},
ndk: {
enabled: true,
+ apex_available: [
+ "com.android.os.statsd",
+ ],
}
- }
+
+ },
}
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 63a853a..ab669d4 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_visibility: [ ":__pkg__" ]
+}
+
genrule {
name: "statslog-statsd-java-gen",
tools: ["stats-log-api-gen"],
@@ -25,6 +29,9 @@
srcs: [
":statslog-statsd-java-gen",
],
+ visibility: [
+ "//cts/hostsidetests/statsd/apps:__subpackages__",
+ ]
}
filegroup {
@@ -34,6 +41,9 @@
":framework-statsd-aidl-sources",
":statslog-statsd-java-gen",
],
+ visibility: [
+ "//frameworks/base", // For the "global" stubs.
+ ],
}
java_defaults {
@@ -139,6 +149,10 @@
"framework-statsd-defaults",
],
srcs: [ ":framework-statsd-stubs-srcs-publicapi" ],
+ visibility: [
+ "//frameworks/base", // Framework
+ "//frameworks/base/apex/statsd", // statsd apex
+ ]
}
java_library {
@@ -147,6 +161,10 @@
"framework-statsd-defaults",
],
srcs: [ ":framework-statsd-stubs-srcs-systemapi" ],
+ visibility: [
+ "//frameworks/base", // Framework
+ "//frameworks/base/apex/statsd", // statsd apex
+ ]
}
java_library {
@@ -155,4 +173,9 @@
"framework-statsd-defaults",
],
srcs: [ ":framework-statsd-stubs-srcs-module_libs_api" ],
+ visibility: [
+ "//frameworks/base", // Framework
+ "//frameworks/base/apex/statsd", // statsd apex
+ "//frameworks/opt/net/wifi/service" // wifi service
+ ]
}
diff --git a/api/current.txt b/api/current.txt
index 5260ceb..5e8974f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -289,6 +289,7 @@
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowEmbedded = 16843765; // 0x10103f5
+ field public static final int allowNativeHeapPointerTagging = 16844311; // 0x1010617
field public static final int allowParallelSyncs = 16843570; // 0x1010332
field public static final int allowSingleTap = 16843353; // 0x1010259
field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -2876,6 +2877,7 @@
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.accessibilityservice.AccessibilityService.ScreenshotResult>);
field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
+ field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
@@ -2883,6 +2885,7 @@
field public static final int GESTURE_2_FINGER_SWIPE_UP = 25; // 0x19
field public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; // 0x15
field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
+ field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29
field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e
field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f
@@ -2890,6 +2893,7 @@
field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d
field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18
field public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; // 0x26
+ field public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; // 0x2a
field public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; // 0x25
field public static final int GESTURE_4_FINGER_SWIPE_DOWN = 34; // 0x22
field public static final int GESTURE_4_FINGER_SWIPE_LEFT = 35; // 0x23
@@ -2954,7 +2958,7 @@
}
public static final class AccessibilityService.ScreenshotResult {
- method @Nullable public android.graphics.ColorSpace getColorSpace();
+ method @NonNull public android.graphics.ColorSpace getColorSpace();
method @NonNull public android.hardware.HardwareBuffer getHardwareBuffer();
method public long getTimestamp();
}
@@ -7238,7 +7242,7 @@
public final class FactoryResetProtectionPolicy implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<java.lang.String> getFactoryResetProtectionAccounts();
- method public boolean isFactoryResetProtectionDisabled();
+ method public boolean isFactoryResetProtectionEnabled();
method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FactoryResetProtectionPolicy> CREATOR;
}
@@ -7247,7 +7251,7 @@
ctor public FactoryResetProtectionPolicy.Builder();
method @NonNull public android.app.admin.FactoryResetProtectionPolicy build();
method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionAccounts(@NonNull java.util.List<java.lang.String>);
- method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionDisabled(boolean);
+ method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionEnabled(boolean);
}
public class FreezePeriod {
@@ -10141,7 +10145,6 @@
field public static final String ALARM_SERVICE = "alarm";
field public static final String APPWIDGET_SERVICE = "appwidget";
field public static final String APP_OPS_SERVICE = "appops";
- field public static final String APP_SEARCH_SERVICE = "app_search";
field public static final String AUDIO_SERVICE = "audio";
field public static final String BATTERY_SERVICE = "batterymanager";
field public static final int BIND_ABOVE_CLIENT = 8; // 0x8
@@ -12068,7 +12071,7 @@
field public static final String FEATURE_COMPANION_DEVICE_SETUP = "android.software.companion_device_setup";
field public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
field public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
- field public static final String FEATURE_CONTEXTHUB = "android.hardware.context_hub";
+ field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
field public static final String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
field public static final String FEATURE_EMBEDDED = "android.hardware.type.embedded";
field public static final String FEATURE_ETHERNET = "android.hardware.ethernet";
@@ -27041,7 +27044,7 @@
method @NonNull public String getId();
method @NonNull public java.util.List<android.media.MediaRoute2Info> getSelectableRoutes();
method @NonNull public java.util.List<android.media.MediaRoute2Info> getSelectedRoutes();
- method @NonNull public java.util.List<android.media.MediaRoute2Info> getTransferrableRoutes();
+ method @NonNull public java.util.List<android.media.MediaRoute2Info> getTransferableRoutes();
method public int getVolume();
method public int getVolumeHandling();
method public int getVolumeMax();
@@ -27421,7 +27424,7 @@
public final class RouteDiscoveryPreference implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<java.lang.String> getPreferredFeatures();
- method public boolean isActiveScan();
+ method public boolean shouldPerformActiveScan();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteDiscoveryPreference> CREATOR;
}
@@ -27430,8 +27433,8 @@
ctor public RouteDiscoveryPreference.Builder(@NonNull java.util.List<java.lang.String>, boolean);
ctor public RouteDiscoveryPreference.Builder(@NonNull android.media.RouteDiscoveryPreference);
method @NonNull public android.media.RouteDiscoveryPreference build();
- method @NonNull public android.media.RouteDiscoveryPreference.Builder setActiveScan(boolean);
method @NonNull public android.media.RouteDiscoveryPreference.Builder setPreferredFeatures(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.media.RouteDiscoveryPreference.Builder setShouldPerformActiveScan(boolean);
}
public final class RoutingSessionInfo implements android.os.Parcelable {
@@ -27442,7 +27445,7 @@
method @NonNull public String getId();
method @NonNull public java.util.List<java.lang.String> getSelectableRoutes();
method @NonNull public java.util.List<java.lang.String> getSelectedRoutes();
- method @NonNull public java.util.List<java.lang.String> getTransferrableRoutes();
+ method @NonNull public java.util.List<java.lang.String> getTransferableRoutes();
method public int getVolume();
method public int getVolumeHandling();
method public int getVolumeMax();
@@ -27456,16 +27459,16 @@
method @NonNull public android.media.RoutingSessionInfo.Builder addDeselectableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo.Builder addSelectableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo.Builder addSelectedRoute(@NonNull String);
- method @NonNull public android.media.RoutingSessionInfo.Builder addTransferrableRoute(@NonNull String);
+ method @NonNull public android.media.RoutingSessionInfo.Builder addTransferableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo build();
method @NonNull public android.media.RoutingSessionInfo.Builder clearDeselectableRoutes();
method @NonNull public android.media.RoutingSessionInfo.Builder clearSelectableRoutes();
method @NonNull public android.media.RoutingSessionInfo.Builder clearSelectedRoutes();
- method @NonNull public android.media.RoutingSessionInfo.Builder clearTransferrableRoutes();
+ method @NonNull public android.media.RoutingSessionInfo.Builder clearTransferableRoutes();
method @NonNull public android.media.RoutingSessionInfo.Builder removeDeselectableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo.Builder removeSelectableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo.Builder removeSelectedRoute(@NonNull String);
- method @NonNull public android.media.RoutingSessionInfo.Builder removeTransferrableRoute(@NonNull String);
+ method @NonNull public android.media.RoutingSessionInfo.Builder removeTransferableRoute(@NonNull String);
method @NonNull public android.media.RoutingSessionInfo.Builder setControlHints(@Nullable android.os.Bundle);
method @NonNull public android.media.RoutingSessionInfo.Builder setVolume(int);
method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeHandling(int);
@@ -37311,6 +37314,7 @@
method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
method public boolean unmountObb(String, boolean, android.os.storage.OnObbStateChangeListener);
method public void unregisterStorageVolumeCallback(@NonNull android.os.storage.StorageManager.StorageVolumeCallback);
+ field @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) public static final String ACTION_CLEAR_APP_CACHE = "android.os.storage.action.CLEAR_APP_CACHE";
field public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
field public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
field public static final String EXTRA_UUID = "android.os.storage.extra.UUID";
@@ -47132,6 +47136,19 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ClosedSubscriberGroupInfo> CREATOR;
}
+ public final class DisplayInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getNetworkType();
+ method public int getOverrideNetworkType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DisplayInfo> CREATOR;
+ field public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2; // 0x2
+ field public static final int OVERRIDE_NETWORK_TYPE_LTE_CA = 1; // 0x1
+ field public static final int OVERRIDE_NETWORK_TYPE_NONE = 0; // 0x0
+ field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA = 3; // 0x3
+ field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4; // 0x4
+ }
+
public class IccOpenLogicalChannelResponse implements android.os.Parcelable {
method public int describeContents();
method public int getChannel();
@@ -47388,6 +47405,7 @@
method public void onDataActivity(int);
method public void onDataConnectionStateChanged(int);
method public void onDataConnectionStateChanged(int, int);
+ method @RequiresPermission("android.permission.READ_PHONE_STATE") public void onDisplayInfoChanged(@NonNull android.telephony.DisplayInfo);
method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
method public void onMessageWaitingIndicatorChanged(boolean);
method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
@@ -47405,6 +47423,7 @@
field public static final int LISTEN_CELL_LOCATION = 16; // 0x10
field public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80
field public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40
+ field public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000
field public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
diff --git a/api/module-lib-lint-baseline.txt b/api/module-lib-lint-baseline.txt
new file mode 100644
index 0000000..6e59596
--- /dev/null
+++ b/api/module-lib-lint-baseline.txt
@@ -0,0 +1,33 @@
+// Baseline format: 1.0
+ActionValue: android.net.TetheringConstants#EXTRA_ADD_TETHER_TYPE:
+ Inconsistent extra value; expected `android.net.extra.ADD_TETHER_TYPE`, was `extraAddTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_PROVISION_CALLBACK:
+ Inconsistent extra value; expected `android.net.extra.PROVISION_CALLBACK`, was `extraProvisionCallback`
+ActionValue: android.net.TetheringConstants#EXTRA_REM_TETHER_TYPE:
+ Inconsistent extra value; expected `android.net.extra.REM_TETHER_TYPE`, was `extraRemTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_RUN_PROVISION:
+ Inconsistent extra value; expected `android.net.extra.RUN_PROVISION`, was `extraRunProvision`
+ActionValue: android.net.TetheringConstants#EXTRA_SET_ALARM:
+ Inconsistent extra value; expected `android.net.extra.SET_ALARM`, was `extraSetAlarm`
+ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
+ Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
+ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
+ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
+ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
+
+
+ManagerConstructor: android.net.TetheringManager#TetheringManager(android.content.Context, java.util.function.Supplier<android.os.IBinder>):
+ Managers must always be obtained from Context; no direct constructors
+
+
+PrivateSuperclass: android.location.GnssAntennaInfo.PhaseCenterVariationCorrections:
+ Public class android.location.GnssAntennaInfo.PhaseCenterVariationCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
+PrivateSuperclass: android.location.GnssAntennaInfo.SignalGainCorrections:
+ Public class android.location.GnssAntennaInfo.SignalGainCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
+
+
+StaticUtils: android.net.TetheringConstants:
+ Fully-static utility classes must not have constructor
diff --git a/api/system-current.txt b/api/system-current.txt
index 67f4ec5..3b1ab45 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1809,7 +1809,7 @@
field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
field public static final String TETHERING_SERVICE = "tethering";
field public static final String VR_SERVICE = "vrmanager";
- field public static final String WIFI_COND_SERVICE = "wificond";
+ field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
field public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
}
@@ -2217,6 +2217,7 @@
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000
field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
+ field public static final int FLAG_PERMISSION_AUTO_REVOKED = 1048576; // 0x100000
field public static final int FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED = 131072; // 0x20000
field public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 262144; // 0x40000
field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
@@ -2302,7 +2303,7 @@
method public void onPermissionsChanged(int);
}
- @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_USER_SET}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
+ @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKE_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
}
public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -7597,7 +7598,7 @@
field @Deprecated public int numAssociation;
field @Deprecated public int numScorerOverride;
field @Deprecated public int numScorerOverrideAndSwitchedNetwork;
- field @Deprecated public boolean requirePMF;
+ field @Deprecated public boolean requirePmf;
field @Deprecated @Nullable public String saePasswordId;
field @Deprecated public boolean shared;
field @Deprecated public boolean useExternalScores;
@@ -8329,21 +8330,21 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.RadioChainInfo> CREATOR;
}
- public class WifiCondManager {
+ public class WifiNl80211Manager {
method public void abortScan(@NonNull String);
method public void enableVerboseLogging(boolean);
method @NonNull public int[] getChannelsMhzForBand(int);
method @Nullable public android.net.wifi.wificond.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int);
- method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String);
- method @Nullable public static android.net.wifi.wificond.WifiCondManager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
- method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SoftApCallback);
- method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SendMgmtFrameCallback);
+ method @Nullable public android.net.wifi.wificond.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
+ method @Nullable public static android.net.wifi.wificond.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
+ method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.SoftApCallback);
+ method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.SendMgmtFrameCallback);
method public void setOnServiceDeadCallback(@NonNull Runnable);
- method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback);
+ method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiNl80211Manager.ScanEventCallback);
method public boolean setupInterfaceForSoftApMode(@NonNull String);
- method @Nullable public android.net.wifi.wificond.WifiCondManager.SignalPollResult signalPoll(@NonNull String);
- method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.wificond.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.PnoScanRequestCallback);
+ method @Nullable public android.net.wifi.wificond.WifiNl80211Manager.SignalPollResult signalPoll(@NonNull String);
+ method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.wificond.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.PnoScanRequestCallback);
method public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>);
method public boolean stopPnoScan(@NonNull String);
method public boolean tearDownClientInterface(@NonNull String);
@@ -8358,43 +8359,43 @@
field public static final int SEND_MGMT_FRAME_ERROR_UNKNOWN = 1; // 0x1
}
- public static class WifiCondManager.OemSecurityType {
- ctor public WifiCondManager.OemSecurityType(int, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, int);
+ public static class WifiNl80211Manager.OemSecurityType {
+ ctor public WifiNl80211Manager.OemSecurityType(int, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, int);
field public final int groupCipher;
field @NonNull public final java.util.List<java.lang.Integer> keyManagement;
field @NonNull public final java.util.List<java.lang.Integer> pairwiseCipher;
field public final int protocol;
}
- public static interface WifiCondManager.PnoScanRequestCallback {
+ public static interface WifiNl80211Manager.PnoScanRequestCallback {
method public void onPnoRequestFailed();
method public void onPnoRequestSucceeded();
}
- public static interface WifiCondManager.ScanEventCallback {
+ public static interface WifiNl80211Manager.ScanEventCallback {
method public void onScanFailed();
method public void onScanResultReady();
}
- public static interface WifiCondManager.SendMgmtFrameCallback {
+ public static interface WifiNl80211Manager.SendMgmtFrameCallback {
method public void onAck(int);
method public void onFailure(int);
}
- public static class WifiCondManager.SignalPollResult {
+ public static class WifiNl80211Manager.SignalPollResult {
field public final int associationFrequencyMHz;
field public final int currentRssiDbm;
field public final int rxBitrateMbps;
field public final int txBitrateMbps;
}
- public static interface WifiCondManager.SoftApCallback {
+ public static interface WifiNl80211Manager.SoftApCallback {
method public void onConnectedClientsChanged(@NonNull android.net.wifi.wificond.NativeWifiClient, boolean);
method public void onFailure();
method public void onSoftApChannelSwitched(int, int);
}
- public static class WifiCondManager.TxPacketCounters {
+ public static class WifiNl80211Manager.TxPacketCounters {
field public final int txPacketFailed;
field public final int txPacketSucceeded;
}
@@ -8839,8 +8840,8 @@
}
public class PowerWhitelistManager {
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean addToWhitelist(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public int addToWhitelist(@NonNull java.util.List<java.lang.String>);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long);
method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
field public static final int EVENT_MMS = 2; // 0x2
@@ -12571,6 +12572,7 @@
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);
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 0caee6b..f440381 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -206,7 +206,7 @@
MutableBareField: android.net.wifi.WifiConfiguration#meteredOverride:
-MutableBareField: android.net.wifi.WifiConfiguration#requirePMF:
+MutableBareField: android.net.wifi.WifiConfiguration#requirePmf:
MutableBareField: android.net.wifi.WifiConfiguration#saePasswordId:
@@ -234,7 +234,7 @@
If implemented by developer, should follow the on<Something> style; otherwise consider marking final
-PairedRegistration: android.net.wifi.wificond.WifiCondManager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiCondManager.SoftApCallback):
+PairedRegistration: android.net.wifi.wificond.WifiNl80211Manager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiNl80211Manager.SoftApCallback):
diff --git a/api/test-current.txt b/api/test-current.txt
index c83555a..4c8bb02 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2451,8 +2451,8 @@
}
public class PowerWhitelistManager {
- method @RequiresPermission("android.permission.DEVICE_POWER") public boolean addToWhitelist(@NonNull String);
- method @RequiresPermission("android.permission.DEVICE_POWER") public int addToWhitelist(@NonNull java.util.List<java.lang.String>);
+ method @RequiresPermission("android.permission.DEVICE_POWER") public void addToWhitelist(@NonNull String);
+ method @RequiresPermission("android.permission.DEVICE_POWER") public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public void whitelistAppTemporarily(@NonNull String, long);
method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
field public static final int EVENT_MMS = 2; // 0x2
@@ -3720,6 +3720,7 @@
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);
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 956fd29..1bcf44e 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -120,13 +120,14 @@
"libstatslog",
"libstatsmetadata",
"libsysutils",
+ // TODO(b/145923087): move to shared when statsd is moved to the apex
+ "libstatssocket",
"libutils",
],
shared_libs: [
"libbinder",
"libincident",
"liblog",
- "libstatssocket",
"statsd-aidl-cpp",
],
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 03f97d8..23a4437 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -381,7 +381,7 @@
UserspaceRebootReported userspace_reboot_reported = 243 [(module) = "framework"];
NotificationReported notification_reported = 244 [(module) = "framework"];
NotificationPanelReported notification_panel_reported = 245;
- NotificationChannelModified notification_panel_modified = 246;
+ NotificationChannelModified notification_channel_modified = 246;
IntegrityCheckResultReported integrity_check_result_reported = 247 [(module) = "framework"];
IntegrityRulesPushed integrity_rules_pushed = 248 [(module) = "framework"];
CellBroadcastMessageReported cb_message_reported =
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 1bac19e..4899b4a 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -690,14 +690,16 @@
if (name[0] == '.') continue;
FileName output;
+ string file_name;
if (parseTimestampOnly) {
+ file_name = StringPrintf("%s/%s", path, name);
output.mTimestampSec = StrToInt64(strtok(name, "_"));
output.mIsHistory = false;
} else {
parseFileName(name, &output);
+ file_name = output.getFullFileName(path);
}
if (output.mTimestampSec == -1) continue;
- string file_name = output.getFullFileName(path);
// Check for timestamp and delete if it's too old.
long fileAge = nowSec - output.mTimestampSec;
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index ace13513..25729ab 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -18,6 +18,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
@@ -25,6 +26,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
@@ -32,6 +34,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT;
@@ -83,9 +86,11 @@
@IntDef(prefix = { "GESTURE_" }, value = {
GESTURE_2_FINGER_SINGLE_TAP,
GESTURE_2_FINGER_DOUBLE_TAP,
+ GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD,
GESTURE_2_FINGER_TRIPLE_TAP,
GESTURE_3_FINGER_SINGLE_TAP,
GESTURE_3_FINGER_DOUBLE_TAP,
+ GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD,
GESTURE_3_FINGER_TRIPLE_TAP,
GESTURE_DOUBLE_TAP,
GESTURE_DOUBLE_TAP_AND_HOLD,
@@ -114,6 +119,7 @@
GESTURE_3_FINGER_SWIPE_RIGHT,
GESTURE_3_FINGER_SWIPE_UP,
GESTURE_4_FINGER_DOUBLE_TAP,
+ GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD,
GESTURE_4_FINGER_SINGLE_TAP,
GESTURE_4_FINGER_SWIPE_DOWN,
GESTURE_4_FINGER_SWIPE_LEFT,
@@ -175,12 +181,18 @@
switch (eventType) {
case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP";
case GESTURE_2_FINGER_DOUBLE_TAP: return "GESTURE_2_FINGER_DOUBLE_TAP";
+ case GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD:
+ return "GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD";
case GESTURE_2_FINGER_TRIPLE_TAP: return "GESTURE_2_FINGER_TRIPLE_TAP";
case GESTURE_3_FINGER_SINGLE_TAP: return "GESTURE_3_FINGER_SINGLE_TAP";
case GESTURE_3_FINGER_DOUBLE_TAP: return "GESTURE_3_FINGER_DOUBLE_TAP";
+ case GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD:
+ return "GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD";
case GESTURE_3_FINGER_TRIPLE_TAP: return "GESTURE_3_FINGER_TRIPLE_TAP";
case GESTURE_4_FINGER_SINGLE_TAP: return "GESTURE_4_FINGER_SINGLE_TAP";
case GESTURE_4_FINGER_DOUBLE_TAP: return "GESTURE_4_FINGER_DOUBLE_TAP";
+ case GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD:
+ return "GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD";
case GESTURE_4_FINGER_TRIPLE_TAP: return "GESTURE_4_FINGER_TRIPLE_TAP";
case GESTURE_DOUBLE_TAP: return "GESTURE_DOUBLE_TAP";
case GESTURE_DOUBLE_TAP_AND_HOLD: return "GESTURE_DOUBLE_TAP_AND_HOLD";
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index b65f68e..b7a35f7 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -29,6 +29,7 @@
import android.content.pm.ParceledListSlice;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
+import android.graphics.ParcelableColorSpace;
import android.graphics.Region;
import android.hardware.HardwareBuffer;
import android.os.Binder;
@@ -39,6 +40,7 @@
import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -411,6 +413,15 @@
/** The user has performed a four-finger triple tap gesture on the touch screen. */
public static final int GESTURE_4_FINGER_TRIPLE_TAP = 39;
+ /** The user has performed a two-finger double tap and hold gesture on the touch screen. */
+ public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40;
+
+ /** The user has performed a three-finger double tap and hold gesture on the touch screen. */
+ public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41;
+
+ /** The user has performed a two-finger double tap and hold gesture on the touch screen. */
+ public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42;
+
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@@ -591,8 +602,12 @@
"screenshot_hardwareBuffer";
/** @hide */
- public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID =
- "screenshot_colorSpaceId";
+ public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE =
+ "screenshot_colorSpace";
+
+ /** @hide */
+ public static final String KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP =
+ "screenshot_timestamp";
/**
* Callback for {@link android.view.accessibility.AccessibilityEvent}s.
@@ -1911,6 +1926,8 @@
* default display.
* @param executor Executor on which to run the callback.
* @param callback The callback invoked when the taking screenshot is done.
+ * The {@link AccessibilityService.ScreenshotResult} will be null for an
+ * invalid display.
*
* @return {@code true} if the taking screenshot accepted, {@code false} if not.
*/
@@ -1932,14 +1949,11 @@
}
final HardwareBuffer hardwareBuffer =
result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER);
- final int colorSpaceId =
- result.getInt(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID);
- ColorSpace colorSpace = null;
- if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
- colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
- }
+ final ParcelableColorSpace colorSpace =
+ result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE);
ScreenshotResult screenshot = new ScreenshotResult(hardwareBuffer,
- colorSpace, System.currentTimeMillis());
+ colorSpace.getColorSpace(),
+ result.getLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP));
sendScreenshotResult(executor, callback, screenshot);
}));
} catch (RemoteException re) {
@@ -2352,41 +2366,38 @@
}
/**
- * Class including hardwareBuffer, colorSpace, and timestamp to be the result for
+ * Can be used to construct a bitmap of the screenshot or any other operations for
* {@link AccessibilityService#takeScreenshot} API.
- * <p>
- * <strong>Note:</strong> colorSpace would be null if the name of this colorSpace isn't at
- * {@link ColorSpace.Named}.
- * </p>
*/
public static final class ScreenshotResult {
private final @NonNull HardwareBuffer mHardwareBuffer;
- private final @Nullable ColorSpace mColorSpace;
+ private final @NonNull ColorSpace mColorSpace;
private final long mTimestamp;
private ScreenshotResult(@NonNull HardwareBuffer hardwareBuffer,
- @Nullable ColorSpace colorSpace, long timestamp) {
+ @NonNull ColorSpace colorSpace, long timestamp) {
Preconditions.checkNotNull(hardwareBuffer, "hardwareBuffer cannot be null");
+ Preconditions.checkNotNull(colorSpace, "colorSpace cannot be null");
mHardwareBuffer = hardwareBuffer;
mColorSpace = colorSpace;
mTimestamp = timestamp;
}
/**
- * Gets the colorSpace identifying a specific organization of colors of the screenshot.
+ * Gets the {@link ColorSpace} identifying a specific organization of colors of the
+ * screenshot.
*
- * @return the colorSpace or {@code null} if the name of colorSpace isn't at
- * {@link ColorSpace.Named}
+ * @return the color space
*/
- @Nullable
+ @NonNull
public ColorSpace getColorSpace() {
return mColorSpace;
}
/**
- * Gets the hardwareBuffer representing a memory buffer of the screenshot.
+ * Gets the {@link HardwareBuffer} representing a memory buffer of the screenshot.
*
- * @return the hardwareBuffer
+ * @return the hardware buffer
*/
@NonNull
public HardwareBuffer getHardwareBuffer() {
@@ -2396,7 +2407,8 @@
/**
* Gets the timestamp of taking the screenshot.
*
- * @return the timestamp from {@link System#currentTimeMillis()}
+ * @return milliseconds of non-sleep uptime before screenshot since boot and it's from
+ * {@link SystemClock#uptimeMillis()}
*/
public long getTimestamp() {
return mTimestamp;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 82c7635..c1e2195 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -551,11 +551,6 @@
private int mHtmlDescriptionRes;
/**
- * Non localized html description of the accessibility service.
- */
- private String mNonLocalizedHtmlDescription;
-
- /**
* Creates a new instance.
*/
public AccessibilityServiceInfo() {
@@ -683,10 +678,6 @@
com.android.internal.R.styleable.AccessibilityService_htmlDescription);
if (peekedValue != null) {
mHtmlDescriptionRes = peekedValue.resourceId;
- final CharSequence nonLocalizedHtmlDescription = peekedValue.coerceToString();
- if (nonLocalizedHtmlDescription != null) {
- mNonLocalizedHtmlDescription = nonLocalizedHtmlDescription.toString().trim();
- }
}
asAttributes.recycle();
} catch (NameNotFoundException e) {
@@ -919,7 +910,7 @@
@Nullable
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
if (mHtmlDescriptionRes == 0) {
- return mNonLocalizedHtmlDescription;
+ return null;
}
final ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
@@ -1017,7 +1008,6 @@
parcel.writeInt(mAnimatedImageRes);
parcel.writeInt(mHtmlDescriptionRes);
parcel.writeString(mNonLocalizedDescription);
- parcel.writeString(mNonLocalizedHtmlDescription);
}
private void initFromParcel(Parcel parcel) {
@@ -1039,7 +1029,6 @@
mAnimatedImageRes = parcel.readInt();
mHtmlDescriptionRes = parcel.readInt();
mNonLocalizedDescription = parcel.readString();
- mNonLocalizedHtmlDescription = parcel.readString();
}
@Override
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 367c2f2..1de68ba 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -7824,9 +7824,7 @@
* @hide
*/
public static boolean isCollectingNotedAppOps() {
- synchronized (sLock) {
- return sNotedAppOpsCollector != null;
- }
+ return sNotedAppOpsCollector != null;
}
/**
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 9aa6b87..526c0b3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -162,6 +162,7 @@
void applyAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment);
void applyAdjustmentsFromAssistant(in INotificationListener token, in List<Adjustment> adjustments);
void unsnoozeNotificationFromAssistant(in INotificationListener token, String key);
+ void unsnoozeNotificationFromSystemListener(in INotificationListener token, String key);
ComponentName getEffectsSuppressor();
boolean matchesCallFilter(in Bundle extras);
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 06288c0..37bdda0 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -204,4 +204,12 @@
* @param frozen if true, Recents Tasks list is currently frozen, false otherwise
*/
void onRecentTaskListFrozenChanged(boolean frozen);
+
+ /**
+ * Called when a task gets or loses focus.
+ *
+ * @param taskId id of the task.
+ * @param {@code true} if the task got focus, {@code false} if it lost it.
+ */
+ void onTaskFocusChanged(int taskId, boolean focused);
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 8b07418..9b73060 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -132,7 +132,7 @@
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
import android.net.wifi.WifiFrameworkInitializer;
-import android.net.wifi.wificond.WifiCondManager;
+import android.net.wifi.wificond.WifiNl80211Manager;
import android.nfc.NfcManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -761,11 +761,11 @@
return new EthernetManager(ctx.getOuterContext(), service);
}});
- registerService(Context.WIFI_COND_SERVICE, WifiCondManager.class,
- new CachedServiceFetcher<WifiCondManager>() {
+ registerService(Context.WIFI_NL80211_SERVICE, WifiNl80211Manager.class,
+ new CachedServiceFetcher<WifiNl80211Manager>() {
@Override
- public WifiCondManager createService(ContextImpl ctx) {
- return new WifiCondManager(ctx.getOuterContext());
+ public WifiNl80211Manager createService(ContextImpl ctx) {
+ return new WifiNl80211Manager(ctx.getOuterContext());
}
});
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 343b386..da0aadb 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -199,4 +199,8 @@
@Override
public void onRecentTaskListFrozenChanged(boolean frozen) {
}
+
+ @Override
+ public void onTaskFocusChanged(int taskId, boolean focused) {
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index dc11013..4a5a23a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4718,15 +4718,40 @@
public static final int KEYGUARD_DISABLE_FEATURES_ALL = 0x7fffffff;
/**
- * Keyguard features that when set on a managed profile that doesn't have its own challenge will
- * affect the profile's parent user. These can also be set on the managed profile's parent
+ * Keyguard features that when set on a non-organization-owned managed profile that doesn't
+ * have its own challenge will affect the profile's parent user. These can also be set on the
+ * managed profile's parent {@link DevicePolicyManager} instance to explicitly control the
+ * parent user.
+ *
+ * <p>
+ * Organization-owned managed profile supports disabling additional keyguard features on the
+ * parent user as defined in {@link #ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY}.
+ *
+ * @hide
+ */
+ public static final int NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER =
+ DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS
+ | DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS;
+
+ /**
+ * Keyguard features that when set by the profile owner of an organization-owned managed
+ * profile will affect the profile's parent user if set on the managed profile's parent
* {@link DevicePolicyManager} instance.
*
* @hide
*/
+ public static final int ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY =
+ KEYGUARD_DISABLE_SECURE_CAMERA;
+
+ /**
+ * Keyguard features that when set on a normal or organization-owned managed profile, have
+ * the potential to affect the profile's parent user.
+ *
+ * @hide
+ */
public static final int PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER =
- DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS
- | DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS;
+ DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER
+ | DevicePolicyManager.ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY;
/**
* @deprecated This method does not actually modify the storage encryption of the device.
@@ -6115,11 +6140,20 @@
* <li>{@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS} which affects notifications generated
* by applications in the managed profile.
* </ul>
+ * <p>
+ * From version {@link android.os.Build.VERSION_CODES#R} the profile owner of an
+ * organization-owned managed profile can set:
+ * <ul>
+ * <li>{@link #KEYGUARD_DISABLE_SECURE_CAMERA} which affects the parent user when called on the
+ * parent profile.
+ * </ul>
* {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, {@link #KEYGUARD_DISABLE_FINGERPRINT},
- * {@link #KEYGUARD_DISABLE_FACE} and {@link #KEYGUARD_DISABLE_IRIS} can also be
- * set on the {@link DevicePolicyManager} instance returned by
- * {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
- * profile.
+ * {@link #KEYGUARD_DISABLE_FACE}, {@link #KEYGUARD_DISABLE_IRIS} and
+ * {@link #KEYGUARD_DISABLE_SECURE_CAMERA} can also be set on the {@link DevicePolicyManager}
+ * instance returned by {@link #getParentProfileInstance(ComponentName)} in order to set
+ * restrictions on the parent profile. {@link #KEYGUARD_DISABLE_SECURE_CAMERA} can only be set
+ * on the parent profile instance if the calling device admin is the profile owner of an
+ * organization-owned managed profile.
* <p>
* Requests to disable other features on a managed profile will be ignored.
* <p>
diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
index ed74779..954db04 100644
--- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java
+++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
@@ -53,17 +53,17 @@
private static final String KEY_FACTORY_RESET_PROTECTION_ACCOUNT =
"factory_reset_protection_account";
- private static final String KEY_FACTORY_RESET_PROTECTION_DISABLED =
- "factory_reset_protection_disabled";
+ private static final String KEY_FACTORY_RESET_PROTECTION_ENABLED =
+ "factory_reset_protection_enabled";
private static final String ATTR_VALUE = "value";
private final List<String> mFactoryResetProtectionAccounts;
- private final boolean mFactoryResetProtectionDisabled;
+ private final boolean mFactoryResetProtectionEnabled;
private FactoryResetProtectionPolicy(List<String> factoryResetProtectionAccounts,
- boolean factoryResetProtectionDisabled) {
+ boolean factoryResetProtectionEnabled) {
mFactoryResetProtectionAccounts = factoryResetProtectionAccounts;
- mFactoryResetProtectionDisabled = factoryResetProtectionDisabled;
+ mFactoryResetProtectionEnabled = factoryResetProtectionEnabled;
}
/**
@@ -74,10 +74,10 @@
}
/**
- * Return whether factory reset protection for the device is disabled or not.
+ * Return whether factory reset protection for the device is enabled or not.
*/
- public boolean isFactoryResetProtectionDisabled() {
- return mFactoryResetProtectionDisabled;
+ public boolean isFactoryResetProtectionEnabled() {
+ return mFactoryResetProtectionEnabled;
}
/**
@@ -85,12 +85,13 @@
*/
public static class Builder {
private List<String> mFactoryResetProtectionAccounts;
- private boolean mFactoryResetProtectionDisabled;
+ private boolean mFactoryResetProtectionEnabled;
/**
* Initialize a new Builder to construct a {@link FactoryResetProtectionPolicy}.
*/
public Builder() {
+ mFactoryResetProtectionEnabled = true;
};
/**
@@ -113,18 +114,19 @@
}
/**
- * Sets whether factory reset protection is disabled or not.
+ * Sets whether factory reset protection is enabled or not.
* <p>
* Once disabled, factory reset protection will not kick in all together when the device
* goes through untrusted factory reset. This applies to both the consumer unlock flow and
- * the admin account overrides via {@link #setFactoryResetProtectionAccounts}
+ * the admin account overrides via {@link #setFactoryResetProtectionAccounts}. By default,
+ * factory reset protection is enabled.
*
- * @param factoryResetProtectionDisabled Whether the policy is disabled or not.
+ * @param factoryResetProtectionEnabled Whether the policy is enabled or not.
* @return the same Builder instance.
*/
@NonNull
- public Builder setFactoryResetProtectionDisabled(boolean factoryResetProtectionDisabled) {
- mFactoryResetProtectionDisabled = factoryResetProtectionDisabled;
+ public Builder setFactoryResetProtectionEnabled(boolean factoryResetProtectionEnabled) {
+ mFactoryResetProtectionEnabled = factoryResetProtectionEnabled;
return this;
}
@@ -136,7 +138,7 @@
@NonNull
public FactoryResetProtectionPolicy build() {
return new FactoryResetProtectionPolicy(mFactoryResetProtectionAccounts,
- mFactoryResetProtectionDisabled);
+ mFactoryResetProtectionEnabled);
}
}
@@ -144,7 +146,7 @@
public String toString() {
return "FactoryResetProtectionPolicy{"
+ "mFactoryResetProtectionAccounts=" + mFactoryResetProtectionAccounts
- + ", mFactoryResetProtectionDisabled=" + mFactoryResetProtectionDisabled
+ + ", mFactoryResetProtectionEnabled=" + mFactoryResetProtectionEnabled
+ '}';
}
@@ -155,7 +157,7 @@
for (String account: mFactoryResetProtectionAccounts) {
dest.writeString(account);
}
- dest.writeBoolean(mFactoryResetProtectionDisabled);
+ dest.writeBoolean(mFactoryResetProtectionEnabled);
}
@Override
@@ -173,10 +175,10 @@
for (int i = 0; i < accountsCount; i++) {
factoryResetProtectionAccounts.add(in.readString());
}
- boolean factoryResetProtectionDisabled = in.readBoolean();
+ boolean factoryResetProtectionEnabled = in.readBoolean();
return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
- factoryResetProtectionDisabled);
+ factoryResetProtectionEnabled);
}
@Override
@@ -195,8 +197,8 @@
@Nullable
public static FactoryResetProtectionPolicy readFromXml(@NonNull XmlPullParser parser) {
try {
- boolean factoryResetProtectionDisabled = Boolean.parseBoolean(
- parser.getAttributeValue(null, KEY_FACTORY_RESET_PROTECTION_DISABLED));
+ boolean factoryResetProtectionEnabled = Boolean.parseBoolean(
+ parser.getAttributeValue(null, KEY_FACTORY_RESET_PROTECTION_ENABLED));
List<String> factoryResetProtectionAccounts = new ArrayList<>();
int outerDepth = parser.getDepth();
@@ -214,7 +216,7 @@
}
return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
- factoryResetProtectionDisabled);
+ factoryResetProtectionEnabled);
} catch (XmlPullParserException | IOException e) {
Log.w(LOG_TAG, "Reading from xml failed", e);
}
@@ -225,8 +227,8 @@
* @hide
*/
public void writeToXml(@NonNull XmlSerializer out) throws IOException {
- out.attribute(null, KEY_FACTORY_RESET_PROTECTION_DISABLED,
- Boolean.toString(mFactoryResetProtectionDisabled));
+ out.attribute(null, KEY_FACTORY_RESET_PROTECTION_ENABLED,
+ Boolean.toString(mFactoryResetProtectionEnabled));
for (String account : mFactoryResetProtectionAccounts) {
out.startTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT);
out.attribute(null, ATTR_VALUE, account);
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index ea66fd47..db4f1de 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -90,6 +90,7 @@
* The name of the dialer role.
*
* @see Intent#ACTION_DIAL
+ * @see android.telecom.InCallService
*/
public static final String ROLE_DIALER = "android.app.role.DIALER";
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index ab71e73..0f999ad 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -41,6 +41,43 @@
/** @hide */
public static final String INSTANT_APP_CLASS_NAME = "android.instant_class";
+ /** @hide */
+ public static final String OBFUSCATED_NOTIFICATION_CHANNEL_ID = "unknown_channel_id";
+
+ /**
+ * Flag: indicates to not obfuscate or hide any usage event data when being queried.
+ * @hide
+ */
+ public static final int SHOW_ALL_EVENT_DATA = 0x00000000;
+
+ /**
+ * Flag: indicates to obfuscate package and class names for instant apps when querying usage
+ * events.
+ * @hide
+ */
+ public static final int OBFUSCATE_INSTANT_APPS = 0x00000001;
+
+ /**
+ * Flag: indicates to hide all {@link Event#SHORTCUT_INVOCATION} events when querying usage
+ * events.
+ * @hide
+ */
+ public static final int HIDE_SHORTCUT_EVENTS = 0x00000002;
+
+ /**
+ * Flag: indicates to obfuscate the notification channel id for all notification events,
+ * such as {@link Event#NOTIFICATION_SEEN} and {@link Event#NOTIFICATION_INTERRUPTION} events,
+ * when querying usage events.
+ * @hide
+ */
+ public static final int OBFUSCATE_NOTIFICATION_EVENTS = 0x00000004;
+
+ /**
+ * Flag: indicates to hide all {@link Event#LOCUS_ID_SET} events when querying usage events.
+ * @hide
+ */
+ public static final int HIDE_LOCUS_EVENTS = 0x00000008;
+
/**
* An event representing a state change for a component.
*/
@@ -627,6 +664,13 @@
return ret;
}
+ /** @hide */
+ public Event getObfuscatedNotificationEvent() {
+ final Event ret = new Event(this);
+ ret.mNotificationChannelId = OBFUSCATED_NOTIFICATION_CHANNEL_ID;
+ return ret;
+ }
+
/**
* Returns the locusId for this event if the event is of type {@link #LOCUS_ID_SET},
* otherwise it returns null.
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 5668944..2c701b4 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -599,7 +599,8 @@
/**
* Returns whether the specified app is currently considered inactive. This will be true if the
* app hasn't been used directly or indirectly for a period of time defined by the system. This
- * could be of the order of several hours or days.
+ * could be of the order of several hours or days. Apps are not considered inactive when the
+ * device is charging.
* @param packageName The package name of the app to query
* @return whether the app is currently considered inactive
*/
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 49f62f4..ae12de0 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4064,16 +4064,16 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
- * {@link android.net.wifi.WifiCondManager} for handling management of the Wi-Fi control
- * daemon.
+ * {@link android.net.wifi.wificond.WifiNl80211Manager} for handling management of the
+ * Wi-Fi nl802.11 daemon (wificond).
*
* @see #getSystemService(String)
- * @see android.net.wifi.WifiCondManager
+ * @see android.net.wifi.wificond.WifiNl80211Manager
* @hide
*/
@SystemApi
@SuppressLint("ServiceName")
- public static final String WIFI_COND_SERVICE = "wificond";
+ public static final String WIFI_NL80211_SERVICE = "wifinl80211";
/**
* Use with {@link #getSystemService(String)} to retrieve a {@link
@@ -5098,10 +5098,11 @@
/**
* Use with {@link #getSystemService(String)} to retrieve an
- * AppSearchManager for indexing and querying app data managed
- * by the system.
+ * {@link android.app.appsearch.AppSearchManager} for
+ * indexing and querying app data managed by the system.
*
* @see #getSystemService(String)
+ * @hide
*/
public static final String APP_SEARCH_SERVICE = "app_search";
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index d251ba9..9d1c677 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -702,6 +702,13 @@
*/
public static final int PRIVATE_FLAG_ODM = 1 << 30;
+ /**
+ * Value for {@link #privateFlags}: If {@code true} this app allows heap tagging.
+ * {@link com.android.server.am.ProcessList#NATIVE_HEAP_POINTER_TAGGING}
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING = 1 << 31;
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -733,6 +740,7 @@
PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE,
PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE,
PRIVATE_FLAG_ODM,
+ PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApplicationInfoPrivateFlags {}
@@ -1878,6 +1886,15 @@
return (privateFlags & PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) != 0;
}
+ /**
+ * If {@code true} this app allows heap pointer tagging.
+ *
+ * @hide
+ */
+ public boolean allowsNativeHeapPointerTagging() {
+ return (privateFlags & PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING) != 0;
+ }
+
private boolean isAllowedToUseHiddenApis() {
if (isSignedWithPlatformKey()) {
return true;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c78d30d..7b484b7 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -38,7 +38,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.usage.StorageStatsManager;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -1988,10 +1988,11 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: The device supports a Context Hub.
+ * {@link #hasSystemFeature}: The device supports a Context Hub, used to expose the
+ * functionalities in {@link android.hardware.location.ContextHubManager}.
*/
@SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_CONTEXTHUB = "android.hardware.context_hub";
+ public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
/** {@hide} */
@SdkConstant(SdkConstantType.FEATURE)
@@ -3385,6 +3386,14 @@
public static final int FLAG_PERMISSION_AUTO_REVOKE_USER_SET = 1 << 18;
/**
+ * Permission flag: Whether permission was revoked by auto-revoke.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 20;
+
+ /**
* Permission flags: Reserved for use by the permission controller.
*
* @hide
@@ -3437,7 +3446,8 @@
| FLAG_PERMISSION_REVOKED_COMPAT
| FLAG_PERMISSION_ONE_TIME
| FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED
- | FLAG_PERMISSION_AUTO_REVOKE_USER_SET;
+ | FLAG_PERMISSION_AUTO_REVOKE_USER_SET
+ | FLAG_PERMISSION_AUTO_REVOKED;
/**
* Injected activity in app that forwards user to setting activity of that app.
@@ -3581,7 +3591,7 @@
* @hide
*/
@ChangeId
- @Disabled
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
public static final long FILTER_APPLICATION_QUERY = 135549675L;
/** {@hide} */
@@ -4262,7 +4272,8 @@
FLAG_PERMISSION_REVOKED_COMPAT,
FLAG_PERMISSION_ONE_TIME,
FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED,
- FLAG_PERMISSION_AUTO_REVOKE_USER_SET
+ FLAG_PERMISSION_AUTO_REVOKE_USER_SET,
+ FLAG_PERMISSION_AUTO_REVOKED
})
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionFlags {}
@@ -7401,6 +7412,7 @@
case FLAG_PERMISSION_ONE_TIME: return "ONE_TIME";
case FLAG_PERMISSION_AUTO_REVOKE_IF_UNUSED: return "AUTO_REVOKE_IF_UNUSED";
case FLAG_PERMISSION_AUTO_REVOKE_USER_SET: return "AUTO_REVOKE_USER_SET";
+ case FLAG_PERMISSION_AUTO_REVOKED: return "AUTO_REVOKED";
default: return Integer.toString(flag);
}
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 637e64d..da44f70 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3710,6 +3710,11 @@
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE;
}
+ if (sa.getBoolean(
+ R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, true)) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING;
+ }
+
ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0);
ai.minAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0);
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index 548d82a..a267113 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -2098,6 +2098,9 @@
R.styleable.AndroidManifestApplication_requestLegacyExternalStorage,
parsingPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q));
+ parsingPackage.setAllowNativeHeapPointerTagging(sa.getBoolean(
+ R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, true));
+
parsingPackage
.setMaxAspectRatio(
sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0))
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index 0df9500..778d7b8 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -1509,6 +1509,16 @@
}
@Override
+ public PackageImpl setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging) {
+ this.privateFlags = allowNativeHeapPointerTagging
+ ? this.privateFlags | ApplicationInfo
+ .PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING
+ : this.privateFlags & ~ApplicationInfo
+ .PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING;
+ return this;
+ }
+
+ @Override
public PackageImpl setUsesNonSdkApi(boolean usesNonSdkApi) {
this.privateFlags = usesNonSdkApi
? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index a2fe064..954d65c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -191,6 +191,8 @@
ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage);
+ ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging);
+
ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
ParsingPackage setSplitHasCode(int splitIndex, boolean splitHasCode);
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index ef28e6c..ac36188 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -75,6 +75,18 @@
/**
* @hide
*/
+ public static final String KEY_DEVICE_CREDENTIAL_TITLE = "device_credential_title";
+ /**
+ * @hide
+ */
+ public static final String KEY_DEVICE_CREDENTIAL_SUBTITLE = "device_credential_subtitle";
+ /**
+ * @hide
+ */
+ public static final String KEY_DEVICE_CREDENTIAL_DESCRIPTION = "device_credential_description";
+ /**
+ * @hide
+ */
public static final String KEY_NEGATIVE_TEXT = "negative_text";
/**
* @hide
@@ -221,6 +233,30 @@
}
/**
+ * Sets an optional title, subtitle, and/or description that will override other text when
+ * the user is authenticating with PIN/pattern/password. Currently for internal use only.
+ * @return This builder.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ @NonNull
+ public Builder setTextForDeviceCredential(
+ @Nullable CharSequence title,
+ @Nullable CharSequence subtitle,
+ @Nullable CharSequence description) {
+ if (title != null) {
+ mBundle.putCharSequence(KEY_DEVICE_CREDENTIAL_TITLE, title);
+ }
+ if (subtitle != null) {
+ mBundle.putCharSequence(KEY_DEVICE_CREDENTIAL_SUBTITLE, subtitle);
+ }
+ if (description != null) {
+ mBundle.putCharSequence(KEY_DEVICE_CREDENTIAL_DESCRIPTION, description);
+ }
+ return this;
+ }
+
+ /**
* Required: Sets the text, executor, and click listener for the negative button on the
* prompt. This is typically a cancel button, but may be also used to show an alternative
* method for authentication, such as a screen that asks for a backup password.
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index db16d24..1ed791d 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -54,7 +54,7 @@
*/
@SystemApi
@SystemService(Context.CONTEXTHUB_SERVICE)
-@RequiresFeature(PackageManager.FEATURE_CONTEXTHUB)
+@RequiresFeature(PackageManager.FEATURE_CONTEXT_HUB)
public final class ContextHubManager {
private static final String TAG = "ContextHubManager";
diff --git a/core/java/android/inputmethodservice/OWNERS b/core/java/android/inputmethodservice/OWNERS
new file mode 100644
index 0000000..4447197
--- /dev/null
+++ b/core/java/android/inputmethodservice/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include ../../../../services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 09ec6c3..d83715c 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -51,7 +51,7 @@
*
* <p>Note that not all aspects of IPsec are permitted by this API. Applications may create
* transport mode security associations and apply them to individual sockets. Applications looking
- * to create a VPN should use {@link VpnService}.
+ * to create an IPsec VPN should use {@link VpnManager} and {@link Ikev2VpnProfile}.
*
* @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
* Internet Protocol</a>
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 6450a67..6516917 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -16,6 +16,7 @@
package android.os.incremental;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -47,6 +48,15 @@
}
/**
+ * Construct a V4Signature from .idsig file.
+ */
+ public static V4Signature readFrom(byte[] bytes) throws IOException {
+ try (DataInputStream stream = new DataInputStream(new ByteArrayInputStream(bytes))) {
+ return readFrom(stream);
+ }
+ }
+
+ /**
* Store the V4Signature to a byte-array.
*/
public byte[] toByteArray() {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index bb1dafc..8d04df0 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -215,6 +215,20 @@
public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
/**
+ * Activity Action: Allows the user to free up space by clearing app external cache directories.
+ * The intent doesn't automatically clear cache, but shows a dialog and lets the user decide.
+ * <p>
+ * This intent should be launched using
+ * {@link Activity#startActivityForResult(Intent, int)} so that the user
+ * knows which app is requesting to clear cache. The returned result will
+ * be {@link Activity#RESULT_OK} if the activity was launched and the user accepted to clear
+ * cache, or {@link Activity#RESULT_CANCELED} otherwise.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CLEAR_APP_CACHE = "android.os.storage.action.CLEAR_APP_CACHE";
+
+ /**
* Extra {@link UUID} used to indicate the storage volume where an
* application is interested in allocating or managing disk space.
*
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index e65bd9f..d273500 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -301,6 +301,13 @@
public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000;
/**
+ * Listen for display info changed event.
+ *
+ * @see #onDisplayInfoChanged
+ */
+ public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000;
+
+ /**
* Listen for changes to the phone capability.
*
* @see #onPhoneCapabilityChanged
@@ -848,6 +855,21 @@
}
/**
+ * Callback invoked when the display info has changed on the registered subscription.
+ * <p> The {@link DisplayInfo} contains status information shown to the user based on
+ * carrier policy.
+ *
+ * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling
+ * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param displayInfo The display information.
+ */
+ @RequiresPermission((android.Manifest.permission.READ_PHONE_STATE))
+ public void onDisplayInfoChanged(@NonNull DisplayInfo displayInfo) {
+ // default implementation empty
+ }
+
+ /**
* Callback invoked when the current emergency number list has changed on the registered
* subscription.
* Note, the registration subId comes from {@link TelephonyManager} object which registers
@@ -1226,6 +1248,15 @@
() -> psl.onUserMobileDataStateChanged(enabled)));
}
+ public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onDisplayInfoChanged(displayInfo)));
+ }
+
public void onOemHookRawEvent(byte[] rawData) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 4024db1..2c077bb 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -589,6 +589,24 @@
}
/**
+ * Notify display info changed.
+ *
+ * @param slotIndex The SIM slot index for which display info has changed. Can be
+ * derived from {@code subscriptionId} except when {@code subscriptionId} is invalid, such as
+ * when the device is in emergency-only mode.
+ * @param subscriptionId Subscription id for which display network info has changed.
+ * @param displayInfo The display info.
+ */
+ public void notifyDisplayInfoChanged(int slotIndex, int subscriptionId,
+ @NonNull DisplayInfo displayInfo) {
+ try {
+ sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, displayInfo);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
* Notify IMS call disconnect causes which contains {@link android.telephony.ims.ImsReasonInfo}.
*
* @param subId for which ims call disconnect.
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index afeb6c3..44e7ae6 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -79,6 +79,7 @@
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
DEFAULT_FLAGS.put(SETTINGS_SCHEDULES_FLAG, "false");
+ DEFAULT_FLAGS.put("settings_contextual_home2", "false");
}
/**
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 2548068..9cd6050e 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -882,6 +882,9 @@
} else {
hideDirectly(types);
}
+ if (mViewRoot.mView == null) {
+ return;
+ }
mViewRoot.mView.dispatchWindowInsetsAnimationPrepare(animation);
mViewRoot.mView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
@Override
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 06cb519..31fdfb7 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -16,528 +16,52 @@
package android.view;
-import android.animation.Animator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.compat.annotation.UnsupportedAppUsage;
+import android.annotation.NonNull;
import android.graphics.CanvasProperty;
import android.graphics.Paint;
-import android.graphics.RecordingCanvas;
-import android.graphics.RenderNode;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.SparseIntArray;
-
-import com.android.internal.util.VirtualRefBasePtr;
-import com.android.internal.view.animation.FallbackLUTInterpolator;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-
-import java.util.ArrayList;
/**
* @hide
*/
-public class RenderNodeAnimator extends Animator {
- // Keep in sync with enum RenderProperty in Animator.h
- public static final int TRANSLATION_X = 0;
- public static final int TRANSLATION_Y = 1;
- public static final int TRANSLATION_Z = 2;
- public static final int SCALE_X = 3;
- public static final int SCALE_Y = 4;
- public static final int ROTATION = 5;
- public static final int ROTATION_X = 6;
- public static final int ROTATION_Y = 7;
- public static final int X = 8;
- public static final int Y = 9;
- public static final int Z = 10;
- public static final int ALPHA = 11;
- // The last value in the enum, used for array size initialization
- public static final int LAST_VALUE = ALPHA;
+public class RenderNodeAnimator extends android.graphics.animation.RenderNodeAnimator
+ implements android.graphics.animation.RenderNodeAnimator.ViewListener {
- // Keep in sync with enum PaintFields in Animator.h
- public static final int PAINT_STROKE_WIDTH = 0;
-
- /**
- * Field for the Paint alpha channel, which should be specified as a value
- * between 0 and 255.
- */
- public static final int PAINT_ALPHA = 1;
-
- // ViewPropertyAnimator uses a mask for its values, we need to remap them
- // to the enum values here. RenderPropertyAnimator can't use the mask values
- // directly as internally it uses a lookup table so it needs the values to
- // be sequential starting from 0
- private static final SparseIntArray sViewPropertyAnimatorMap = new SparseIntArray(15) {{
- put(ViewPropertyAnimator.TRANSLATION_X, TRANSLATION_X);
- put(ViewPropertyAnimator.TRANSLATION_Y, TRANSLATION_Y);
- put(ViewPropertyAnimator.TRANSLATION_Z, TRANSLATION_Z);
- put(ViewPropertyAnimator.SCALE_X, SCALE_X);
- put(ViewPropertyAnimator.SCALE_Y, SCALE_Y);
- put(ViewPropertyAnimator.ROTATION, ROTATION);
- put(ViewPropertyAnimator.ROTATION_X, ROTATION_X);
- put(ViewPropertyAnimator.ROTATION_Y, ROTATION_Y);
- put(ViewPropertyAnimator.X, X);
- put(ViewPropertyAnimator.Y, Y);
- put(ViewPropertyAnimator.Z, Z);
- put(ViewPropertyAnimator.ALPHA, ALPHA);
- }};
-
- private VirtualRefBasePtr mNativePtr;
-
- private Handler mHandler;
- private RenderNode mTarget;
private View mViewTarget;
- private int mRenderProperty = -1;
- private float mFinalValue;
- private TimeInterpolator mInterpolator;
- private static final int STATE_PREPARE = 0;
- private static final int STATE_DELAYED = 1;
- private static final int STATE_RUNNING = 2;
- private static final int STATE_FINISHED = 3;
- private int mState = STATE_PREPARE;
-
- private long mUnscaledDuration = 300;
- private long mUnscaledStartDelay = 0;
- // If this is true, we will run any start delays on the UI thread. This is
- // the safe default, and is necessary to ensure start listeners fire at
- // the correct time. Animators created by RippleDrawable (the
- // CanvasProperty<> ones) do not have this expectation, and as such will
- // set this to false so that the renderthread handles the startdelay instead
- private final boolean mUiThreadHandlesDelay;
- private long mStartDelay = 0;
- private long mStartTime;
-
- @UnsupportedAppUsage
- public static int mapViewPropertyToRenderProperty(int viewProperty) {
- return sViewPropertyAnimatorMap.get(viewProperty);
- }
-
- @UnsupportedAppUsage
public RenderNodeAnimator(int property, float finalValue) {
- mRenderProperty = property;
- mFinalValue = finalValue;
- mUiThreadHandlesDelay = true;
- init(nCreateAnimator(property, finalValue));
+ super(property, finalValue);
}
- @UnsupportedAppUsage
public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) {
- init(nCreateCanvasPropertyFloatAnimator(
- property.getNativeContainer(), finalValue));
- mUiThreadHandlesDelay = false;
+ super(property, finalValue);
}
- /**
- * Creates a new render node animator for a field on a Paint property.
- *
- * @param property The paint property to target
- * @param paintField Paint field to animate, one of {@link #PAINT_ALPHA} or
- * {@link #PAINT_STROKE_WIDTH}
- * @param finalValue The target value for the property
- */
- @UnsupportedAppUsage
public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) {
- init(nCreateCanvasPropertyPaintAnimator(
- property.getNativeContainer(), paintField, finalValue));
- mUiThreadHandlesDelay = false;
+ super(property, paintField, finalValue);
}
public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) {
- init(nCreateRevealAnimator(x, y, startRadius, endRadius));
- mUiThreadHandlesDelay = true;
- }
-
- private void init(long ptr) {
- mNativePtr = new VirtualRefBasePtr(ptr);
- }
-
- private void checkMutable() {
- if (mState != STATE_PREPARE) {
- throw new IllegalStateException("Animator has already started, cannot change it now!");
- }
- if (mNativePtr == null) {
- throw new IllegalStateException("Animator's target has been destroyed "
- + "(trying to modify an animation after activity destroy?)");
- }
- }
-
- static boolean isNativeInterpolator(TimeInterpolator interpolator) {
- return interpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class);
- }
-
- private void applyInterpolator() {
- if (mInterpolator == null || mNativePtr == null) return;
-
- long ni;
- if (isNativeInterpolator(mInterpolator)) {
- ni = ((NativeInterpolatorFactory)mInterpolator).createNativeInterpolator();
- } else {
- long duration = nGetDuration(mNativePtr.get());
- ni = FallbackLUTInterpolator.createNativeInterpolator(mInterpolator, duration);
- }
- nSetInterpolator(mNativePtr.get(), ni);
+ super(x, y, startRadius, endRadius);
}
@Override
- public void start() {
- if (mTarget == null) {
- throw new IllegalStateException("Missing target!");
- }
-
- if (mState != STATE_PREPARE) {
- throw new IllegalStateException("Already started!");
- }
-
- mState = STATE_DELAYED;
- if (mHandler == null) {
- mHandler = new Handler(true);
- }
- applyInterpolator();
-
- if (mNativePtr == null) {
- // It's dead, immediately cancel
- cancel();
- } else if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
- nSetStartDelay(mNativePtr.get(), mStartDelay);
- doStart();
- } else {
- getHelper().addDelayedAnimation(this);
- }
- }
-
- private void doStart() {
+ public void onAlphaAnimationStart(float finalAlpha) {
// Alpha is a special snowflake that has the canonical value stored
// in mTransformationInfo instead of in RenderNode, so we need to update
// it with the final value here.
- if (mRenderProperty == RenderNodeAnimator.ALPHA) {
- mViewTarget.ensureTransformationInfo();
- mViewTarget.setAlphaInternal(mFinalValue);
- }
-
- moveToRunningState();
-
- if (mViewTarget != null) {
- // Kick off a frame to start the process
- mViewTarget.invalidateViewProperty(true, false);
- }
- }
-
- private void moveToRunningState() {
- mState = STATE_RUNNING;
- if (mNativePtr != null) {
- nStart(mNativePtr.get());
- }
- notifyStartListeners();
- }
-
- private void notifyStartListeners() {
- final ArrayList<AnimatorListener> listeners = cloneListeners();
- final int numListeners = listeners == null ? 0 : listeners.size();
- for (int i = 0; i < numListeners; i++) {
- listeners.get(i).onAnimationStart(this);
- }
+ mViewTarget.ensureTransformationInfo();
+ mViewTarget.setAlphaInternal(finalAlpha);
}
@Override
- public void cancel() {
- if (mState != STATE_PREPARE && mState != STATE_FINISHED) {
- if (mState == STATE_DELAYED) {
- getHelper().removeDelayedAnimation(this);
- moveToRunningState();
- }
-
- final ArrayList<AnimatorListener> listeners = cloneListeners();
- final int numListeners = listeners == null ? 0 : listeners.size();
- for (int i = 0; i < numListeners; i++) {
- listeners.get(i).onAnimationCancel(this);
- }
-
- end();
- }
- }
-
- @Override
- public void end() {
- if (mState != STATE_FINISHED) {
- if (mState < STATE_RUNNING) {
- getHelper().removeDelayedAnimation(this);
- doStart();
- }
- if (mNativePtr != null) {
- nEnd(mNativePtr.get());
- if (mViewTarget != null) {
- // Kick off a frame to flush the state change
- mViewTarget.invalidateViewProperty(true, false);
- }
- } else {
- // It's already dead, jump to onFinish
- onFinished();
- }
- }
- }
-
- @Override
- public void pause() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void resume() {
- throw new UnsupportedOperationException();
+ public void invalidateParent(boolean forceRedraw) {
+ mViewTarget.invalidateViewProperty(true, false);
}
/** @hide */
- @UnsupportedAppUsage
- public void setTarget(View view) {
+ public void setTarget(@NonNull View view) {
mViewTarget = view;
+ setViewListener(this);
setTarget(mViewTarget.mRenderNode);
}
-
- /** Sets the animation target to the owning view of the RecordingCanvas */
- public void setTarget(RecordingCanvas canvas) {
- setTarget(canvas.mNode);
- }
-
- /** @hide */
- @UnsupportedAppUsage
- public void setTarget(DisplayListCanvas canvas) {
- setTarget((RecordingCanvas) canvas);
- }
-
- private void setTarget(RenderNode node) {
- checkMutable();
- if (mTarget != null) {
- throw new IllegalStateException("Target already set!");
- }
- nSetListener(mNativePtr.get(), this);
- mTarget = node;
- mTarget.addAnimator(this);
- }
-
- @UnsupportedAppUsage
- public void setStartValue(float startValue) {
- checkMutable();
- nSetStartValue(mNativePtr.get(), startValue);
- }
-
- @Override
- public void setStartDelay(long startDelay) {
- checkMutable();
- if (startDelay < 0) {
- throw new IllegalArgumentException("startDelay must be positive; " + startDelay);
- }
- mUnscaledStartDelay = startDelay;
- mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
- }
-
- @Override
- public long getStartDelay() {
- return mUnscaledStartDelay;
- }
-
- @UnsupportedAppUsage
- @Override
- public RenderNodeAnimator setDuration(long duration) {
- checkMutable();
- if (duration < 0) {
- throw new IllegalArgumentException("duration must be positive; " + duration);
- }
- mUnscaledDuration = duration;
- nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale()));
- return this;
- }
-
- @Override
- public long getDuration() {
- return mUnscaledDuration;
- }
-
- @Override
- public long getTotalDuration() {
- return mUnscaledDuration + mUnscaledStartDelay;
- }
-
- @Override
- public boolean isRunning() {
- return mState == STATE_DELAYED || mState == STATE_RUNNING;
- }
-
- @Override
- public boolean isStarted() {
- return mState != STATE_PREPARE;
- }
-
- @Override
- public void setInterpolator(TimeInterpolator interpolator) {
- checkMutable();
- mInterpolator = interpolator;
- }
-
- @Override
- public TimeInterpolator getInterpolator() {
- return mInterpolator;
- }
-
- protected void onFinished() {
- if (mState == STATE_PREPARE) {
- // Unlikely but possible, the native side has been destroyed
- // before we have started.
- releaseNativePtr();
- return;
- }
- if (mState == STATE_DELAYED) {
- getHelper().removeDelayedAnimation(this);
- notifyStartListeners();
- }
- mState = STATE_FINISHED;
-
- final ArrayList<AnimatorListener> listeners = cloneListeners();
- final int numListeners = listeners == null ? 0 : listeners.size();
- for (int i = 0; i < numListeners; i++) {
- listeners.get(i).onAnimationEnd(this);
- }
-
- // Release the native object, as it has a global reference to us. This
- // breaks the cyclic reference chain, and allows this object to be
- // GC'd
- releaseNativePtr();
- }
-
- private void releaseNativePtr() {
- if (mNativePtr != null) {
- mNativePtr.release();
- mNativePtr = null;
- }
- }
-
- @SuppressWarnings("unchecked")
- private ArrayList<AnimatorListener> cloneListeners() {
- ArrayList<AnimatorListener> listeners = getListeners();
- if (listeners != null) {
- listeners = (ArrayList<AnimatorListener>) listeners.clone();
- }
- return listeners;
- }
-
- public long getNativeAnimator() {
- return mNativePtr.get();
- }
-
- /**
- * @return true if the animator was started, false if still delayed
- */
- private boolean processDelayed(long frameTimeMs) {
- if (mStartTime == 0) {
- mStartTime = frameTimeMs;
- } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
- doStart();
- return true;
- }
- return false;
- }
-
- private static DelayedAnimationHelper getHelper() {
- DelayedAnimationHelper helper = sAnimationHelper.get();
- if (helper == null) {
- helper = new DelayedAnimationHelper();
- sAnimationHelper.set(helper);
- }
- return helper;
- }
-
- private static ThreadLocal<DelayedAnimationHelper> sAnimationHelper =
- new ThreadLocal<DelayedAnimationHelper>();
-
- private static class DelayedAnimationHelper implements Runnable {
-
- private ArrayList<RenderNodeAnimator> mDelayedAnims = new ArrayList<RenderNodeAnimator>();
- private final Choreographer mChoreographer;
- private boolean mCallbackScheduled;
-
- public DelayedAnimationHelper() {
- mChoreographer = Choreographer.getInstance();
- }
-
- public void addDelayedAnimation(RenderNodeAnimator animator) {
- mDelayedAnims.add(animator);
- scheduleCallback();
- }
-
- public void removeDelayedAnimation(RenderNodeAnimator animator) {
- mDelayedAnims.remove(animator);
- }
-
- private void scheduleCallback() {
- if (!mCallbackScheduled) {
- mCallbackScheduled = true;
- mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
- }
- }
-
- @Override
- public void run() {
- long frameTimeMs = mChoreographer.getFrameTime();
- mCallbackScheduled = false;
-
- int end = 0;
- for (int i = 0; i < mDelayedAnims.size(); i++) {
- RenderNodeAnimator animator = mDelayedAnims.get(i);
- if (!animator.processDelayed(frameTimeMs)) {
- if (end != i) {
- mDelayedAnims.set(end, animator);
- }
- end++;
- }
- }
- while (mDelayedAnims.size() > end) {
- mDelayedAnims.remove(mDelayedAnims.size() - 1);
- }
-
- if (mDelayedAnims.size() > 0) {
- scheduleCallback();
- }
- }
- }
-
- // Called by native
- @UnsupportedAppUsage
- private static void callOnFinished(RenderNodeAnimator animator) {
- if (animator.mHandler != null) {
- animator.mHandler.post(animator::onFinished);
- } else {
- new Handler(Looper.getMainLooper(), null, true).post(animator::onFinished);
- }
- }
-
- @Override
- public Animator clone() {
- throw new IllegalStateException("Cannot clone this animator");
- }
-
- @Override
- public void setAllowRunningAsynchronously(boolean mayRunAsync) {
- checkMutable();
- nSetAllowRunningAsync(mNativePtr.get(), mayRunAsync);
- }
-
- private static native long nCreateAnimator(int property, float finalValue);
- private static native long nCreateCanvasPropertyFloatAnimator(
- long canvasProperty, float finalValue);
- private static native long nCreateCanvasPropertyPaintAnimator(
- long canvasProperty, int paintField, float finalValue);
- private static native long nCreateRevealAnimator(
- int x, int y, float startRadius, float endRadius);
-
- private static native void nSetStartValue(long nativePtr, float startValue);
- private static native void nSetDuration(long nativePtr, long duration);
- private static native long nGetDuration(long nativePtr);
- private static native void nSetStartDelay(long nativePtr, long startDelay);
- private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
- private static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync);
- private static native void nSetListener(long animPtr, RenderNodeAnimator listener);
-
- private static native void nStart(long animPtr);
- private static native void nEnd(long animPtr);
}
diff --git a/core/java/android/view/RenderNodeAnimatorSetHelper.java b/core/java/android/view/RenderNodeAnimatorSetHelper.java
deleted file mode 100644
index d222e07..0000000
--- a/core/java/android/view/RenderNodeAnimatorSetHelper.java
+++ /dev/null
@@ -1,52 +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 android.view;
-
-import android.animation.TimeInterpolator;
-import android.graphics.RecordingCanvas;
-import android.graphics.RenderNode;
-
-import com.android.internal.view.animation.FallbackLUTInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
-
-/**
- * This is a helper class to get access to methods and fields needed for RenderNodeAnimatorSet
- * that are internal or package private to android.view package.
- *
- * @hide
- */
-public class RenderNodeAnimatorSetHelper {
-
- /** checkstyle @hide */
- public static RenderNode getTarget(RecordingCanvas recordingCanvas) {
- return recordingCanvas.mNode;
- }
-
- /** checkstyle @hide */
- public static long createNativeInterpolator(TimeInterpolator interpolator, long
- duration) {
- if (interpolator == null) {
- // create LinearInterpolator
- return NativeInterpolatorFactoryHelper.createLinearInterpolator();
- } else if (RenderNodeAnimator.isNativeInterpolator(interpolator)) {
- return ((NativeInterpolatorFactory)interpolator).createNativeInterpolator();
- } else {
- return FallbackLUTInterpolator.createNativeInterpolator(interpolator, duration);
- }
- }
-
-}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index bf84819..680a878 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -24,6 +24,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
/**
* Utility class for adding a View hierarchy to a {@link SurfaceControl}. The View hierarchy
@@ -40,6 +41,7 @@
private WindowlessWindowManager mWm;
private SurfaceControl mSurfaceControl;
+ private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
/**
* Package encapsulating a Surface hierarchy which contains interactive view
@@ -49,15 +51,18 @@
*/
public static final class SurfacePackage implements Parcelable {
private final SurfaceControl mSurfaceControl;
- // TODO: Accessibility ID goes here
+ private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
- SurfacePackage(SurfaceControl sc) {
+ SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection) {
mSurfaceControl = sc;
+ mAccessibilityEmbeddedConnection = connection;
}
private SurfacePackage(Parcel in) {
mSurfaceControl = new SurfaceControl();
mSurfaceControl.readFromParcel(in);
+ mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface(
+ in.readStrongBinder());
}
/**
@@ -69,6 +74,16 @@
return mSurfaceControl;
}
+ /**
+ * Gets an accessibility embedded connection interface for this SurfaceControlViewHost.
+ *
+ * @return {@link IAccessibilityEmbeddedConnection} interface.
+ * @hide
+ */
+ public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() {
+ return mAccessibilityEmbeddedConnection;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -77,6 +92,7 @@
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
mSurfaceControl.writeToParcel(out, flags);
+ out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder());
}
public static final @NonNull Creator<SurfacePackage> CREATOR
@@ -95,6 +111,7 @@
@NonNull WindowlessWindowManager wwm) {
mWm = wwm;
mViewRoot = new ViewRootImpl(c, d, mWm);
+ mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
/**
@@ -118,6 +135,7 @@
mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
mSurfaceControl, hostToken);
mViewRoot = new ViewRootImpl(context, display, mWm);
+ mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
/**
@@ -128,8 +146,8 @@
* are linked.
*/
public @Nullable SurfacePackage getSurfacePackage() {
- if (mSurfaceControl != null) {
- return new SurfacePackage(mSurfaceControl);
+ if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
+ return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection);
} else {
return null;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d5ed36b..47ffd3e 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -28,6 +28,7 @@
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
@@ -38,12 +39,14 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceControlViewHost;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
import com.android.internal.view.SurfaceCallbackHelper;
@@ -203,8 +206,12 @@
private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
private int mParentSurfaceGenerationId;
- // The token of embedded windowless view hierarchy.
- private IBinder mEmbeddedViewHierarchy;
+ private RemoteAccessibilityEmbeddedConnection mRemoteAccessibilityEmbeddedConnection;
+
+ private final Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
+ private final Matrix mTmpMatrix = new Matrix();
+ private final float[] mMatrixValues = new float[9];
+
SurfaceControlViewHost.SurfacePackage mSurfacePackage;
public SurfaceView(Context context) {
@@ -923,6 +930,7 @@
}
mTmpTransaction.apply();
+ updateScreenMatrixForEmbeddedHierarchy();
if (sizeChanged || creating) {
redrawNeeded = true;
@@ -1510,6 +1518,7 @@
@Override
public void surfaceDestroyed() {
setWindowStopped(true);
+ setRemoteAccessibilityEmbeddedConnection(null, null);
}
/**
@@ -1568,31 +1577,133 @@
private void reparentSurfacePackage(SurfaceControl.Transaction t,
SurfaceControlViewHost.SurfacePackage p) {
- // TODO: Link accessibility IDs here.
+ initEmbeddedHierarchyForAccessibility(p);
final SurfaceControl sc = p.getSurfaceControl();
t.reparent(sc, mSurfaceControl).show(sc);
}
- /**
- * Add the token of embedded view hierarchy. Set {@code null} to clear the embedded view
- * hierarchy.
- *
- * @param token IBinder token.
- * @hide
- */
- public void setEmbeddedViewHierarchy(IBinder token) {
- mEmbeddedViewHierarchy = token;
- }
-
/** @hide */
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
- if (mEmbeddedViewHierarchy == null) {
+ final RemoteAccessibilityEmbeddedConnection wrapper =
+ getRemoteAccessibilityEmbeddedConnection();
+ if (wrapper == null) {
return;
}
// Add a leashed child when this SurfaceView embeds another view hierarchy. Getting this
// leashed child would return the root node in the embedded hierarchy
- info.addChild(mEmbeddedViewHierarchy);
+ info.addChild(wrapper.getLeashToken());
+ }
+
+ private void initEmbeddedHierarchyForAccessibility(SurfaceControlViewHost.SurfacePackage p) {
+ final IAccessibilityEmbeddedConnection connection = p.getAccessibilityEmbeddedConnection();
+ final RemoteAccessibilityEmbeddedConnection wrapper =
+ getRemoteAccessibilityEmbeddedConnection();
+
+ // Do nothing if package is embedding the same view hierarchy.
+ if (wrapper != null && wrapper.getConnection().equals(connection)) {
+ return;
+ }
+
+ // If this SurfaceView embeds a different view hierarchy, unlink the previous one first.
+ setRemoteAccessibilityEmbeddedConnection(null, null);
+
+ try {
+ final IBinder leashToken = connection.associateEmbeddedHierarchy(
+ getViewRootImpl().mLeashToken, getAccessibilityViewId());
+ setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error while associateEmbeddedHierarchy " + e);
+ }
+ updateScreenMatrixForEmbeddedHierarchy();
+ }
+
+ private void setRemoteAccessibilityEmbeddedConnection(
+ IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
+ try {
+ if (mRemoteAccessibilityEmbeddedConnection != null) {
+ mRemoteAccessibilityEmbeddedConnection.getConnection()
+ .disassociateEmbeddedHierarchy();
+ mRemoteAccessibilityEmbeddedConnection.unlinkToDeath();
+ mRemoteAccessibilityEmbeddedConnection = null;
+ }
+ if (connection != null && leashToken != null) {
+ mRemoteAccessibilityEmbeddedConnection =
+ new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
+ mRemoteAccessibilityEmbeddedConnection.linkToDeath();
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
+ }
+ }
+
+ private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
+ return mRemoteAccessibilityEmbeddedConnection;
+ }
+
+ private void updateScreenMatrixForEmbeddedHierarchy() {
+ mTmpMatrix.reset();
+ mTmpMatrix.setTranslate(mScreenRect.left, mScreenRect.top);
+ mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
+ mScreenRect.height() / (float) mSurfaceHeight);
+
+ // If the screen matrix is identity or doesn't change, do nothing.
+ if (mTmpMatrix.isIdentity() || mTmpMatrix.equals(mScreenMatrixForEmbeddedHierarchy)) {
+ return;
+ }
+
+ try {
+ final RemoteAccessibilityEmbeddedConnection wrapper =
+ getRemoteAccessibilityEmbeddedConnection();
+ if (wrapper == null) {
+ return;
+ }
+ mTmpMatrix.getValues(mMatrixValues);
+ wrapper.getConnection().setScreenMatrix(mMatrixValues);
+ mScreenMatrixForEmbeddedHierarchy.set(mTmpMatrix);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error while setScreenMatrix " + e);
+ }
+ }
+
+ /**
+ * Wrapper of accessibility embedded connection for embedded view hierarchy.
+ */
+ private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
+ private final IAccessibilityEmbeddedConnection mConnection;
+ private final IBinder mLeashToken;
+
+ RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
+ IBinder leashToken) {
+ mConnection = connection;
+ mLeashToken = leashToken;
+ }
+
+ IAccessibilityEmbeddedConnection getConnection() {
+ return mConnection;
+ }
+
+ IBinder getLeashToken() {
+ return mLeashToken;
+ }
+
+ void linkToDeath() throws RemoteException {
+ mConnection.asBinder().linkToDeath(this, 0);
+ }
+
+ void unlinkToDeath() {
+ mConnection.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ unlinkToDeath();
+ runOnUiThread(() -> {
+ if (mRemoteAccessibilityEmbeddedConnection == this) {
+ mRemoteAccessibilityEmbeddedConnection = null;
+ }
+ });
+ }
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 857bc50..b971a20 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -655,7 +655,7 @@
private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
- private IAccessibilityEmbeddedConnection mEmbeddedConnection;
+ private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
static final class SystemUiVisibilityInfo {
int seq;
@@ -9370,11 +9370,12 @@
* Gets an accessibility embedded connection interface for this ViewRootImpl.
* @hide
*/
- public IAccessibilityEmbeddedConnection getEmbeddedConnection() {
- if (mEmbeddedConnection == null) {
- mEmbeddedConnection = new AccessibilityEmbeddedConnection(ViewRootImpl.this);
+ public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() {
+ if (mAccessibilityEmbeddedConnection == null) {
+ mAccessibilityEmbeddedConnection = new AccessibilityEmbeddedConnection(
+ ViewRootImpl.this);
}
- return mEmbeddedConnection;
+ return mAccessibilityEmbeddedConnection;
}
private class SendWindowContentChangedAccessibilityEvent implements Runnable {
diff --git a/core/java/android/view/animation/AccelerateDecelerateInterpolator.java b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
index 21d5a5b..a2bbc5c 100644
--- a/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
+++ b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
@@ -17,19 +17,18 @@
package android.view.animation;
import android.content.Context;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
-
/**
* An interpolator where the rate of change starts and ends slowly but
* accelerates through the middle.
*/
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator extends BaseInterpolator
- implements NativeInterpolatorFactory {
+ implements NativeInterpolator {
public AccelerateDecelerateInterpolator() {
}
@@ -44,6 +43,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
+ return NativeInterpolatorFactory.createAccelerateDecelerateInterpolator();
}
}
diff --git a/core/java/android/view/animation/AccelerateInterpolator.java b/core/java/android/view/animation/AccelerateInterpolator.java
index 6c8d7b1..9d4cd32 100644
--- a/core/java/android/view/animation/AccelerateInterpolator.java
+++ b/core/java/android/view/animation/AccelerateInterpolator.java
@@ -20,12 +20,12 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the rate of change starts out slowly and
@@ -33,7 +33,7 @@
*
*/
@HasNativeInterpolator
-public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolator {
private final float mFactor;
private final double mDoubleFactor;
@@ -85,6 +85,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createAccelerateInterpolator(mFactor);
+ return NativeInterpolatorFactory.createAccelerateInterpolator(mFactor);
}
}
diff --git a/core/java/android/view/animation/AnticipateInterpolator.java b/core/java/android/view/animation/AnticipateInterpolator.java
index 7a837c3..d146394 100644
--- a/core/java/android/view/animation/AnticipateInterpolator.java
+++ b/core/java/android/view/animation/AnticipateInterpolator.java
@@ -20,18 +20,18 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the change starts backward then flings forward.
*/
@HasNativeInterpolator
-public class AnticipateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class AnticipateInterpolator extends BaseInterpolator implements NativeInterpolator {
private final float mTension;
public AnticipateInterpolator() {
@@ -73,6 +73,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createAnticipateInterpolator(mTension);
+ return NativeInterpolatorFactory.createAnticipateInterpolator(mTension);
}
}
diff --git a/core/java/android/view/animation/AnticipateOvershootInterpolator.java b/core/java/android/view/animation/AnticipateOvershootInterpolator.java
index 9a75134..4d6a390 100644
--- a/core/java/android/view/animation/AnticipateOvershootInterpolator.java
+++ b/core/java/android/view/animation/AnticipateOvershootInterpolator.java
@@ -24,11 +24,11 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the change starts backward then flings forward and overshoots
@@ -36,7 +36,7 @@
*/
@HasNativeInterpolator
public class AnticipateOvershootInterpolator extends BaseInterpolator
- implements NativeInterpolatorFactory {
+ implements NativeInterpolator {
private final float mTension;
public AnticipateOvershootInterpolator() {
@@ -103,6 +103,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createAnticipateOvershootInterpolator(mTension);
+ return NativeInterpolatorFactory.createAnticipateOvershootInterpolator(mTension);
}
}
diff --git a/core/java/android/view/animation/BounceInterpolator.java b/core/java/android/view/animation/BounceInterpolator.java
index 909eaa4..d3f6a3f 100644
--- a/core/java/android/view/animation/BounceInterpolator.java
+++ b/core/java/android/view/animation/BounceInterpolator.java
@@ -17,17 +17,16 @@
package android.view.animation;
import android.content.Context;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
-
/**
* An interpolator where the change bounces at the end.
*/
@HasNativeInterpolator
-public class BounceInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class BounceInterpolator extends BaseInterpolator implements NativeInterpolator {
public BounceInterpolator() {
}
@@ -56,6 +55,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createBounceInterpolator();
+ return NativeInterpolatorFactory.createBounceInterpolator();
}
}
\ No newline at end of file
diff --git a/core/java/android/view/animation/CycleInterpolator.java b/core/java/android/view/animation/CycleInterpolator.java
index 72d64a1..6b1a80a 100644
--- a/core/java/android/view/animation/CycleInterpolator.java
+++ b/core/java/android/view/animation/CycleInterpolator.java
@@ -20,12 +20,12 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* Repeats the animation for a specified number of cycles. The
@@ -33,7 +33,7 @@
*
*/
@HasNativeInterpolator
-public class CycleInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class CycleInterpolator extends BaseInterpolator implements NativeInterpolator {
public CycleInterpolator(float cycles) {
mCycles = cycles;
}
@@ -65,6 +65,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createCycleInterpolator(mCycles);
+ return NativeInterpolatorFactory.createCycleInterpolator(mCycles);
}
}
diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java
index 2d1249d..2d2f770 100644
--- a/core/java/android/view/animation/DecelerateInterpolator.java
+++ b/core/java/android/view/animation/DecelerateInterpolator.java
@@ -20,12 +20,12 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the rate of change starts out quickly and
@@ -33,7 +33,7 @@
*
*/
@HasNativeInterpolator
-public class DecelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class DecelerateInterpolator extends BaseInterpolator implements NativeInterpolator {
public DecelerateInterpolator() {
}
@@ -81,6 +81,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createDecelerateInterpolator(mFactor);
+ return NativeInterpolatorFactory.createDecelerateInterpolator(mFactor);
}
}
diff --git a/core/java/android/view/animation/LinearInterpolator.java b/core/java/android/view/animation/LinearInterpolator.java
index 2a047b4..f6a820c 100644
--- a/core/java/android/view/animation/LinearInterpolator.java
+++ b/core/java/android/view/animation/LinearInterpolator.java
@@ -17,17 +17,16 @@
package android.view.animation;
import android.content.Context;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
-
/**
* An interpolator where the rate of change is constant
*/
@HasNativeInterpolator
-public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class LinearInterpolator extends BaseInterpolator implements NativeInterpolator {
public LinearInterpolator() {
}
@@ -42,6 +41,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createLinearInterpolator();
+ return NativeInterpolatorFactory.createLinearInterpolator();
}
}
diff --git a/core/java/android/view/animation/OvershootInterpolator.java b/core/java/android/view/animation/OvershootInterpolator.java
index 306688a..e6445d7 100644
--- a/core/java/android/view/animation/OvershootInterpolator.java
+++ b/core/java/android/view/animation/OvershootInterpolator.java
@@ -20,19 +20,19 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the change flings forward and overshoots the last value
* then comes back.
*/
@HasNativeInterpolator
-public class OvershootInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class OvershootInterpolator extends BaseInterpolator implements NativeInterpolator {
private final float mTension;
public OvershootInterpolator() {
@@ -76,6 +76,6 @@
/** @hide */
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createOvershootInterpolator(mTension);
+ return NativeInterpolatorFactory.createOvershootInterpolator(mTension);
}
}
diff --git a/core/java/android/view/animation/PathInterpolator.java b/core/java/android/view/animation/PathInterpolator.java
index 924437a..99d6b9c 100644
--- a/core/java/android/view/animation/PathInterpolator.java
+++ b/core/java/android/view/animation/PathInterpolator.java
@@ -20,14 +20,14 @@
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Path;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
import android.util.PathParser;
import android.view.InflateException;
import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator that can traverse a Path that extends from <code>Point</code>
@@ -46,7 +46,7 @@
* </pre></blockquote></p>
*/
@HasNativeInterpolator
-public class PathInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class PathInterpolator extends BaseInterpolator implements NativeInterpolator {
// This governs how accurate the approximation of the Path is.
private static final float PRECISION = 0.002f;
@@ -237,7 +237,7 @@
/** @hide **/
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createPathInterpolator(mX, mY);
+ return NativeInterpolatorFactory.createPathInterpolator(mX, mY);
}
}
diff --git a/core/java/android/view/inputmethod/OWNERS b/core/java/android/view/inputmethod/OWNERS
new file mode 100644
index 0000000..244cc30
--- /dev/null
+++ b/core/java/android/view/inputmethod/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include ../../../../../services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 8a1a0b5..556b24c 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -34,7 +34,7 @@
private final UserInfo mUserInfo;
private final PackageInfo mPackageInfo;
- public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.Q;
+ public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.R;
public UserPackage(UserInfo user, PackageInfo packageInfo) {
this.mUserInfo = user;
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 941af6e..8790bbd 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -47,7 +47,7 @@
// visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
/** @hide */
private static final String CHROMIUM_WEBVIEW_FACTORY =
- "com.android.webview.chromium.WebViewChromiumFactoryProviderForQ";
+ "com.android.webview.chromium.WebViewChromiumFactoryProviderForR";
private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 46b8b77..12f245e 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -5271,6 +5271,8 @@
if (opacity < 10 || opacity > 100) {
opacity = 50;
}
+ // Converts the opacity value from range {0..100} to {0..255}.
+ opacity = opacity * 255 / 100;
}
mDeltaHeight = deltaHeight;
mDrawableOpacity = opacity;
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index 78e8518..efcd54f 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -61,6 +61,7 @@
private Set<Integer> mLoadedPages;
private final UserHandle mPersonalProfileUserHandle;
private final UserHandle mWorkProfileUserHandle;
+ private Injector mInjector;
AbstractMultiProfilePagerAdapter(Context context, int currentPage,
UserHandle personalProfileUserHandle,
@@ -70,6 +71,33 @@
mLoadedPages = new HashSet<>();
mPersonalProfileUserHandle = personalProfileUserHandle;
mWorkProfileUserHandle = workProfileUserHandle;
+ UserManager userManager = context.getSystemService(UserManager.class);
+ mInjector = new Injector() {
+ @Override
+ public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
+ int targetUserId) {
+ return AbstractMultiProfilePagerAdapter.this
+ .hasCrossProfileIntents(intents, sourceUserId, targetUserId);
+ }
+
+ @Override
+ public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+ return userManager.isQuietModeEnabled(workProfileUserHandle);
+ }
+
+ @Override
+ public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
+ userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
+ }
+ };
+ }
+
+ /**
+ * Overrides the default {@link Injector} for testing purposes.
+ */
+ @VisibleForTesting
+ public void setInjector(Injector injector) {
+ mInjector = injector;
}
void setOnProfileSelectedListener(OnProfileSelectedListener listener) {
@@ -252,19 +280,18 @@
private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
UserHandle listUserHandle = activeListAdapter.getUserHandle();
- UserManager userManager = mContext.getSystemService(UserManager.class);
if (listUserHandle == mWorkProfileUserHandle
- && userManager.isQuietModeEnabled(mWorkProfileUserHandle)) {
+ && mInjector.isQuietModeEnabled(mWorkProfileUserHandle)) {
showEmptyState(activeListAdapter,
R.drawable.ic_work_apps_off,
R.string.resolver_turn_on_work_apps,
R.string.resolver_turn_on_work_apps_explanation,
(View.OnClickListener) v ->
- userManager.requestQuietModeEnabled(false, mWorkProfileUserHandle));
+ mInjector.requestQuietModeEnabled(false, mWorkProfileUserHandle));
return false;
}
if (UserHandle.myUserId() != listUserHandle.getIdentifier()) {
- if (!hasCrossProfileIntents(activeListAdapter.getIntents(),
+ if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
UserHandle.myUserId(), listUserHandle.getIdentifier())) {
if (listUserHandle == mPersonalProfileUserHandle) {
showEmptyState(activeListAdapter,
@@ -366,4 +393,26 @@
*/
void onProfileSelected(int profileIndex);
}
+
+ /**
+ * Describes an injector to be used for cross profile functionality. Overridable for testing.
+ */
+ @VisibleForTesting
+ public interface Injector {
+ /**
+ * Returns {@code true} if at least one of the provided {@code intents} can be forwarded
+ * from {@code sourceUserId} to {@code targetUserId}.
+ */
+ boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId, int targetUserId);
+
+ /**
+ * Returns whether the given profile is in quiet mode or not.
+ */
+ boolean isQuietModeEnabled(UserHandle workProfileUserHandle);
+
+ /**
+ * Enables or disables quiet mode for a managed profile.
+ */
+ void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle);
+ }
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/OWNERS b/core/java/com/android/internal/inputmethod/OWNERS
new file mode 100644
index 0000000..fc0e5d4
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include ../../../../../../services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 518911e..0e9c2c4 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -19,8 +19,6 @@
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationErrorReport;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.type.DefaultMimeMapFactory;
import android.os.Build;
@@ -36,7 +34,6 @@
import com.android.internal.logging.AndroidConfig;
import com.android.server.NetworkManagementSocketTagger;
-import dalvik.annotation.compat.VersionCodes;
import dalvik.system.RuntimeHooks;
import dalvik.system.ThreadPrioritySetter;
import dalvik.system.VMRuntime;
@@ -67,18 +64,8 @@
private static volatile boolean mCrashing = false;
- /**
- * Native heap allocations will now have a non-zero tag in the most significant byte.
- * See
- * <a href="https://source.android.com/devices/tech/debug/tagged-pointers">https://source.android.com/devices/tech/debug/tagged-pointers</a>.
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = VersionCodes.Q)
- private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
-
private static final native void nativeFinishInit();
private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
- private static native void nativeDisableHeapPointerTagging();
private static int Clog_e(String tag, String msg, Throwable tr) {
return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
@@ -411,20 +398,6 @@
if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
}
- private static void maybeDisableHeapPointerTagging(long[] disabledCompatChanges) {
- // Heap tagging needs to be disabled before any additional threads are created, but the
- // AppCompat framework is not initialized enough at this point.
- // Check if the change is enabled manually.
- if (disabledCompatChanges != null) {
- for (int i = 0; i < disabledCompatChanges.length; i++) {
- if (disabledCompatChanges[i] == NATIVE_HEAP_POINTER_TAGGING) {
- nativeDisableHeapPointerTagging();
- break;
- }
- }
- }
- }
-
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
// If the application calls System.exit(), terminate the process
@@ -437,8 +410,6 @@
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
- maybeDisableHeapPointerTagging(disabledCompatChanges);
-
final Arguments args = new Arguments(argv);
// The end of of the RuntimeInit event (see #zygoteInit).
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index dfd700f..94924a5 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -24,7 +24,6 @@
import android.net.Credentials;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
-import android.os.Build;
import android.os.FactoryTest;
import android.os.IVold;
import android.os.Process;
@@ -122,6 +121,25 @@
*/
public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18;
+ public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20);
+ /**
+ * Enable pointer tagging in this process.
+ * Tags are checked during memory deallocation, but not on access.
+ * TBI stands for Top-Byte-Ignore, an ARM CPU feature.
+ * {@link https://developer.arm.com/docs/den0024/latest/the-memory-management-unit/translation-table-configuration/virtual-address-tagging}
+ */
+ public static final int MEMORY_TAG_LEVEL_TBI = 1 << 19;
+
+ /**
+ * Enable asynchronous memory tag checks in this process.
+ */
+ public static final int MEMORY_TAG_LEVEL_ASYNC = 2 << 19;
+
+ /**
+ * Enable synchronous memory tag checks in this process.
+ */
+ public static final int MEMORY_TAG_LEVEL_SYNC = 3 << 19;
+
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
/** Default external storage should be mounted. */
@@ -272,7 +290,7 @@
static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- int targetSdkVersion, boolean isTopApp, String[] pkgDataInfoList) {
+ boolean isTopApp, String[] pkgDataInfoList) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(
@@ -280,8 +298,6 @@
fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
pkgDataInfoList);
if (pid == 0) {
- Zygote.disableExecuteOnly(targetSdkVersion);
-
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
}
@@ -677,8 +693,6 @@
args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
args.mPkgDataInfoList);
- disableExecuteOnly(args.mTargetSdkVersion);
-
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
@@ -758,17 +772,6 @@
}
/**
- * Mark execute-only segments of libraries read+execute for apps with targetSdkVersion<Q.
- */
- private static void disableExecuteOnly(int targetSdkVersion) {
- if ((targetSdkVersion < Build.VERSION_CODES.Q) && !nativeDisableExecuteOnly()) {
- Log.e("Zygote", "Failed to set libraries to read+execute.");
- }
- }
-
- private static native boolean nativeDisableExecuteOnly();
-
- /**
* @return Raw file descriptors for the read-end of USAP reporting pipes.
*/
static int[] getUsapPipeFDs() {
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index c91c661..4949811 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -257,8 +257,8 @@
pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
- parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion,
- parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList);
+ parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
+ parsedArgs.mPkgDataInfoList);
try {
if (pid == 0) {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index ae54eb2..e34aa97 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -788,6 +788,10 @@
Zygote.applyDebuggerSystemProperty(parsedArgs);
Zygote.applyInvokeWithSystemProperty(parsedArgs);
+ /* Enable pointer tagging in the system server unconditionally. Hardware support for
+ * this is present in all ARMv8 CPUs; this flag has no effect on other platforms. */
+ parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+
if (shouldProfileSystemServer()) {
parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 0f50596..3d5dfbb 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -21,6 +21,7 @@
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.DisplayInfo;
import android.telephony.PhoneCapability;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
@@ -54,6 +55,7 @@
void onOemHookRawEvent(in byte[] rawData);
void onCarrierNetworkChange(in boolean active);
void onUserMobileDataStateChanged(in boolean enabled);
+ void onDisplayInfoChanged(in DisplayInfo displayInfo);
void onPhoneCapabilityChanged(in PhoneCapability capability);
void onActiveDataSubIdChanged(in int subId);
void onRadioPowerStateChanged(in int state);
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 47752c5..520ffc9 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -23,6 +23,7 @@
import android.telephony.CallQuality;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
+import android.telephony.DisplayInfo;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.PhoneCapability;
import android.telephony.PhysicalChannelConfig;
@@ -87,6 +88,7 @@
void notifyOpportunisticSubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
+ void notifyDisplayInfoChanged(int slotIndex, int subId, in DisplayInfo displayInfo);
void notifyPhoneCapabilityChanged(in PhoneCapability capability);
void notifyActiveDataSubIdChanged(int activeDataSubId);
void notifyRadioPowerStateChanged(in int phoneId, in int subId, in int state);
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
deleted file mode 100644
index ebeec40..0000000
--- a/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.view.animation;
-
-/**
- * Static utility class for constructing native interpolators to keep the
- * JNI simpler
- */
-public final class NativeInterpolatorFactoryHelper {
- private NativeInterpolatorFactoryHelper() {}
-
- public static native long createAccelerateDecelerateInterpolator();
- public static native long createAccelerateInterpolator(float factor);
- public static native long createAnticipateInterpolator(float tension);
- public static native long createAnticipateOvershootInterpolator(float tension);
- public static native long createBounceInterpolator();
- public static native long createCycleInterpolator(float cycles);
- public static native long createDecelerateInterpolator(float factor);
- public static native long createLinearInterpolator();
- public static native long createOvershootInterpolator(float tension);
- public static native long createPathInterpolator(float[] x, float[] y);
- public static native long createLutInterpolator(float[] values);
-}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 30d0d1d..d1a7d24 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -42,9 +42,7 @@
"android_util_StringBlock.cpp",
"android_util_XmlBlock.cpp",
"android_util_jar_StrictJarFile.cpp",
- "android_view_RenderNodeAnimator.cpp",
"com_android_internal_util_VirtualRefBasePtr.cpp",
- "com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
],
include_dirs: [
@@ -351,6 +349,8 @@
"android/graphics/apex/android_paint.cpp",
"android/graphics/apex/android_region.cpp",
+ "android_graphics_animation_NativeInterpolatorFactory.cpp",
+ "android_graphics_animation_RenderNodeAnimator.cpp",
"android_graphics_Canvas.cpp",
"android_graphics_ColorSpace.cpp",
"android_graphics_drawable_AnimatedVectorDrawable.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0b1c8b7..bbc23fe 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -119,13 +119,11 @@
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
extern int register_android_view_InputApplicationHandle(JNIEnv* env);
extern int register_android_view_InputWindowHandle(JNIEnv* env);
-extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
extern int register_android_view_Surface(JNIEnv* env);
extern int register_android_view_SurfaceControl(JNIEnv* env);
extern int register_android_view_SurfaceSession(JNIEnv* env);
extern int register_android_view_CompositionSamplingListener(JNIEnv* env);
extern int register_android_view_TextureView(JNIEnv* env);
-extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
extern int register_android_database_CursorWindow(JNIEnv* env);
extern int register_android_database_SQLiteConnection(JNIEnv* env);
extern int register_android_database_SQLiteGlobal(JNIEnv* env);
@@ -243,14 +241,6 @@
gCurRuntime->setExitWithoutCleanup(exitWithoutCleanup);
}
-static void com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging(
- JNIEnv* env, jobject clazz) {
- HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_NONE;
- if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level))) {
- ALOGE("ERROR: could not disable heap pointer tagging\n");
- }
-}
-
/*
* JNI registration.
*/
@@ -262,8 +252,6 @@
(void*)com_android_internal_os_RuntimeInit_nativeFinishInit},
{"nativeSetExitWithoutCleanup", "(Z)V",
(void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},
- {"nativeDisableHeapPointerTagging", "()V",
- (void*)com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging},
};
return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
methods, NELEM(methods));
@@ -1471,7 +1459,6 @@
REG_JNI(register_android_os_VintfRuntimeInfo),
REG_JNI(register_android_service_DataLoaderService),
REG_JNI(register_android_view_DisplayEventReceiver),
- REG_JNI(register_android_view_RenderNodeAnimator),
REG_JNI(register_android_view_InputApplicationHandle),
REG_JNI(register_android_view_InputWindowHandle),
REG_JNI(register_android_view_Surface),
@@ -1479,7 +1466,6 @@
REG_JNI(register_android_view_SurfaceSession),
REG_JNI(register_android_view_CompositionSamplingListener),
REG_JNI(register_android_view_TextureView),
- REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper),
REG_JNI(register_com_google_android_gles_jni_EGLImpl),
REG_JNI(register_com_google_android_gles_jni_GLImpl),
REG_JNI(register_android_opengl_jni_EGL14),
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 7ceef98..7ee509b 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -65,6 +65,8 @@
extern int register_android_graphics_PathMeasure(JNIEnv* env);
extern int register_android_graphics_Picture(JNIEnv* env);
extern int register_android_graphics_Region(JNIEnv* env);
+extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env);
+extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env);
extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
extern int register_android_graphics_fonts_Font(JNIEnv* env);
@@ -82,10 +84,8 @@
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
extern int register_android_view_RenderNode(JNIEnv* env);
-extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
-extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
#define REG_JNI(name) { name }
struct RegJNIRec {
@@ -130,6 +130,10 @@
{"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
{"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
{"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
+ {"android.graphics.animation.NativeInterpolatorFactory",
+ REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)},
+ {"android.graphics.animation.RenderNodeAnimator",
+ REG_JNI(register_android_graphics_animation_RenderNodeAnimator)},
{"android.graphics.drawable.AnimatedVectorDrawable",
REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)},
{"android.graphics.drawable.VectorDrawable",
@@ -151,11 +155,8 @@
{"android.util.Log", REG_JNI(register_android_util_Log)},
{"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
{"android.util.jar.StrictJarFile", REG_JNI(register_android_util_jar_StrictJarFile)},
- {"android.view.RenderNodeAnimator", REG_JNI(register_android_view_RenderNodeAnimator)},
{"com.android.internal.util.VirtualRefBasePtr",
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
- {"com.android.internal.view.animation.NativeInterpolatorFactoryHelper",
- REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper)},
};
// Vector to store the names of classes that need delegates of their native methods
static vector<string> classesToDelegate;
diff --git a/core/jni/android/graphics/apex/jni_runtime.cpp b/core/jni/android/graphics/apex/jni_runtime.cpp
index 1f66153..35c997d 100644
--- a/core/jni/android/graphics/apex/jni_runtime.cpp
+++ b/core/jni/android/graphics/apex/jni_runtime.cpp
@@ -60,6 +60,8 @@
extern int register_android_graphics_Picture(JNIEnv*);
extern int register_android_graphics_Region(JNIEnv* env);
extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
+extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env);
+extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env);
extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
extern int register_android_graphics_fonts_Font(JNIEnv* env);
@@ -123,6 +125,8 @@
REG_JNI(register_android_graphics_SurfaceTexture),
REG_JNI(register_android_graphics_Typeface),
REG_JNI(register_android_graphics_YuvImage),
+ REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory),
+ REG_JNI(register_android_graphics_animation_RenderNodeAnimator),
REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
REG_JNI(register_android_graphics_drawable_VectorDrawable),
REG_JNI(register_android_graphics_fonts_Font),
diff --git a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp b/core/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
similarity index 95%
rename from core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
rename to core/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
index f4d2e7b..2073ac2 100644
--- a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
+++ b/core/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
@@ -90,7 +90,7 @@
// JNI Glue
// ----------------------------------------------------------------------------
-const char* const kClassPathName = "com/android/internal/view/animation/NativeInterpolatorFactoryHelper";
+const char* const kClassPathName = "android/graphics/animation/NativeInterpolatorFactory";
static const JNINativeMethod gMethods[] = {
{ "createAccelerateDecelerateInterpolator", "()J", (void*) createAccelerateDecelerateInterpolator },
@@ -106,7 +106,7 @@
{ "createLutInterpolator", "([F)J", (void*) createLutInterpolator },
};
-int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv* env) {
+int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env) {
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_graphics_animation_RenderNodeAnimator.cpp
similarity index 96%
rename from core/jni/android_view_RenderNodeAnimator.cpp
rename to core/jni/android_graphics_animation_RenderNodeAnimator.cpp
index ca32b00..878d4fc 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_graphics_animation_RenderNodeAnimator.cpp
@@ -190,7 +190,7 @@
// JNI Glue
// ----------------------------------------------------------------------------
-const char* const kClassPathName = "android/view/RenderNodeAnimator";
+const char* const kClassPathName = "android/graphics/animation/RenderNodeAnimator";
static const JNINativeMethod gMethods[] = {
{ "nCreateAnimator", "(IF)J", (void*) createAnimator },
@@ -203,12 +203,12 @@
{ "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
{ "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
{ "nSetAllowRunningAsync", "(JZ)V", (void*) setAllowRunningAsync },
- { "nSetListener", "(JLandroid/view/RenderNodeAnimator;)V", (void*) setListener},
+ { "nSetListener", "(JLandroid/graphics/animation/RenderNodeAnimator;)V", (void*) setListener},
{ "nStart", "(J)V", (void*) start},
{ "nEnd", "(J)V", (void*) end },
};
-int register_android_view_RenderNodeAnimator(JNIEnv* env) {
+int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env) {
sLifecycleChecker.incStrong(0);
gRenderNodeAnimatorClassInfo.clazz = FindClassOrDie(env, kClassPathName);
gRenderNodeAnimatorClassInfo.clazz = MakeGlobalRefOrDie(env,
@@ -216,7 +216,7 @@
gRenderNodeAnimatorClassInfo.callOnFinished = GetStaticMethodIDOrDie(
env, gRenderNodeAnimatorClassInfo.clazz, "callOnFinished",
- "(Landroid/view/RenderNodeAnimator;)V");
+ "(Landroid/graphics/animation/RenderNodeAnimator;)V");
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 87be5f6..d8f30e9 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -566,10 +566,10 @@
return (jint) status;
}
-static jint
-android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state)
-{
- return (jint) check_AudioSystem_Command(AudioSystem::setPhoneState((audio_mode_t) state));
+static jint android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state,
+ jint uid) {
+ return (jint)check_AudioSystem_Command(
+ AudioSystem::setPhoneState((audio_mode_t)state, (uid_t)uid));
}
static jint
@@ -2434,7 +2434,7 @@
(void *)android_media_AudioSystem_getDeviceConnectionState},
{"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;I)I",
(void *)android_media_AudioSystem_handleDeviceConfigChange},
- {"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState},
+ {"setPhoneState", "(II)I", (void *)android_media_AudioSystem_setPhoneState},
{"setForceUse", "(II)I", (void *)android_media_AudioSystem_setForceUse},
{"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse},
{"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume},
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7a9a3f8..9e578bf 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -49,7 +49,6 @@
#include <fcntl.h>
#include <grp.h>
#include <inttypes.h>
-#include <link.h>
#include <malloc.h>
#include <mntent.h>
#include <paths.h>
@@ -59,7 +58,6 @@
#include <sys/capability.h>
#include <sys/cdefs.h>
#include <sys/eventfd.h>
-#include <sys/mman.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/resource.h>
@@ -76,10 +74,8 @@
#include <android-base/properties.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <bionic/malloc.h>
-#include <bionic/page.h>
#include <cutils/fs.h>
#include <cutils/multiuser.h>
#include <cutils/sockets.h>
@@ -349,6 +345,8 @@
enum RuntimeFlags : uint32_t {
DEBUG_ENABLE_JDWP = 1,
PROFILE_FROM_SHELL = 1 << 15,
+ MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20),
+ MEMORY_TAG_LEVEL_TBI = 1 << 19,
};
enum UnsolicitedZygoteMessageTypes : uint32_t {
@@ -1627,6 +1625,16 @@
}
}
+ HeapTaggingLevel heap_tagging_level;
+ switch (runtime_flags & RuntimeFlags::MEMORY_TAG_LEVEL_MASK) {
+ case RuntimeFlags::MEMORY_TAG_LEVEL_TBI:
+ heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
+ break;
+ default:
+ heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
+ }
+ android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level));
+
if (NeedsNoRandomizeWorkaround()) {
// Work around ARM kernel ASLR lossage (http://b/5817320).
int old_personality = personality(0xffffffff);
@@ -1893,25 +1901,6 @@
UnmountTree("/storage");
}
-static int DisableExecuteOnly(struct dl_phdr_info* info,
- size_t size [[maybe_unused]],
- void* data [[maybe_unused]]) {
- // Search for any execute-only segments and mark them read+execute.
- for (int i = 0; i < info->dlpi_phnum; i++) {
- const auto& phdr = info->dlpi_phdr[i];
- if ((phdr.p_type == PT_LOAD) && (phdr.p_flags == PF_X)) {
- auto addr = reinterpret_cast<void*>(info->dlpi_addr + PAGE_START(phdr.p_vaddr));
- size_t len = PAGE_OFFSET(phdr.p_vaddr) + phdr.p_memsz;
- if (mprotect(addr, len, PROT_READ | PROT_EXEC) == -1) {
- ALOGE("mprotect(%p, %zu, PROT_READ | PROT_EXEC) failed: %m", addr, len);
- return -1;
- }
- }
- }
- // Return non-zero to exit dl_iterate_phdr.
- return 0;
-}
-
} // anonymous namespace
namespace android {
@@ -2274,14 +2263,6 @@
}
}
-/**
- * @param env Managed runtime environment
- * @return True if disable was successful.
- */
-static jboolean com_android_internal_os_Zygote_nativeDisableExecuteOnly(JNIEnv* env, jclass) {
- return dl_iterate_phdr(DisableExecuteOnly, nullptr) == 0;
-}
-
static void com_android_internal_os_Zygote_nativeBlockSigTerm(JNIEnv* env, jclass) {
auto fail_fn = std::bind(ZygoteFailure, env, "usap", nullptr, _1);
BlockSignal(SIGTERM, fail_fn);
@@ -2363,8 +2344,6 @@
{"nativeGetUsapPoolCount", "()I",
(void*)com_android_internal_os_Zygote_nativeGetUsapPoolCount},
{"nativeEmptyUsapPool", "()V", (void*)com_android_internal_os_Zygote_nativeEmptyUsapPool},
- {"nativeDisableExecuteOnly", "()Z",
- (void*)com_android_internal_os_Zygote_nativeDisableExecuteOnly},
{"nativeBlockSigTerm", "()V", (void*)com_android_internal_os_Zygote_nativeBlockSigTerm},
{"nativeUnblockSigTerm", "()V", (void*)com_android_internal_os_Zygote_nativeUnblockSigTerm},
{"nativeBoostUsapPriority", "()V",
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index bf4cdee..03676dd 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -428,7 +428,7 @@
(section).args = "dropbox --proto system_app_wtf"
];
- optional android.service.dropbox.DropBoxManagerServiceDumpProto dropbox_system_server_crashes = 3037 [
+ optional android.service.dropbox.DropBoxManagerServiceDumpProto dropbox_system_server_crash = 3037 [
(section).type = SECTION_DUMPSYS,
(section).args = "dropbox --proto system_server_crash"
];
diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto
index 34ed90a..e1af962 100644
--- a/core/proto/android/stats/mediametrics/mediametrics.proto
+++ b/core/proto/android/stats/mediametrics/mediametrics.proto
@@ -154,6 +154,8 @@
optional int64 latency_avg = 18;
optional int64 latency_count = 19;
optional int64 latency_unknown = 20;
+ optional int32 queue_input_buffer_error = 21;
+ optional int32 queue_secure_input_buffer_error = 22;
}
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 60b3a19..9dc40c8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5344,6 +5344,10 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+ <service android:name="com.android.server.people.data.DataMaintenanceService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
<service
android:name="com.android.server.autofill.AutofillCompatAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index 6959e9c..5ed4c53 100644
--- a/core/res/res/layout/resolver_empty_states.xml
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -34,7 +34,7 @@
android:layout_marginTop="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/textAppearanceMedium"
+ android:fontFamily="@string/config_headlineFontFamilyMedium"
android:textColor="@color/resolver_empty_state_text"
android:textSize="18sp"/>
<TextView
@@ -52,7 +52,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
- android:textAppearance="?attr/textAppearanceMedium"
+ android:fontFamily="@string/config_headlineFontFamilyMedium"
android:textSize="14sp"
android:textColor="@color/resolver_tabs_active_color"/>
</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7d8b8db..7fc041c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3774,7 +3774,7 @@
<attr name="animatedImageDrawable" format="reference"/>
<!-- Html description of the accessibility service, to help users understand
how the service can help them.-->
- <attr name="htmlDescription" format="string"/>
+ <attr name="htmlDescription" format="reference"/>
<!-- Short description of the accessibility service purpose or behavior.-->
<attr name="description" />
@@ -3795,7 +3795,7 @@
<attr name="animatedImageDrawable" format="reference"/>
<!-- Html description of the target of accessibility shortcut purpose or behavior, to help
users understand how the target of accessibility shortcut can help them. -->
- <attr name="htmlDescription" format="string"/>
+ <attr name="htmlDescription" format="reference"/>
</declare-styleable>
<!-- Use <code>print-service</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c66261b..e231c3a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1782,6 +1782,15 @@
The default value is {@code false}. -->
<attr name="crossProfile" format="boolean" />
+
+ <!-- If {@code true} this app will receive tagged pointers to native heap allocations
+ from functions like malloc() on compatible devices. Note that this may not always
+ be respected due to policy or backwards compatibility reasons. See the
+ <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged Pointers</a>
+ document for more information on this feature.
+
+ The default value is {@code true}. -->
+ <attr name="allowNativeHeapPointerTagging" format="boolean" />
</declare-styleable>
<!-- The <code>feature</code> tag declares a feature. A feature is a logical part of an app.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4172044..0edad3b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3017,6 +3017,7 @@
<public name="autofillInlineSuggestionSubtitle" />
<!-- @hide @SystemApi -->
<public name="isAutofillInlineSuggestionTheme" />
+ <public name="allowNativeHeapPointerTagging" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5aefe11..b3dd1fc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3902,4 +3902,6 @@
<!-- Whether to expand the lock screen user switcher by default -->
<java-symbol type="bool" name="config_expandLockScreenUserSwitcher" />
+
+ <java-symbol type="string" name="loading" />
</resources>
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index ce71beb..6d0e58b 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -18,6 +18,7 @@
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
@@ -355,18 +356,14 @@
public void hasOtherProfileOneOption() throws Exception {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
-
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
markWorkProfileUserAvailable();
+
+ ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
Intent sendIntent = createSendTextIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2);
- ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
- when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
-
final ChooserWrapperActivity activity = mActivityRule
.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
@@ -382,9 +379,11 @@
// Make a stable copy of the components as the original list may be modified
List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2);
+ createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10);
waitForIdle();
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
+ Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+
+ onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
.perform(click());
waitForIdle();
assertThat(chosen[0], is(toChoose));
@@ -1218,17 +1217,7 @@
int workProfileTargets = 4;
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(
workProfileTargets);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
sendIntent.setType("TestType");
markWorkProfileUserAvailable();
@@ -1245,7 +1234,7 @@
}
@Test
- public void testWorkTab_workProfileHasExpectedNumberOfTargets() {
+ public void testWorkTab_workProfileHasExpectedNumberOfTargets() throws InterruptedException {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
markWorkProfileUserAvailable();
@@ -1254,18 +1243,7 @@
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
sendIntent.setType("TestType");
@@ -1284,12 +1262,12 @@
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
int workProfileTargets = 4;
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets);
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendTextIntent();
sendIntent.setType("TestType");
ResolveInfo[] chosen = new ResolveInfo[1];
@@ -1312,6 +1290,85 @@
assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
}
+ @Test
+ public void testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ sOverrides.hasCrossProfileIntents = false;
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ final ChooserWrapperActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+
+ onView(withText(R.string.resolver_cant_share_with_work_apps))
+ .check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testWorkTab_workProfileDisabled_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ sOverrides.isQuietModeEnabled = true;
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ final ChooserWrapperActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ onView(withText(R.string.resolver_turn_on_work_apps))
+ .check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testWorkTab_noWorkTargets_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(0);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ final ChooserWrapperActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ onView(withText(R.string.resolver_no_apps_available))
+ .check(matches(isDisplayed()));
+ }
+
private Intent createSendTextIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
@@ -1486,4 +1543,21 @@
private void markWorkProfileUserAvailable() {
sOverrides.workProfileUserHandle = UserHandle.of(10);
}
+
+ private void setupResolverControllers(
+ List<ResolvedComponentInfo> personalResolvedComponentInfos,
+ List<ResolvedComponentInfo> workResolvedComponentInfos) {
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
+ when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class),
+ eq(UserHandle.SYSTEM)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index a68b5908..363551b 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -16,15 +16,10 @@
package com.android.internal.app;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionManager;
-import android.app.prediction.AppPredictor;
import android.app.usage.UsageStatsManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -35,7 +30,6 @@
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.os.Bundle;
import android.os.UserHandle;
import android.util.Size;
@@ -45,8 +39,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import org.mockito.Mockito;
-
+import java.util.List;
import java.util.function.Function;
public class ChooserWrapperActivity extends ChooserActivity {
@@ -56,6 +49,15 @@
static final OverrideData sOverrides = new OverrideData();
private UsageStatsManager mUsm;
+ @Override
+ protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
+ Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed) {
+ AbstractMultiProfilePagerAdapter multiProfilePagerAdapter =
+ super.createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
+ multiProfilePagerAdapter.setInjector(sOverrides.multiPagerAdapterInjector);
+ return multiProfilePagerAdapter;
+ }
+
ChooserListAdapter getAdapter() {
return mChooserMultiProfilePagerAdapter.getActiveListAdapter();
}
@@ -206,6 +208,9 @@
public int alternateProfileSetting;
public Resources resources;
public UserHandle workProfileUserHandle;
+ public boolean hasCrossProfileIntents;
+ public boolean isQuietModeEnabled;
+ public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
public void reset() {
onSafelyStartCallback = null;
@@ -221,6 +226,26 @@
alternateProfileSetting = 0;
resources = null;
workProfileUserHandle = null;
+ hasCrossProfileIntents = true;
+ isQuietModeEnabled = false;
+ multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
+ @Override
+ public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
+ int targetUserId) {
+ return hasCrossProfileIntents;
+ }
+
+ @Override
+ public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+ return isQuietModeEnabled;
+ }
+
+ @Override
+ public void requestQuietModeEnabled(boolean enabled,
+ UserHandle workProfileUserHandle) {
+ isQuietModeEnabled = enabled;
+ }
+ };
}
}
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 5f4194a..a7bf488 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -18,16 +18,16 @@
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static com.android.internal.app.MatcherUtils.first;
import static com.android.internal.app.ResolverDataProvider.createPackageManagerMockedInfo;
-import static com.android.internal.app.ResolverDataProvider.createResolvedComponentInfoWithOtherId;
import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
import static org.hamcrest.CoreMatchers.allOf;
@@ -56,9 +56,6 @@
import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
import com.android.internal.widget.ResolverDrawerLayout;
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -224,17 +221,14 @@
public void hasOtherProfileOneOption() throws Exception {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
-
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
markWorkProfileUserAvailable();
+
+ ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
Intent sendIntent = createSendImageIntent();
- List<ResolvedComponentInfo> resolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(2);
- ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
-
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource());
waitForIdle();
@@ -249,8 +243,9 @@
};
// Make a stable copy of the components as the original list may be modified
List<ResolvedComponentInfo> stableCopy =
- createResolvedComponentsForTestWithOtherProfile(2);
- onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
+ createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10);
+ // We pick the first one as there is another one in the work profile side
+ onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
.perform(click());
onView(withId(R.id.button_once))
.perform(click());
@@ -415,18 +410,14 @@
}
@Test
- public void testWorkTab_workTabListEmptyBeforeGoingToTab() {
+ public void testWorkTab_workTabListPopulatedBeforeGoingToTab() throws InterruptedException {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTest(3);
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId = */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ setupResolverControllers(personalResolvedComponentInfos,
+ new ArrayList<>(workResolvedComponentInfos));
Intent sendIntent = createSendImageIntent();
markWorkProfileUserAvailable();
@@ -434,8 +425,8 @@
waitForIdle();
assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
- // The work list adapter must only be filled when we open the work tab
- assertThat(activity.getWorkListAdapter().getCount(), is(0));
+ // The work list adapter must be populated in advance before tapping the other tab
+ assertThat(activity.getWorkListAdapter().getCount(), is(4));
}
@Test
@@ -445,17 +436,7 @@
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
markWorkProfileUserAvailable();
@@ -474,34 +455,7 @@
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- when(sOverrides.resolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.resolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(sOverrides.workProfileUserHandle)))
- .thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(sOverrides.workProfileUserHandle)))
- .thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
markWorkProfileUserAvailable();
@@ -521,18 +475,7 @@
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
@@ -552,18 +495,7 @@
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
@@ -597,35 +529,7 @@
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(1);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
-
- when(sOverrides.resolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.resolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(sOverrides.workProfileUserHandle)))
- .thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(sOverrides.workProfileUserHandle)))
- .thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
- when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
@@ -644,10 +548,10 @@
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId= */ 10);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
- when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
@@ -672,6 +576,82 @@
assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
}
+ @Test
+ public void testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ sOverrides.hasCrossProfileIntents = false;
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+
+ onView(withText(R.string.resolver_cant_share_with_work_apps))
+ .check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testWorkTab_workProfileDisabled_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ sOverrides.isQuietModeEnabled = true;
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ onView(withText(R.string.resolver_turn_on_work_apps))
+ .check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testWorkTab_noWorkTargets_emptyStateShown() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(0);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withId(R.id.contentPanel))
+ .perform(swipeUp());
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ onView(withText(R.string.resolver_no_apps_available))
+ .check(matches(isDisplayed()));
+ }
+
private Intent createSendImageIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
@@ -722,4 +702,21 @@
private void markWorkProfileUserAvailable() {
ResolverWrapperActivity.sOverrides.workProfileUserHandle = UserHandle.of(10);
}
+
+ private void setupResolverControllers(
+ List<ResolvedComponentInfo> personalResolvedComponentInfos,
+ List<ResolvedComponentInfo> workResolvedComponentInfos) {
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class),
+ eq(UserHandle.SYSTEM)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index 36c8724..2087104 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -47,6 +47,15 @@
filterLastUsed, createListController(userHandle), useLayoutForBrowsables, this);
}
+ @Override
+ protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
+ Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed) {
+ AbstractMultiProfilePagerAdapter multiProfilePagerAdapter =
+ super.createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
+ multiProfilePagerAdapter.setInjector(sOverrides.multiPagerAdapterInjector);
+ return multiProfilePagerAdapter;
+ }
+
ResolverWrapperAdapter getAdapter() {
return (ResolverWrapperAdapter) mMultiProfilePagerAdapter.getActiveListAdapter();
}
@@ -124,6 +133,9 @@
public ResolverListController workResolverListController;
public Boolean isVoiceInteraction;
public UserHandle workProfileUserHandle;
+ public boolean hasCrossProfileIntents;
+ public boolean isQuietModeEnabled;
+ public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
public void reset() {
onSafelyStartCallback = null;
@@ -132,6 +144,26 @@
resolverListController = mock(ResolverListController.class);
workResolverListController = mock(ResolverListController.class);
workProfileUserHandle = null;
+ hasCrossProfileIntents = true;
+ isQuietModeEnabled = false;
+ multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
+ @Override
+ public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
+ int targetUserId) {
+ return hasCrossProfileIntents;
+ }
+
+ @Override
+ public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+ return isQuietModeEnabled;
+ }
+
+ @Override
+ public void requestQuietModeEnabled(boolean enabled,
+ UserHandle workProfileUserHandle) {
+ isQuietModeEnabled = enabled;
+ }
+ };
}
}
}
\ No newline at end of file
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index af115b1..4b95e4d 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -381,6 +381,8 @@
<permission name="android.permission.REBOOT"/>
<!-- Permission required for access VIBRATOR_STATE. -->
<permission name="android.permission.ACCESS_VIBRATOR_STATE"/>
+ <!-- Permission required for UsageStatsTest CTS test. -->
+ <permission name="android.permission.MANAGE_NOTIFICATIONS"/>
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 06d4fbd..ce8ff7d 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -867,7 +867,8 @@
}
}
- private ColorSpace(
+ /** @hide */
+ ColorSpace(
@NonNull String name,
@NonNull Model model,
@IntRange(from = MIN_ID, to = MAX_ID) int id) {
diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java
new file mode 100644
index 0000000..f9033a5
--- /dev/null
+++ b/graphics/java/android/graphics/ParcelableColorSpace.java
@@ -0,0 +1,183 @@
+/*
+ * 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.graphics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A {@link Parcelable} {@link ColorSpace}. In order to enable parceling, the ColorSpace
+ * must be either a {@link ColorSpace.Named Named} ColorSpace or a {@link ColorSpace.Rgb} instance
+ * that has an ICC parametric transfer function as returned by {@link Rgb#getTransferParameters()}.
+ * TODO: Make public
+ * @hide
+ */
+public final class ParcelableColorSpace extends ColorSpace implements Parcelable {
+ private final ColorSpace mColorSpace;
+
+ /**
+ * Checks if the given ColorSpace is able to be parceled. A ColorSpace can only be
+ * parceled if it is a {@link ColorSpace.Named Named} ColorSpace or a {@link ColorSpace.Rgb}
+ * instance that has an ICC parametric transfer function as returned by
+ * {@link Rgb#getTransferParameters()}
+ */
+ public static boolean isParcelable(@NonNull ColorSpace colorSpace) {
+ if (colorSpace.getId() == ColorSpace.MIN_ID) {
+ if (!(colorSpace instanceof ColorSpace.Rgb)) {
+ return false;
+ }
+ ColorSpace.Rgb rgb = (ColorSpace.Rgb) colorSpace;
+ if (rgb.getTransferParameters() == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Constructs a new ParcelableColorSpace that wraps the provided ColorSpace.
+ *
+ * @param colorSpace The ColorSpace to wrap. The ColorSpace must be either named or be an
+ * RGB ColorSpace with an ICC parametric transfer function.
+ * @throws IllegalArgumentException If the provided ColorSpace does not satisfy the requirements
+ * to be parceled. See {@link #isParcelable(ColorSpace)}.
+ */
+ public ParcelableColorSpace(@NonNull ColorSpace colorSpace) {
+ super(colorSpace.getName(), colorSpace.getModel(), colorSpace.getId());
+ mColorSpace = colorSpace;
+
+ if (mColorSpace.getId() == ColorSpace.MIN_ID) {
+ if (!(mColorSpace instanceof ColorSpace.Rgb)) {
+ throw new IllegalArgumentException(
+ "Unable to parcel unknown ColorSpaces that are not ColorSpace.Rgb");
+ }
+ ColorSpace.Rgb rgb = (ColorSpace.Rgb) mColorSpace;
+ if (rgb.getTransferParameters() == null) {
+ throw new IllegalArgumentException("ColorSpace must use an ICC "
+ + "parametric transfer function to be parcelable");
+ }
+ }
+ }
+
+ public @NonNull ColorSpace getColorSpace() {
+ return mColorSpace;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ final int id = mColorSpace.getId();
+ dest.writeInt(id);
+ if (id == ColorSpace.MIN_ID) {
+ // Not a named color space. We have to actually write, like, stuff. And things. Ugh.
+ // Cast is safe because this was asserted in the constructor
+ ColorSpace.Rgb rgb = (ColorSpace.Rgb) mColorSpace;
+ dest.writeString(rgb.getName());
+ dest.writeFloatArray(rgb.getPrimaries());
+ dest.writeFloatArray(rgb.getWhitePoint());
+ ColorSpace.Rgb.TransferParameters transferParameters = rgb.getTransferParameters();
+ dest.writeDouble(transferParameters.a);
+ dest.writeDouble(transferParameters.b);
+ dest.writeDouble(transferParameters.c);
+ dest.writeDouble(transferParameters.d);
+ dest.writeDouble(transferParameters.e);
+ dest.writeDouble(transferParameters.f);
+ dest.writeDouble(transferParameters.g);
+ }
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<ParcelableColorSpace> CREATOR =
+ new Parcelable.Creator<ParcelableColorSpace>() {
+
+ public @NonNull ParcelableColorSpace createFromParcel(@NonNull Parcel in) {
+ final int id = in.readInt();
+ if (id == ColorSpace.MIN_ID) {
+ String name = in.readString();
+ float[] primaries = in.createFloatArray();
+ float[] whitePoint = in.createFloatArray();
+ double a = in.readDouble();
+ double b = in.readDouble();
+ double c = in.readDouble();
+ double d = in.readDouble();
+ double e = in.readDouble();
+ double f = in.readDouble();
+ double g = in.readDouble();
+ ColorSpace.Rgb.TransferParameters function =
+ new ColorSpace.Rgb.TransferParameters(a, b, c, d, e, f, g);
+ return new ParcelableColorSpace(
+ new ColorSpace.Rgb(name, primaries, whitePoint, function));
+ } else {
+ return new ParcelableColorSpace(ColorSpace.get(id));
+ }
+ }
+
+ public ParcelableColorSpace[] newArray(int size) {
+ return new ParcelableColorSpace[size];
+ }
+ };
+
+ @Override
+ public boolean isWideGamut() {
+ return mColorSpace.isWideGamut();
+ }
+
+ @Override
+ public float getMinValue(int component) {
+ return mColorSpace.getMinValue(component);
+ }
+
+ @Override
+ public float getMaxValue(int component) {
+ return mColorSpace.getMaxValue(component);
+ }
+
+ @Override
+ public @NonNull float[] toXyz(@NonNull float[] v) {
+ return mColorSpace.toXyz(v);
+ }
+
+ @Override
+ public @NonNull float[] fromXyz(@NonNull float[] v) {
+ return mColorSpace.fromXyz(v);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ParcelableColorSpace other = (ParcelableColorSpace) o;
+ return mColorSpace.equals(other.mColorSpace);
+ }
+
+ @Override
+ public int hashCode() {
+ return mColorSpace.hashCode();
+ }
+
+ /** @hide */
+ @Override
+ long getNativeInstance() {
+ return mColorSpace.getNativeInstance();
+ }
+}
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 3835b2d..752695f 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -22,8 +22,8 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.animation.RenderNodeAnimator;
import android.view.NativeVectorDrawableAnimator;
-import android.view.RenderNodeAnimator;
import android.view.Surface;
import android.view.View;
diff --git a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java b/graphics/java/android/graphics/animation/FallbackLUTInterpolator.java
similarity index 89%
rename from core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
rename to graphics/java/android/graphics/animation/FallbackLUTInterpolator.java
index d28ab07..36062c1 100644
--- a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
+++ b/graphics/java/android/graphics/animation/FallbackLUTInterpolator.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.view.animation;
+package android.graphics.animation;
import android.animation.TimeInterpolator;
import android.util.TimeUtils;
@@ -26,14 +26,15 @@
* with {@link HasNativeInterpolator}
*
* This implements TimeInterpolator to allow for easier interop with Animators
+ * @hide
*/
@HasNativeInterpolator
-public class FallbackLUTInterpolator implements NativeInterpolatorFactory, TimeInterpolator {
+public class FallbackLUTInterpolator implements NativeInterpolator, TimeInterpolator {
// If the duration of an animation is more than 300 frames, we cap the sample size to 300.
private static final int MAX_SAMPLE_POINTS = 300;
private TimeInterpolator mSourceInterpolator;
- private final float mLut[];
+ private final float[] mLut;
/**
* Used to cache the float[] LUT for use across multiple native
@@ -50,7 +51,7 @@
// We need 2 frame values as the minimal.
int numAnimFrames = Math.max(2, (int) Math.ceil(((double) duration) / animIntervalMs));
numAnimFrames = Math.min(numAnimFrames, MAX_SAMPLE_POINTS);
- float values[] = new float[numAnimFrames];
+ float[] values = new float[numAnimFrames];
float lastFrame = numAnimFrames - 1;
for (int i = 0; i < numAnimFrames; i++) {
float inValue = i / lastFrame;
@@ -61,7 +62,7 @@
@Override
public long createNativeInterpolator() {
- return NativeInterpolatorFactoryHelper.createLutInterpolator(mLut);
+ return NativeInterpolatorFactory.createLutInterpolator(mLut);
}
/**
@@ -69,7 +70,7 @@
*/
public static long createNativeInterpolator(TimeInterpolator interpolator, long duration) {
float[] lut = createLUT(interpolator, duration);
- return NativeInterpolatorFactoryHelper.createLutInterpolator(lut);
+ return NativeInterpolatorFactory.createLutInterpolator(lut);
}
@Override
diff --git a/core/java/com/android/internal/view/animation/HasNativeInterpolator.java b/graphics/java/android/graphics/animation/HasNativeInterpolator.java
similarity index 89%
rename from core/java/com/android/internal/view/animation/HasNativeInterpolator.java
rename to graphics/java/android/graphics/animation/HasNativeInterpolator.java
index 48ea4da..c53d5a4 100644
--- a/core/java/com/android/internal/view/animation/HasNativeInterpolator.java
+++ b/graphics/java/android/graphics/animation/HasNativeInterpolator.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.view.animation;
+package android.graphics.animation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -23,10 +23,10 @@
/**
* This is a class annotation that signals that it is safe to create
- * a native interpolator counterpart via {@link NativeInterpolatorFactory}
+ * a native interpolator counterpart via {@link NativeInterpolator}
*
* The idea here is to prevent subclasses of interpolators from being treated as a
- * NativeInterpolatorFactory, and instead have them fall back to the LUT & LERP
+ * NativeInterpolator, and instead have them fall back to the LUT & LERP
* method like a custom interpolator.
*
* @hide
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java b/graphics/java/android/graphics/animation/NativeInterpolator.java
similarity index 71%
copy from core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
copy to graphics/java/android/graphics/animation/NativeInterpolator.java
index fcacd52..1e6fea8 100644
--- a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
+++ b/graphics/java/android/graphics/animation/NativeInterpolator.java
@@ -14,8 +14,16 @@
* limitations under the License.
*/
-package com.android.internal.view.animation;
+package android.graphics.animation;
-public interface NativeInterpolatorFactory {
+/**
+ * @hide
+ */
+public interface NativeInterpolator {
+ /**
+ * Generates a native interpolator object that can be used by HardwareRenderer to draw
+ * RenderNodes.
+ * @return ptr to native object
+ */
long createNativeInterpolator();
}
diff --git a/graphics/java/android/graphics/animation/NativeInterpolatorFactory.java b/graphics/java/android/graphics/animation/NativeInterpolatorFactory.java
new file mode 100644
index 0000000..3886614
--- /dev/null
+++ b/graphics/java/android/graphics/animation/NativeInterpolatorFactory.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open 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.graphics.animation;
+
+import android.animation.TimeInterpolator;
+
+/**
+ * Static utility class for constructing native interpolators to keep the
+ * JNI simpler
+ *
+ * @hide
+ */
+public final class NativeInterpolatorFactory {
+ private NativeInterpolatorFactory() {}
+
+ /**
+ * Create a native interpolator from the provided param generating a LUT variant if a native
+ * implementation does not exist.
+ */
+ public static long createNativeInterpolator(TimeInterpolator interpolator, long
+ duration) {
+ if (interpolator == null) {
+ return createLinearInterpolator();
+ } else if (RenderNodeAnimator.isNativeInterpolator(interpolator)) {
+ return ((NativeInterpolator) interpolator).createNativeInterpolator();
+ } else {
+ return FallbackLUTInterpolator.createNativeInterpolator(interpolator, duration);
+ }
+ }
+
+ /** Creates a specialized native interpolator for Accelerate/Decelerate */
+ public static native long createAccelerateDecelerateInterpolator();
+ /** Creates a specialized native interpolator for Accelerate */
+ public static native long createAccelerateInterpolator(float factor);
+ /** Creates a specialized native interpolator for Anticipate */
+ public static native long createAnticipateInterpolator(float tension);
+ /** Creates a specialized native interpolator for Anticipate with Overshoot */
+ public static native long createAnticipateOvershootInterpolator(float tension);
+ /** Creates a specialized native interpolator for Bounce */
+ public static native long createBounceInterpolator();
+ /** Creates a specialized native interpolator for Cycle */
+ public static native long createCycleInterpolator(float cycles);
+ /** Creates a specialized native interpolator for Decelerate */
+ public static native long createDecelerateInterpolator(float factor);
+ /** Creates a specialized native interpolator for Linear interpolation */
+ public static native long createLinearInterpolator();
+ /** Creates a specialized native interpolator for Overshoot */
+ public static native long createOvershootInterpolator(float tension);
+ /** Creates a specialized native interpolator for along traveling along a Path */
+ public static native long createPathInterpolator(float[] x, float[] y);
+ /** Creates a specialized native interpolator for LUT */
+ public static native long createLutInterpolator(float[] values);
+}
diff --git a/graphics/java/android/graphics/animation/RenderNodeAnimator.java b/graphics/java/android/graphics/animation/RenderNodeAnimator.java
new file mode 100644
index 0000000..282b2f9
--- /dev/null
+++ b/graphics/java/android/graphics/animation/RenderNodeAnimator.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2014 The Android Open 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.graphics.animation;
+
+import android.animation.Animator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.graphics.CanvasProperty;
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Choreographer;
+
+import com.android.internal.util.VirtualRefBasePtr;
+
+import java.util.ArrayList;
+
+/**
+ * @hide
+ */
+public class RenderNodeAnimator extends Animator {
+ // Keep in sync with enum RenderProperty in Animator.h
+ public static final int TRANSLATION_X = 0;
+ public static final int TRANSLATION_Y = 1;
+ public static final int TRANSLATION_Z = 2;
+ public static final int SCALE_X = 3;
+ public static final int SCALE_Y = 4;
+ public static final int ROTATION = 5;
+ public static final int ROTATION_X = 6;
+ public static final int ROTATION_Y = 7;
+ public static final int X = 8;
+ public static final int Y = 9;
+ public static final int Z = 10;
+ public static final int ALPHA = 11;
+ // The last value in the enum, used for array size initialization
+ public static final int LAST_VALUE = ALPHA;
+
+ // Keep in sync with enum PaintFields in Animator.h
+ public static final int PAINT_STROKE_WIDTH = 0;
+
+ /**
+ * Field for the Paint alpha channel, which should be specified as a value
+ * between 0 and 255.
+ */
+ public static final int PAINT_ALPHA = 1;
+
+ private VirtualRefBasePtr mNativePtr;
+
+ private Handler mHandler;
+ private RenderNode mTarget;
+ private ViewListener mViewListener;
+ private int mRenderProperty = -1;
+ private float mFinalValue;
+ private TimeInterpolator mInterpolator;
+
+ private static final int STATE_PREPARE = 0;
+ private static final int STATE_DELAYED = 1;
+ private static final int STATE_RUNNING = 2;
+ private static final int STATE_FINISHED = 3;
+ private int mState = STATE_PREPARE;
+
+ private long mUnscaledDuration = 300;
+ private long mUnscaledStartDelay = 0;
+ // If this is true, we will run any start delays on the UI thread. This is
+ // the safe default, and is necessary to ensure start listeners fire at
+ // the correct time. Animators created by RippleDrawable (the
+ // CanvasProperty<> ones) do not have this expectation, and as such will
+ // set this to false so that the renderthread handles the startdelay instead
+ private final boolean mUiThreadHandlesDelay;
+ private long mStartDelay = 0;
+ private long mStartTime;
+
+ /**
+ * Interface used by the view system to update the view hierarchy in conjunction
+ * with this animator.
+ */
+ public interface ViewListener {
+ /** notify the listener that an alpha animation has begun. */
+ void onAlphaAnimationStart(float finalAlpha);
+ /** notify the listener that the animator has mutated a value that requires invalidation */
+ void invalidateParent(boolean forceRedraw);
+ }
+
+ public RenderNodeAnimator(int property, float finalValue) {
+ mRenderProperty = property;
+ mFinalValue = finalValue;
+ mUiThreadHandlesDelay = true;
+ init(nCreateAnimator(property, finalValue));
+ }
+
+ public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) {
+ init(nCreateCanvasPropertyFloatAnimator(
+ property.getNativeContainer(), finalValue));
+ mUiThreadHandlesDelay = false;
+ }
+
+ /**
+ * Creates a new render node animator for a field on a Paint property.
+ *
+ * @param property The paint property to target
+ * @param paintField Paint field to animate, one of {@link #PAINT_ALPHA} or
+ * {@link #PAINT_STROKE_WIDTH}
+ * @param finalValue The target value for the property
+ */
+ public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) {
+ init(nCreateCanvasPropertyPaintAnimator(
+ property.getNativeContainer(), paintField, finalValue));
+ mUiThreadHandlesDelay = false;
+ }
+
+ public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) {
+ init(nCreateRevealAnimator(x, y, startRadius, endRadius));
+ mUiThreadHandlesDelay = true;
+ }
+
+ private void init(long ptr) {
+ mNativePtr = new VirtualRefBasePtr(ptr);
+ }
+
+ private void checkMutable() {
+ if (mState != STATE_PREPARE) {
+ throw new IllegalStateException("Animator has already started, cannot change it now!");
+ }
+ if (mNativePtr == null) {
+ throw new IllegalStateException("Animator's target has been destroyed "
+ + "(trying to modify an animation after activity destroy?)");
+ }
+ }
+
+ static boolean isNativeInterpolator(TimeInterpolator interpolator) {
+ return interpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class);
+ }
+
+ private void applyInterpolator() {
+ if (mInterpolator == null || mNativePtr == null) return;
+
+ long ni;
+ if (isNativeInterpolator(mInterpolator)) {
+ ni = ((NativeInterpolator) mInterpolator).createNativeInterpolator();
+ } else {
+ long duration = nGetDuration(mNativePtr.get());
+ ni = FallbackLUTInterpolator.createNativeInterpolator(mInterpolator, duration);
+ }
+ nSetInterpolator(mNativePtr.get(), ni);
+ }
+
+ @Override
+ public void start() {
+ if (mTarget == null) {
+ throw new IllegalStateException("Missing target!");
+ }
+
+ if (mState != STATE_PREPARE) {
+ throw new IllegalStateException("Already started!");
+ }
+
+ mState = STATE_DELAYED;
+ if (mHandler == null) {
+ mHandler = new Handler(true);
+ }
+ applyInterpolator();
+
+ if (mNativePtr == null) {
+ // It's dead, immediately cancel
+ cancel();
+ } else if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
+ nSetStartDelay(mNativePtr.get(), mStartDelay);
+ doStart();
+ } else {
+ getHelper().addDelayedAnimation(this);
+ }
+ }
+
+ private void doStart() {
+ // Alpha is a special snowflake that has the canonical value stored
+ // in mTransformationInfo instead of in RenderNode, so we need to update
+ // it with the final value here.
+ if (mRenderProperty == RenderNodeAnimator.ALPHA && mViewListener != null) {
+ mViewListener.onAlphaAnimationStart(mFinalValue);
+ }
+
+ moveToRunningState();
+
+ if (mViewListener != null) {
+ // Kick off a frame to start the process
+ mViewListener.invalidateParent(false);
+ }
+ }
+
+ private void moveToRunningState() {
+ mState = STATE_RUNNING;
+ if (mNativePtr != null) {
+ nStart(mNativePtr.get());
+ }
+ notifyStartListeners();
+ }
+
+ private void notifyStartListeners() {
+ final ArrayList<AnimatorListener> listeners = cloneListeners();
+ final int numListeners = listeners == null ? 0 : listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ listeners.get(i).onAnimationStart(this);
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (mState != STATE_PREPARE && mState != STATE_FINISHED) {
+ if (mState == STATE_DELAYED) {
+ getHelper().removeDelayedAnimation(this);
+ moveToRunningState();
+ }
+
+ final ArrayList<AnimatorListener> listeners = cloneListeners();
+ final int numListeners = listeners == null ? 0 : listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ listeners.get(i).onAnimationCancel(this);
+ }
+
+ end();
+ }
+ }
+
+ @Override
+ public void end() {
+ if (mState != STATE_FINISHED) {
+ if (mState < STATE_RUNNING) {
+ getHelper().removeDelayedAnimation(this);
+ doStart();
+ }
+ if (mNativePtr != null) {
+ nEnd(mNativePtr.get());
+ if (mViewListener != null) {
+ // Kick off a frame to flush the state change
+ mViewListener.invalidateParent(false);
+ }
+ } else {
+ // It's already dead, jump to onFinish
+ onFinished();
+ }
+ }
+ }
+
+ @Override
+ public void pause() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void resume() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @hide */
+ public void setViewListener(ViewListener listener) {
+ mViewListener = listener;
+ }
+
+ /** Sets the animation target to the owning view of the RecordingCanvas */
+ public final void setTarget(RecordingCanvas canvas) {
+ setTarget(canvas.mNode);
+ }
+
+ /** Sets the node that is to be the target of this animation */
+ protected void setTarget(RenderNode node) {
+ checkMutable();
+ if (mTarget != null) {
+ throw new IllegalStateException("Target already set!");
+ }
+ nSetListener(mNativePtr.get(), this);
+ mTarget = node;
+ mTarget.addAnimator(this);
+ }
+
+ /** Set the start value for the animation */
+ public void setStartValue(float startValue) {
+ checkMutable();
+ nSetStartValue(mNativePtr.get(), startValue);
+ }
+
+ @Override
+ public void setStartDelay(long startDelay) {
+ checkMutable();
+ if (startDelay < 0) {
+ throw new IllegalArgumentException("startDelay must be positive; " + startDelay);
+ }
+ mUnscaledStartDelay = startDelay;
+ mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
+ }
+
+ @Override
+ public long getStartDelay() {
+ return mUnscaledStartDelay;
+ }
+
+ @Override
+ public RenderNodeAnimator setDuration(long duration) {
+ checkMutable();
+ if (duration < 0) {
+ throw new IllegalArgumentException("duration must be positive; " + duration);
+ }
+ mUnscaledDuration = duration;
+ nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale()));
+ return this;
+ }
+
+ @Override
+ public long getDuration() {
+ return mUnscaledDuration;
+ }
+
+ @Override
+ public long getTotalDuration() {
+ return mUnscaledDuration + mUnscaledStartDelay;
+ }
+
+ @Override
+ public boolean isRunning() {
+ return mState == STATE_DELAYED || mState == STATE_RUNNING;
+ }
+
+ @Override
+ public boolean isStarted() {
+ return mState != STATE_PREPARE;
+ }
+
+ @Override
+ public void setInterpolator(TimeInterpolator interpolator) {
+ checkMutable();
+ mInterpolator = interpolator;
+ }
+
+ @Override
+ public TimeInterpolator getInterpolator() {
+ return mInterpolator;
+ }
+
+ protected void onFinished() {
+ if (mState == STATE_PREPARE) {
+ // Unlikely but possible, the native side has been destroyed
+ // before we have started.
+ releaseNativePtr();
+ return;
+ }
+ if (mState == STATE_DELAYED) {
+ getHelper().removeDelayedAnimation(this);
+ notifyStartListeners();
+ }
+ mState = STATE_FINISHED;
+
+ final ArrayList<AnimatorListener> listeners = cloneListeners();
+ final int numListeners = listeners == null ? 0 : listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ listeners.get(i).onAnimationEnd(this);
+ }
+
+ // Release the native object, as it has a global reference to us. This
+ // breaks the cyclic reference chain, and allows this object to be
+ // GC'd
+ releaseNativePtr();
+ }
+
+ private void releaseNativePtr() {
+ if (mNativePtr != null) {
+ mNativePtr.release();
+ mNativePtr = null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private ArrayList<AnimatorListener> cloneListeners() {
+ ArrayList<AnimatorListener> listeners = getListeners();
+ if (listeners != null) {
+ listeners = (ArrayList<AnimatorListener>) listeners.clone();
+ }
+ return listeners;
+ }
+
+ public long getNativeAnimator() {
+ return mNativePtr.get();
+ }
+
+ /**
+ * @return true if the animator was started, false if still delayed
+ */
+ private boolean processDelayed(long frameTimeMs) {
+ if (mStartTime == 0) {
+ mStartTime = frameTimeMs;
+ } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
+ doStart();
+ return true;
+ }
+ return false;
+ }
+
+ private static DelayedAnimationHelper getHelper() {
+ DelayedAnimationHelper helper = sAnimationHelper.get();
+ if (helper == null) {
+ helper = new DelayedAnimationHelper();
+ sAnimationHelper.set(helper);
+ }
+ return helper;
+ }
+
+ private static ThreadLocal<DelayedAnimationHelper> sAnimationHelper =
+ new ThreadLocal<DelayedAnimationHelper>();
+
+ private static class DelayedAnimationHelper implements Runnable {
+
+ private ArrayList<RenderNodeAnimator> mDelayedAnims = new ArrayList<RenderNodeAnimator>();
+ private final Choreographer mChoreographer;
+ private boolean mCallbackScheduled;
+
+ DelayedAnimationHelper() {
+ mChoreographer = Choreographer.getInstance();
+ }
+
+ public void addDelayedAnimation(RenderNodeAnimator animator) {
+ mDelayedAnims.add(animator);
+ scheduleCallback();
+ }
+
+ public void removeDelayedAnimation(RenderNodeAnimator animator) {
+ mDelayedAnims.remove(animator);
+ }
+
+ private void scheduleCallback() {
+ if (!mCallbackScheduled) {
+ mCallbackScheduled = true;
+ mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
+ }
+ }
+
+ @Override
+ public void run() {
+ long frameTimeMs = mChoreographer.getFrameTime();
+ mCallbackScheduled = false;
+
+ int end = 0;
+ for (int i = 0; i < mDelayedAnims.size(); i++) {
+ RenderNodeAnimator animator = mDelayedAnims.get(i);
+ if (!animator.processDelayed(frameTimeMs)) {
+ if (end != i) {
+ mDelayedAnims.set(end, animator);
+ }
+ end++;
+ }
+ }
+ while (mDelayedAnims.size() > end) {
+ mDelayedAnims.remove(mDelayedAnims.size() - 1);
+ }
+
+ if (mDelayedAnims.size() > 0) {
+ scheduleCallback();
+ }
+ }
+ }
+
+ // Called by native
+ private static void callOnFinished(RenderNodeAnimator animator) {
+ if (animator.mHandler != null) {
+ animator.mHandler.post(animator::onFinished);
+ } else {
+ new Handler(Looper.getMainLooper(), null, true).post(animator::onFinished);
+ }
+ }
+
+ @Override
+ public Animator clone() {
+ throw new IllegalStateException("Cannot clone this animator");
+ }
+
+ @Override
+ public void setAllowRunningAsynchronously(boolean mayRunAsync) {
+ checkMutable();
+ nSetAllowRunningAsync(mNativePtr.get(), mayRunAsync);
+ }
+
+ private static native long nCreateAnimator(int property, float finalValue);
+ private static native long nCreateCanvasPropertyFloatAnimator(
+ long canvasProperty, float finalValue);
+ private static native long nCreateCanvasPropertyPaintAnimator(
+ long canvasProperty, int paintField, float finalValue);
+ private static native long nCreateRevealAnimator(
+ int x, int y, float startRadius, float endRadius);
+
+ private static native void nSetStartValue(long nativePtr, float startValue);
+ private static native void nSetDuration(long nativePtr, long duration);
+ private static native long nGetDuration(long nativePtr);
+ private static native void nSetStartDelay(long nativePtr, long startDelay);
+ private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
+ private static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync);
+ private static native void nSetListener(long animPtr, RenderNodeAnimator listener);
+
+ private static native void nStart(long animPtr);
+ private static native void nEnd(long animPtr);
+}
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 1acf6c5..9fb72cf 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -42,6 +42,7 @@
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
+import android.graphics.animation.NativeInterpolatorFactory;
import android.os.Build;
import android.os.Handler;
import android.util.ArrayMap;
@@ -54,7 +55,6 @@
import android.util.TimeUtils;
import android.view.Choreographer;
import android.view.NativeVectorDrawableAnimator;
-import android.view.RenderNodeAnimatorSetHelper;
import android.view.View;
import com.android.internal.R;
@@ -1532,7 +1532,7 @@
long startDelay = extraDelay + animator.getStartDelay();
TimeInterpolator interpolator = animator.getInterpolator();
long nativeInterpolator =
- RenderNodeAnimatorSetHelper.createNativeInterpolator(interpolator, duration);
+ NativeInterpolatorFactory.createNativeInterpolator(interpolator, duration);
startDelay *= ValueAnimator.getDurationScale();
duration *= ValueAnimator.getDurationScale();
@@ -1548,7 +1548,7 @@
* to the last seen RenderNode target and start right away.
*/
protected void recordLastSeenTarget(RecordingCanvas canvas) {
- final RenderNode node = RenderNodeAnimatorSetHelper.getTarget(canvas);
+ final RenderNode node = canvas.mNode;
mLastSeenTarget = new WeakReference<RenderNode>(node);
// Add the animator to the list of animators on every draw
if (mInitialized || mPendingAnimationActions.size() > 0) {
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index cce9ba3..0f37695 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -25,9 +25,9 @@
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
+import android.graphics.animation.RenderNodeAnimator;
import android.util.FloatProperty;
import android.util.MathUtils;
-import android.view.RenderNodeAnimator;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 89a9b99..84c07d7 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -16,16 +16,16 @@
#include "Readback.h"
-#include "pipeline/skia/LayerDrawable.h"
-#include "renderthread/EglManager.h"
-#include "renderthread/VulkanManager.h"
-
-#include <gui/Surface.h>
-#include <ui/Fence.h>
+#include <sync/sync.h>
+#include <system/window.h>
#include <ui/GraphicBuffer.h>
+
#include "DeferredLayerUpdater.h"
#include "Properties.h"
#include "hwui/Bitmap.h"
+#include "pipeline/skia/LayerDrawable.h"
+#include "renderthread/EglManager.h"
+#include "renderthread/VulkanManager.h"
#include "utils/Color.h"
#include "utils/MathUtils.h"
#include "utils/TraceUtils.h"
@@ -35,40 +35,43 @@
namespace android {
namespace uirenderer {
-CopyResult Readback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) {
+CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap) {
ATRACE_CALL();
// Setup the source
- sp<GraphicBuffer> sourceBuffer;
- sp<Fence> sourceFence;
+ AHardwareBuffer* rawSourceBuffer;
+ int rawSourceFence;
Matrix4 texTransform;
- status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data);
+ status_t err = ANativeWindow_getLastQueuedBuffer(window, &rawSourceBuffer, &rawSourceFence,
+ texTransform.data);
+ base::unique_fd sourceFence(rawSourceFence);
texTransform.invalidateType();
if (err != NO_ERROR) {
ALOGW("Failed to get last queued buffer, error = %d", err);
return CopyResult::UnknownError;
}
- if (!sourceBuffer.get()) {
+ if (rawSourceBuffer == nullptr) {
ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
return CopyResult::SourceEmpty;
}
- if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
+
+ std::unique_ptr<AHardwareBuffer, decltype(&AHardwareBuffer_release)> sourceBuffer(
+ rawSourceBuffer, AHardwareBuffer_release);
+ AHardwareBuffer_Desc description;
+ AHardwareBuffer_describe(sourceBuffer.get(), &description);
+ if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
ALOGW("Surface is protected, unable to copy from it");
return CopyResult::SourceInvalid;
}
- err = sourceFence->wait(500 /* ms */);
- if (err != NO_ERROR) {
+
+ if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
return CopyResult::Timeout;
}
- if (!sourceBuffer.get()) {
- return CopyResult::UnknownError;
- }
- sk_sp<SkColorSpace> colorSpace =
- DataSpaceToColorSpace(static_cast<android_dataspace>(surface.getBuffersDataSpace()));
- sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
- reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()),
- kPremul_SkAlphaType, colorSpace);
+ sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
+ static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
+ sk_sp<SkImage> image =
+ SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
return copyImageInto(image, texTransform, srcRect, bitmap);
}
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index e86a813..e36f1ff 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -47,7 +47,7 @@
/**
* Copies the surface's most recently queued buffer into the provided bitmap.
*/
- CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap);
+ CopyResult copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 91f9447..1df3336 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -204,8 +204,7 @@
void CanvasContext::allocateBuffers() {
if (mNativeSurface) {
- ANativeWindow* anw = mNativeSurface->getNativeWindow();
- ANativeWindow_allocateBuffers(anw);
+ ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow());
}
}
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index f9e401a..1e7fc71 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -317,8 +317,9 @@
int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom,
SkBitmap* bitmap) {
auto& thread = RenderThread::getInstance();
+ ANativeWindow* window = surface.get();
return static_cast<int>(thread.queue().runSync([&]() -> auto {
- return thread.readback().copySurfaceInto(*surface, Rect(left, top, right, bottom), bitmap);
+ return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap);
}));
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 4683e1d..ab0dd2b 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -140,6 +140,10 @@
*/
ANDROID_API void setRenderAheadDepth(int renderAhead);
+ // TODO: This api will need to take in an ANativeWindow instead, but the
+ // caller, ThreadedRenderer, doesn't have access to libandroid due to a
+ // circular dependency, so it can't use the JNI ANativeWindow methods. Once
+ // that is resolved then replace the surface type here.
ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right,
int bottom, SkBitmap* bitmap);
ANDROID_API static void prepareToDraw(Bitmap& bitmap);
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 0a0f7f6..53379b87 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1024,7 +1024,18 @@
String device_name,
int codecFormat);
@UnsupportedAppUsage
- public static native int setPhoneState(int state);
+ public static int setPhoneState(int state) {
+ Log.w(TAG, "Do not use this method! Use AudioManager.setMode() instead.");
+ return 0;
+ }
+ /**
+ * @hide
+ * Send the current audio mode to audio policy manager and audio HAL.
+ * @param state the audio mode
+ * @param uid the UID of the app owning the audio mode
+ * @return command completion status.
+ */
+ public static native int setPhoneState(int state, int uid);
@UnsupportedAppUsage
public static native int setForceUse(int usage, int config);
@UnsupportedAppUsage
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 0e6ade5..2178393 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -875,12 +875,12 @@
}
/**
- * @return the unmodifiable list of transferrable routes for the session.
+ * @return the unmodifiable list of transferable routes for the session.
*/
@NonNull
- public List<MediaRoute2Info> getTransferrableRoutes() {
+ public List<MediaRoute2Info> getTransferableRoutes() {
synchronized (mControllerLock) {
- return getRoutesWithIdsLocked(mSessionInfo.getTransferrableRoutes());
+ return getRoutesWithIdsLocked(mSessionInfo.getTransferableRoutes());
}
}
@@ -1033,12 +1033,12 @@
* all of the following conditions:
* <ul>
* <li>ID should not be included in {@link #getSelectedRoutes()}</li>
- * <li>ID should be included in {@link #getTransferrableRoutes()}</li>
+ * <li>ID should be included in {@link #getTransferableRoutes()}</li>
* </ul>
* If the route doesn't meet any of above conditions, it will be ignored.
*
* @see #getSelectedRoutes()
- * @see #getTransferrableRoutes()
+ * @see #getTransferableRoutes()
* @see RoutingControllerCallback#onControllerUpdated
*/
public void transferToRoute(@NonNull MediaRoute2Info route) {
@@ -1057,9 +1057,9 @@
return;
}
- List<MediaRoute2Info> transferrableRoutes = getTransferrableRoutes();
- if (!checkRouteListContainsRouteId(transferrableRoutes, route.getId())) {
- Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route);
+ List<MediaRoute2Info> transferableRoutes = getTransferableRoutes();
+ if (!checkRouteListContainsRouteId(transferableRoutes, route.getId())) {
+ Log.w(TAG, "Ignoring transferring to a non-transferable route=" + route);
return;
}
@@ -1156,7 +1156,7 @@
.map(MediaRoute2Info::getId).collect(Collectors.toList());
List<String> deselectableRoutes = getDeselectableRoutes().stream()
.map(MediaRoute2Info::getId).collect(Collectors.toList());
- List<String> transferrableRoutes = getTransferrableRoutes().stream()
+ List<String> transferableRoutes = getTransferableRoutes().stream()
.map(MediaRoute2Info::getId).collect(Collectors.toList());
StringBuilder result = new StringBuilder()
@@ -1171,8 +1171,8 @@
.append(", deselectableRoutes={")
.append(deselectableRoutes)
.append("}")
- .append(", transferrableRoutes={")
- .append(transferrableRoutes)
+ .append(", transferableRoutes={")
+ .append(transferableRoutes)
.append("}")
.append(" }");
return result.toString();
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 5ce291c..4801d44 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -259,7 +259,7 @@
/**
* Selects media route for the specified package name.
*
- * If the given route is {@link RoutingController#getTransferrableRoutes() a transferrable
+ * If the given route is {@link RoutingController#getTransferableRoutes() a transferable
* route} of a routing session of the application, the session will be transferred to
* the route. If not, a new routing session will be created.
*
@@ -274,7 +274,7 @@
//TODO: instead of release all controllers, add an API to specify controllers that
// should be released (or is the system controller).
for (RoutingSessionInfo sessionInfo : getRoutingSessions(packageName)) {
- if (!transferred && sessionInfo.getTransferrableRoutes().contains(route.getId())) {
+ if (!transferred && sessionInfo.getTransferableRoutes().contains(route.getId())) {
new RoutingController(sessionInfo).transferToRoute(route);
transferred = true;
} else if (!sessionInfo.isSystemSession()) {
@@ -543,13 +543,13 @@
}
/**
- * @return the unmodifiable list of transferrable routes for the session.
+ * @return the unmodifiable list of transferable routes for the session.
*/
@NonNull
- public List<MediaRoute2Info> getTransferrableRoutes() {
+ public List<MediaRoute2Info> getTransferableRoutes() {
List<String> routeIds;
synchronized (mControllerLock) {
- routeIds = mSessionInfo.getTransferrableRoutes();
+ routeIds = mSessionInfo.getTransferableRoutes();
}
return getRoutesWithIds(routeIds);
}
@@ -643,12 +643,12 @@
* all of the following conditions:
* <ul>
* <li>ID should not be included in {@link #getSelectedRoutes()}</li>
- * <li>ID should be included in {@link #getTransferrableRoutes()}</li>
+ * <li>ID should be included in {@link #getTransferableRoutes()}</li>
* </ul>
* If the route doesn't meet any of above conditions, it will be ignored.
*
* @see #getSelectedRoutes()
- * @see #getTransferrableRoutes()
+ * @see #getTransferableRoutes()
*/
public void transferToRoute(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
@@ -663,8 +663,8 @@
return;
}
- if (!sessionInfo.getTransferrableRoutes().contains(route.getId())) {
- Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route);
+ if (!sessionInfo.getTransferableRoutes().contains(route.getId())) {
+ Log.w(TAG, "Ignoring transferring to a non-transferable route=" + route);
return;
}
diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java
index 05fa511..2bffe8a 100644
--- a/media/java/android/media/MediaScannerConnection.java
+++ b/media/java/android/media/MediaScannerConnection.java
@@ -156,9 +156,7 @@
}
BackgroundThread.getExecutor().execute(() -> {
final Uri uri = scanFileQuietly(mProvider, new File(path));
- if (mClient != null) {
- mClient.onScanCompleted(path, uri);
- }
+ runCallBack(mContext, mClient, path, uri);
});
}
}
@@ -187,9 +185,7 @@
.acquireContentProviderClient(MediaStore.AUTHORITY)) {
for (String path : paths) {
final Uri uri = scanFileQuietly(client, new File(path));
- if (callback != null) {
- callback.onScanCompleted(path, uri);
- }
+ runCallBack(context, callback, path, uri);
}
}
});
@@ -206,6 +202,23 @@
return uri;
}
+ private static void runCallBack(Context context, OnScanCompletedListener callback,
+ String path, Uri uri) {
+ if (callback != null) {
+ // Ignore exceptions from callback to avoid calling app from crashing.
+ // Don't ignore exceptions for apps targeting 'R' or higher.
+ try {
+ callback.onScanCompleted(path, uri);
+ } catch (Throwable e) {
+ if (context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R) {
+ throw e;
+ } else {
+ Log.w(TAG, "Ignoring exception from callback for backward compatibility", e);
+ }
+ }
+ }
+ }
+
@Deprecated
static class ClientProxy implements MediaScannerConnectionClient {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
index ebcb9ed..2e038e6 100644
--- a/media/java/android/media/RouteDiscoveryPreference.java
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -31,8 +31,16 @@
import java.util.Set;
/**
- * A media route discovery preference describing the kinds of routes that media router
+ * A media route discovery preference describing the features of routes that media router
* would like to discover and whether to perform active scanning.
+ * <p>
+ * When {@link MediaRouter2} instances set discovery preferences by calling
+ * {@link MediaRouter2#registerRouteCallback}, they are merged into a single discovery preference
+ * and it is delivered to call {@link MediaRoute2ProviderService#onDiscoveryPreferenceChanged}.
+ * </p><p>
+ * According to the given discovery preference, {@link MediaRoute2ProviderService} discovers
+ * routes and publishes them.
+ * </p>
*
* @see MediaRouter2#registerRouteCallback
*/
@@ -53,12 +61,12 @@
@NonNull
private final List<String> mPreferredFeatures;
- private final boolean mActiveScan;
+ private final boolean mShouldPerformActiveScan;
@Nullable
private final Bundle mExtras;
/**
- * An empty discovery preference.
+ * An empty discovery preference
* @hide
*/
public static final RouteDiscoveryPreference EMPTY =
@@ -66,23 +74,39 @@
RouteDiscoveryPreference(@NonNull Builder builder) {
mPreferredFeatures = builder.mPreferredFeatures;
- mActiveScan = builder.mActiveScan;
+ mShouldPerformActiveScan = builder.mActiveScan;
mExtras = builder.mExtras;
}
RouteDiscoveryPreference(@NonNull Parcel in) {
mPreferredFeatures = in.createStringArrayList();
- mActiveScan = in.readBoolean();
+ mShouldPerformActiveScan = in.readBoolean();
mExtras = in.readBundle();
}
+ /**
+ * Gets the features of routes that media router would like to discover.
+ * <p>
+ * Routes that have at least one of the features will be discovered.
+ * They may include predefined features such as
+ * {@link MediaRoute2Info#FEATURE_LIVE_AUDIO}, {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
+ * or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK} or custom features defined by a provider.
+ * </p>
+ */
@NonNull
public List<String> getPreferredFeatures() {
return mPreferredFeatures;
}
- public boolean isActiveScan() {
- return mActiveScan;
+ /**
+ * Gets whether active scanning should be performed.
+ * <p>
+ * If any of discovery preferences sets this as {@code true}, active scanning will
+ * be performed regardless of other discovery preferences.
+ * </p>
+ */
+ public boolean shouldPerformActiveScan() {
+ return mShouldPerformActiveScan;
}
/**
@@ -100,7 +124,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStringList(mPreferredFeatures);
- dest.writeBoolean(mActiveScan);
+ dest.writeBoolean(mShouldPerformActiveScan);
dest.writeBundle(mExtras);
}
@@ -112,7 +136,7 @@
.append(String.join(", ", mPreferredFeatures))
.append("}")
.append(", activeScan=")
- .append(mActiveScan)
+ .append(mShouldPerformActiveScan)
.append(" }");
return result.toString();
@@ -128,12 +152,12 @@
}
RouteDiscoveryPreference other = (RouteDiscoveryPreference) o;
return Objects.equals(mPreferredFeatures, other.mPreferredFeatures)
- && mActiveScan == other.mActiveScan;
+ && mShouldPerformActiveScan == other.mShouldPerformActiveScan;
}
@Override
public int hashCode() {
- return Objects.hash(mPreferredFeatures, mActiveScan);
+ return Objects.hash(mPreferredFeatures, mShouldPerformActiveScan);
}
/**
@@ -154,12 +178,12 @@
Objects.requireNonNull(preference, "preference must not be null");
mPreferredFeatures = preference.getPreferredFeatures();
- mActiveScan = preference.isActiveScan();
+ mActiveScan = preference.shouldPerformActiveScan();
mExtras = preference.getExtras();
}
/**
- * A constructor to combine all of the preferences into a single preference .
+ * A constructor to combine all of the preferences into a single preference.
* It ignores extras of preferences.
*
* @hide
@@ -171,13 +195,19 @@
mActiveScan = false;
for (RouteDiscoveryPreference preference : preferences) {
routeFeatureSet.addAll(preference.mPreferredFeatures);
- mActiveScan |= preference.mActiveScan;
+ mActiveScan |= preference.mShouldPerformActiveScan;
}
mPreferredFeatures = new ArrayList<>(routeFeatureSet);
}
/**
* Sets preferred route features to discover.
+ * @param preferredFeatures features of routes that media router would like to discover.
+ * May include predefined features
+ * such as {@link MediaRoute2Info#FEATURE_LIVE_AUDIO},
+ * {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
+ * or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK}
+ * or custom features defined by a provider.
*/
@NonNull
public Builder setPreferredFeatures(@NonNull List<String> preferredFeatures) {
@@ -188,9 +218,13 @@
/**
* Sets if active scanning should be performed.
+ * <p>
+ * Since active scanning uses more system resources, set this as {@code true} only
+ * when it's necessary.
+ * </p>
*/
@NonNull
- public Builder setActiveScan(boolean activeScan) {
+ public Builder setShouldPerformActiveScan(boolean activeScan) {
mActiveScan = activeScan;
return this;
}
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index 0d4e666..19a46ce 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -55,7 +55,7 @@
final List<String> mSelectedRoutes;
final List<String> mSelectableRoutes;
final List<String> mDeselectableRoutes;
- final List<String> mTransferrableRoutes;
+ final List<String> mTransferableRoutes;
final int mVolumeHandling;
final int mVolumeMax;
@@ -79,8 +79,8 @@
convertToUniqueRouteIds(builder.mSelectableRoutes));
mDeselectableRoutes = Collections.unmodifiableList(
convertToUniqueRouteIds(builder.mDeselectableRoutes));
- mTransferrableRoutes = Collections.unmodifiableList(
- convertToUniqueRouteIds(builder.mTransferrableRoutes));
+ mTransferableRoutes = Collections.unmodifiableList(
+ convertToUniqueRouteIds(builder.mTransferableRoutes));
mVolumeHandling = builder.mVolumeHandling;
mVolumeMax = builder.mVolumeMax;
@@ -100,7 +100,7 @@
mSelectedRoutes = ensureList(src.createStringArrayList());
mSelectableRoutes = ensureList(src.createStringArrayList());
mDeselectableRoutes = ensureList(src.createStringArrayList());
- mTransferrableRoutes = ensureList(src.createStringArrayList());
+ mTransferableRoutes = ensureList(src.createStringArrayList());
mVolumeHandling = src.readInt();
mVolumeMax = src.readInt();
@@ -193,11 +193,11 @@
}
/**
- * Gets the list of ids of transferrable routes for the session.
+ * Gets the list of ids of transferable routes for the session.
*/
@NonNull
- public List<String> getTransferrableRoutes() {
- return mTransferrableRoutes;
+ public List<String> getTransferableRoutes() {
+ return mTransferableRoutes;
}
/**
@@ -260,7 +260,7 @@
dest.writeStringList(mSelectedRoutes);
dest.writeStringList(mSelectableRoutes);
dest.writeStringList(mDeselectableRoutes);
- dest.writeStringList(mTransferrableRoutes);
+ dest.writeStringList(mTransferableRoutes);
dest.writeInt(mVolumeHandling);
dest.writeInt(mVolumeMax);
dest.writeInt(mVolume);
@@ -284,7 +284,7 @@
&& Objects.equals(mSelectedRoutes, other.mSelectedRoutes)
&& Objects.equals(mSelectableRoutes, other.mSelectableRoutes)
&& Objects.equals(mDeselectableRoutes, other.mDeselectableRoutes)
- && Objects.equals(mTransferrableRoutes, other.mTransferrableRoutes)
+ && Objects.equals(mTransferableRoutes, other.mTransferableRoutes)
&& (mVolumeHandling == other.mVolumeHandling)
&& (mVolumeMax == other.mVolumeMax)
&& (mVolume == other.mVolume);
@@ -293,7 +293,7 @@
@Override
public int hashCode() {
return Objects.hash(mId, mClientPackageName, mProviderId,
- mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferrableRoutes,
+ mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferableRoutes,
mVolumeMax, mVolumeHandling, mVolume);
}
@@ -311,8 +311,8 @@
.append(", deselectableRoutes={")
.append(String.join(",", mDeselectableRoutes))
.append("}")
- .append(", transferrableRoutes={")
- .append(String.join(",", mTransferrableRoutes))
+ .append(", transferableRoutes={")
+ .append(String.join(",", mTransferableRoutes))
.append("}")
.append(", volumeHandling=").append(getVolumeHandling())
.append(", volumeMax=").append(getVolumeMax())
@@ -350,7 +350,7 @@
final List<String> mSelectedRoutes;
final List<String> mSelectableRoutes;
final List<String> mDeselectableRoutes;
- final List<String> mTransferrableRoutes;
+ final List<String> mTransferableRoutes;
int mVolumeHandling = MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
int mVolumeMax;
int mVolume;
@@ -381,7 +381,7 @@
mSelectedRoutes = new ArrayList<>();
mSelectableRoutes = new ArrayList<>();
mDeselectableRoutes = new ArrayList<>();
- mTransferrableRoutes = new ArrayList<>();
+ mTransferableRoutes = new ArrayList<>();
}
/**
@@ -400,7 +400,7 @@
mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes);
mSelectableRoutes = new ArrayList<>(sessionInfo.mSelectableRoutes);
mDeselectableRoutes = new ArrayList<>(sessionInfo.mDeselectableRoutes);
- mTransferrableRoutes = new ArrayList<>(sessionInfo.mTransferrableRoutes);
+ mTransferableRoutes = new ArrayList<>(sessionInfo.mTransferableRoutes);
mVolumeHandling = sessionInfo.mVolumeHandling;
mVolumeMax = sessionInfo.mVolumeMax;
@@ -524,35 +524,35 @@
}
/**
- * Clears the transferrable routes.
+ * Clears the transferable routes.
*/
@NonNull
- public Builder clearTransferrableRoutes() {
- mTransferrableRoutes.clear();
+ public Builder clearTransferableRoutes() {
+ mTransferableRoutes.clear();
return this;
}
/**
- * Adds a route to the transferrable routes. The {@code routeId} must not be empty.
+ * Adds a route to the transferable routes. The {@code routeId} must not be empty.
*/
@NonNull
- public Builder addTransferrableRoute(@NonNull String routeId) {
+ public Builder addTransferableRoute(@NonNull String routeId) {
if (TextUtils.isEmpty(routeId)) {
throw new IllegalArgumentException("routeId must not be empty");
}
- mTransferrableRoutes.add(routeId);
+ mTransferableRoutes.add(routeId);
return this;
}
/**
- * Removes a route from the transferrable routes. The {@code routeId} must not be empty.
+ * Removes a route from the transferable routes. The {@code routeId} must not be empty.
*/
@NonNull
- public Builder removeTransferrableRoute(@NonNull String routeId) {
+ public Builder removeTransferableRoute(@NonNull String routeId) {
if (TextUtils.isEmpty(routeId)) {
throw new IllegalArgumentException("routeId must not be empty");
}
- mTransferrableRoutes.remove(routeId);
+ mTransferableRoutes.remove(routeId);
return this;
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 9b183a3..44142e3 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -532,6 +532,8 @@
Filter filter = nativeOpenFilter(
mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize);
if (filter != null) {
+ filter.setMainType(mainType);
+ filter.setSubtype(subType);
filter.setCallback(cb);
if (mHandler == null) {
mHandler = createEventHandler();
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 06de6e8..a98183b 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -23,6 +23,7 @@
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.TunerUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -177,6 +178,8 @@
private long mNativeContext;
private FilterCallback mCallback;
private final int mId;
+ private int mMainType;
+ private int mSubtype;
private native int nativeConfigureFilter(
int type, int subType, FilterConfiguration settings);
@@ -197,6 +200,15 @@
}
/** @hide */
+ public void setMainType(@Type int mainType) {
+ mMainType = mainType;
+ }
+ /** @hide */
+ public void setSubtype(@Subtype int subtype) {
+ mSubtype = subtype;
+ }
+
+ /** @hide */
public void setCallback(FilterCallback cb) {
mCallback = cb;
}
@@ -213,10 +225,13 @@
*/
@Result
public int configure(@NonNull FilterConfiguration config) {
- int subType = -1;
+ // TODO: validate main type, subtype, config, settings
+ int subType;
Settings s = config.getSettings();
if (s != null) {
subType = s.getType();
+ } else {
+ subType = TunerUtils.getFilterSubtype(mMainType, mSubtype);
}
return nativeConfigureFilter(config.getType(), subType, config);
}
diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
index bf5aaed..a8dbfa5 100644
--- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
@@ -160,6 +160,12 @@
*/
@NonNull
public IpFilterConfiguration build() {
+ int ipAddrLength = mSrcIpAddress.length;
+ if (ipAddrLength != mDstIpAddress.length || (ipAddrLength != 4 && ipAddrLength != 16)) {
+ throw new IllegalArgumentException(
+ "The lengths of src and dst IP address must be 4 or 16 and must be the same."
+ + "srcLength=" + ipAddrLength + ", dstLength=" + mDstIpAddress.length);
+ }
return new IpFilterConfiguration(
mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort, mDstPort, mPassthrough);
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 08c3f98..ac59003 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -27,16 +27,36 @@
#pragma GCC diagnostic ignored "-Wunused-function"
using ::android::hardware::Void;
+using ::android::hardware::hidl_bitfield;
using ::android::hardware::hidl_vec;
using ::android::hardware::tv::tuner::V1_0::DataFormat;
+using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxAlpLengthType;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionBits;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpAddress;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
using ::android::hardware::tv::tuner::V1_0::DemuxMmtpPid;
using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
+using ::android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType;
+using ::android::hardware::tv::tuner::V1_0::DemuxScHevcIndex;
+using ::android::hardware::tv::tuner::V1_0::DemuxScIndex;
+using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterType;
using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsIndex;
using ::android::hardware::tv::tuner::V1_0::DvrSettings;
using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSettings;
using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
@@ -111,6 +131,9 @@
static fields_t gFields;
+static int IP_V4_LENGTH = 4;
+static int IP_V6_LENGTH = 16;
+
namespace android {
/////////////// LnbCallback ///////////////////////
LnbCallback::LnbCallback(jweak tunerObj, LnbId id) : mObject(tunerObj), mId(id) {}
@@ -1367,38 +1390,352 @@
return NULL;
}
-static DemuxFilterSettings getFilterSettings(
- JNIEnv *env, int type, int subtype, jobject filterSettingsObj) {
+static DemuxFilterSectionBits getFilterSectionBits(JNIEnv *env, const jobject& settings) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithSectionBits");
+ jbyteArray jfilterBytes = static_cast<jbyteArray>(
+ env->GetObjectField(settings, env->GetFieldID(clazz, "mFilter", "[B")));
+ jsize size = env->GetArrayLength(jfilterBytes);
+ std::vector<uint8_t> filterBytes(size);
+ env->GetByteArrayRegion(
+ jfilterBytes, 0, size, reinterpret_cast<jbyte*>(&filterBytes[0]));
+
+ jbyteArray jmask = static_cast<jbyteArray>(
+ env->GetObjectField(settings, env->GetFieldID(clazz, "mMask", "[B")));
+ size = env->GetArrayLength(jmask);
+ std::vector<uint8_t> mask(size);
+ env->GetByteArrayRegion(jmask, 0, size, reinterpret_cast<jbyte*>(&mask[0]));
+
+ jbyteArray jmode = static_cast<jbyteArray>(
+ env->GetObjectField(settings, env->GetFieldID(clazz, "mMode", "[B")));
+ size = env->GetArrayLength(jmode);
+ std::vector<uint8_t> mode(size);
+ env->GetByteArrayRegion(jmode, 0, size, reinterpret_cast<jbyte*>(&mode[0]));
+
+ DemuxFilterSectionBits filterSectionBits {
+ .filter = filterBytes,
+ .mask = mask,
+ .mode = mode,
+ };
+ return filterSectionBits;
+}
+
+static DemuxFilterSectionSettings::Condition::TableInfo getFilterTableInfo(
+ JNIEnv *env, const jobject& settings) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo");
+ uint16_t tableId = static_cast<uint16_t>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mTableId", "I")));
+ uint16_t version = static_cast<uint16_t>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mVersion", "I")));
+ DemuxFilterSectionSettings::Condition::TableInfo tableInfo {
+ .tableId = tableId,
+ .version = version,
+ };
+ return tableInfo;
+}
+
+static DemuxFilterSectionSettings getFilterSectionSettings(JNIEnv *env, const jobject& settings) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettings");
+ bool isCheckCrc = static_cast<bool>(
+ env->GetBooleanField(settings, env->GetFieldID(clazz, "mCrcEnabled", "Z")));
+ bool isRepeat = static_cast<bool>(
+ env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRepeat", "Z")));
+ bool isRaw = static_cast<bool>(
+ env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z")));
+
+ DemuxFilterSectionSettings filterSectionSettings {
+ .isCheckCrc = isCheckCrc,
+ .isRepeat = isRepeat,
+ .isRaw = isRaw,
+ };
+ if (env->IsInstanceOf(
+ settings,
+ env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithSectionBits"))) {
+ filterSectionSettings.condition.sectionBits(getFilterSectionBits(env, settings));
+ } else if (env->IsInstanceOf(
+ settings,
+ env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo"))) {
+ filterSectionSettings.condition.tableInfo(getFilterTableInfo(env, settings));
+ }
+ return filterSectionSettings;
+}
+
+static DemuxFilterAvSettings getFilterAvSettings(JNIEnv *env, const jobject& settings) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/AvSettings");
+ bool isPassthrough = static_cast<bool>(
+ env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsPassthrough", "Z")));
+ DemuxFilterAvSettings filterAvSettings {
+ .isPassthrough = isPassthrough,
+ };
+ return filterAvSettings;
+}
+
+static DemuxFilterPesDataSettings getFilterPesDataSettings(JNIEnv *env, const jobject& settings) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/PesSettings");
+ uint16_t streamId = static_cast<uint16_t>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I")));
+ bool isRaw = static_cast<bool>(
+ env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z")));
+ DemuxFilterPesDataSettings filterPesDataSettings {
+ .streamId = streamId,
+ .isRaw = isRaw,
+ };
+ return filterPesDataSettings;
+}
+
+static DemuxFilterRecordSettings getFilterRecordSettings(JNIEnv *env, const jobject& settings) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/RecordSettings");
+ hidl_bitfield<DemuxTsIndex> tsIndexMask = static_cast<hidl_bitfield<DemuxTsIndex>>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mTsIndexMask", "I")));
+ DemuxRecordScIndexType scIndexType = static_cast<DemuxRecordScIndexType>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexType", "I")));
+ jint scIndexMask = env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexMask", "I"));
+
+ DemuxFilterRecordSettings filterRecordSettings {
+ .tsIndexMask = tsIndexMask,
+ .scIndexType = scIndexType,
+ };
+ if (scIndexType == DemuxRecordScIndexType::SC) {
+ filterRecordSettings.scIndexMask.sc(static_cast<hidl_bitfield<DemuxScIndex>>(scIndexMask));
+ } else if (scIndexType == DemuxRecordScIndexType::SC_HEVC) {
+ filterRecordSettings.scIndexMask.scHevc(
+ static_cast<hidl_bitfield<DemuxScHevcIndex>>(scIndexMask));
+ }
+ return filterRecordSettings;
+}
+
+static DemuxFilterDownloadSettings getFilterDownloadSettings(JNIEnv *env, const jobject& settings) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/DownloadSettings");
+ uint32_t downloadId = static_cast<uint32_t>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mDownloadId", "I")));
+
+ DemuxFilterDownloadSettings filterDownloadSettings {
+ .downloadId = downloadId,
+ };
+ return filterDownloadSettings;
+}
+
+static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/IpFilterConfiguration");
+
+ jbyteArray jsrcIpAddress = static_cast<jbyteArray>(
+ env->GetObjectField(config, env->GetFieldID(clazz, "mSrcIpAddress", "[B")));
+ jsize srcSize = env->GetArrayLength(jsrcIpAddress);
+ jbyteArray jdstIpAddress = static_cast<jbyteArray>(
+ env->GetObjectField(config, env->GetFieldID(clazz, "mDstIpAddress", "[B")));
+ jsize dstSize = env->GetArrayLength(jdstIpAddress);
+
+ DemuxIpAddress res;
+
+ if (srcSize != dstSize) {
+ // should never happen. Validated on Java size.
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "IP address lengths don't match. srcLength=%d, dstLength=%d", srcSize, dstSize);
+ return res;
+ }
+
+ if (srcSize == IP_V4_LENGTH) {
+ uint8_t srcAddr[IP_V4_LENGTH];
+ uint8_t dstAddr[IP_V4_LENGTH];
+ env->GetByteArrayRegion(
+ jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte*>(srcAddr));
+ env->GetByteArrayRegion(
+ jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte*>(dstAddr));
+ res.srcIpAddress.v4(srcAddr);
+ res.dstIpAddress.v4(dstAddr);
+ } else if (srcSize == IP_V6_LENGTH) {
+ uint8_t srcAddr[IP_V6_LENGTH];
+ uint8_t dstAddr[IP_V6_LENGTH];
+ env->GetByteArrayRegion(
+ jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte*>(srcAddr));
+ env->GetByteArrayRegion(
+ jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte*>(dstAddr));
+ res.srcIpAddress.v6(srcAddr);
+ res.dstIpAddress.v6(dstAddr);
+ } else {
+ // should never happen. Validated on Java size.
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Invalid IP address length %d", srcSize);
+ return res;
+ }
+
+ uint16_t srcPort = static_cast<uint16_t>(
+ env->GetIntField(config, env->GetFieldID(clazz, "mSrcPort", "I")));
+ uint16_t dstPort = static_cast<uint16_t>(
+ env->GetIntField(config, env->GetFieldID(clazz, "mDstPort", "I")));
+
+ res.srcPort = srcPort;
+ res.dstPort = dstPort;
+
+ return res;
+}
+
+static DemuxFilterSettings getFilterConfiguration(
+ JNIEnv *env, int type, int subtype, jobject filterConfigObj) {
DemuxFilterSettings filterSettings;
- // TODO: more setting types
jobject settingsObj =
env->GetObjectField(
- filterSettingsObj,
+ filterConfigObj,
env->GetFieldID(
env->FindClass("android/media/tv/tuner/filter/FilterConfiguration"),
"mSettings",
"Landroid/media/tv/tuner/filter/Settings;"));
- if (type == (int)DemuxFilterMainType::TS) {
- // DemuxTsFilterSettings
- jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration");
- int tpid = env->GetIntField(filterSettingsObj, env->GetFieldID(clazz, "mTpid", "I"));
- if (subtype == (int)DemuxTsFilterType::PES) {
- // DemuxFilterPesDataSettings
- jclass settingClazz =
- env->FindClass("android/media/tv/tuner/filter/PesSettings");
- int streamId = env->GetIntField(
- settingsObj, env->GetFieldID(settingClazz, "mStreamId", "I"));
- bool isRaw = (bool)env->GetBooleanField(
- settingsObj, env->GetFieldID(settingClazz, "mIsRaw", "Z"));
- DemuxFilterPesDataSettings filterPesDataSettings {
- .streamId = static_cast<uint16_t>(streamId),
- .isRaw = isRaw,
- };
+ DemuxFilterMainType mainType = static_cast<DemuxFilterMainType>(type);
+ switch (mainType) {
+ case DemuxFilterMainType::TS: {
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration");
+ uint16_t tpid = static_cast<uint16_t>(
+ env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mTpid", "I")));
DemuxTsFilterSettings tsFilterSettings {
- .tpid = static_cast<uint16_t>(tpid),
+ .tpid = tpid,
};
- tsFilterSettings.filterSettings.pesData(filterPesDataSettings);
+
+ DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype);
+ switch (tsType) {
+ case DemuxTsFilterType::SECTION:
+ tsFilterSettings.filterSettings.section(
+ getFilterSectionSettings(env, settingsObj));
+ break;
+ case DemuxTsFilterType::AUDIO:
+ case DemuxTsFilterType::VIDEO:
+ tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
+ break;
+ case DemuxTsFilterType::PES:
+ tsFilterSettings.filterSettings.pesData(
+ getFilterPesDataSettings(env, settingsObj));
+ break;
+ case DemuxTsFilterType::RECORD:
+ tsFilterSettings.filterSettings.record(
+ getFilterRecordSettings(env, settingsObj));
+ break;
+ default:
+ break;
+ }
filterSettings.ts(tsFilterSettings);
+ break;
+ }
+ case DemuxFilterMainType::MMTP: {
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/MmtpFilterConfiguration");
+ uint16_t mmtpPid = static_cast<uint16_t>(
+ env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mMmtpPid", "I")));
+ DemuxMmtpFilterSettings mmtpFilterSettings {
+ .mmtpPid = mmtpPid,
+ };
+ DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype);
+ switch (mmtpType) {
+ case DemuxMmtpFilterType::SECTION:
+ mmtpFilterSettings.filterSettings.section(
+ getFilterSectionSettings(env, settingsObj));
+ break;
+ case DemuxMmtpFilterType::AUDIO:
+ case DemuxMmtpFilterType::VIDEO:
+ mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
+ break;
+ case DemuxMmtpFilterType::PES:
+ mmtpFilterSettings.filterSettings.pesData(
+ getFilterPesDataSettings(env, settingsObj));
+ break;
+ case DemuxMmtpFilterType::RECORD:
+ mmtpFilterSettings.filterSettings.record(
+ getFilterRecordSettings(env, settingsObj));
+ break;
+ case DemuxMmtpFilterType::DOWNLOAD:
+ mmtpFilterSettings.filterSettings.download(
+ getFilterDownloadSettings(env, settingsObj));
+ break;
+ default:
+ break;
+ }
+ filterSettings.mmtp(mmtpFilterSettings);
+ break;
+ }
+ case DemuxFilterMainType::IP: {
+ DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj);
+
+ DemuxIpFilterSettings ipFilterSettings {
+ .ipAddr = ipAddr,
+ };
+ DemuxIpFilterType ipType = static_cast<DemuxIpFilterType>(subtype);
+ switch (ipType) {
+ case DemuxIpFilterType::SECTION: {
+ ipFilterSettings.filterSettings.section(
+ getFilterSectionSettings(env, settingsObj));
+ break;
+ }
+ case DemuxIpFilterType::IP: {
+ jclass clazz = env->FindClass(
+ "android/media/tv/tuner/filter/IpFilterConfiguration");
+ bool bPassthrough = static_cast<bool>(
+ env->GetBooleanField(
+ filterConfigObj, env->GetFieldID(
+ clazz, "mPassthrough", "Z")));
+ ipFilterSettings.filterSettings.bPassthrough(bPassthrough);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ filterSettings.ip(ipFilterSettings);
+ break;
+ }
+ case DemuxFilterMainType::TLV: {
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/TlvFilterConfiguration");
+ uint8_t packetType = static_cast<uint8_t>(
+ env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I")));
+ bool isCompressedIpPacket = static_cast<bool>(
+ env->GetBooleanField(
+ filterConfigObj, env->GetFieldID(clazz, "mIsCompressedIpPacket", "Z")));
+
+ DemuxTlvFilterSettings tlvFilterSettings {
+ .packetType = packetType,
+ .isCompressedIpPacket = isCompressedIpPacket,
+ };
+ DemuxTlvFilterType tlvType = static_cast<DemuxTlvFilterType>(subtype);
+ switch (tlvType) {
+ case DemuxTlvFilterType::SECTION: {
+ tlvFilterSettings.filterSettings.section(
+ getFilterSectionSettings(env, settingsObj));
+ break;
+ }
+ case DemuxTlvFilterType::TLV: {
+ bool bPassthrough = static_cast<bool>(
+ env->GetBooleanField(
+ filterConfigObj, env->GetFieldID(
+ clazz, "mPassthrough", "Z")));
+ tlvFilterSettings.filterSettings.bPassthrough(bPassthrough);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ filterSettings.tlv(tlvFilterSettings);
+ break;
+ }
+ case DemuxFilterMainType::ALP: {
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/AlpFilterConfiguration");
+ uint8_t packetType = static_cast<uint8_t>(
+ env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I")));
+ DemuxAlpLengthType lengthType = static_cast<DemuxAlpLengthType>(
+ env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mLengthType", "I")));
+ DemuxAlpFilterSettings alpFilterSettings {
+ .packetType = packetType,
+ .lengthType = lengthType,
+ };
+ DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype);
+ switch (alpType) {
+ case DemuxAlpFilterType::SECTION:
+ alpFilterSettings.filterSettings.section(
+ getFilterSectionSettings(env, settingsObj));
+ break;
+ default:
+ break;
+ }
+ filterSettings.alp(alpFilterSettings);
+ break;
+ }
+ default: {
+ break;
}
}
return filterSettings;
@@ -1439,7 +1776,7 @@
ALOGD("Failed to configure filter: filter not found");
return (int)Result::INVALID_STATE;
}
- DemuxFilterSettings filterSettings = getFilterSettings(env, type, subtype, settings);
+ DemuxFilterSettings filterSettings = getFilterConfiguration(env, type, subtype, settings);
Result res = iFilterSp->configure(filterSettings);
MQDescriptorSync<uint8_t> filterMQDesc;
if (res == Result::SUCCESS && filterSp->mFilterMQ == NULL) {
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
similarity index 99%
rename from media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
rename to media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 3ffb951..615dc48 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -70,8 +70,8 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class MediaRouterManagerTest {
- private static final String TAG = "MediaRouterManagerTest";
+public class MediaRouter2ManagerTest {
+ private static final String TAG = "MediaRouter2ManagerTest";
private static final int TIMEOUT_MS = 5000;
private Context mContext;
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
index 267927f..f1dcf3d 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
@@ -191,7 +191,7 @@
RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(sessionId, packageName)
.addSelectedRoute(routeId)
.addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
- .addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
+ .addTransferableRoute(ROUTE_ID5_TO_TRANSFER_TO)
.setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
.setVolumeMax(SESSION_VOLUME_MAX)
.setVolume(SESSION_VOLUME_INITIAL)
@@ -300,7 +300,7 @@
.clearSelectedRoutes()
.addSelectedRoute(routeId)
.removeDeselectableRoute(routeId)
- .removeTransferrableRoute(routeId)
+ .removeTransferableRoute(routeId)
.build();
notifySessionUpdated(newSessionInfo);
publishRoutes();
diff --git a/media/tests/TunerTest/Android.bp b/media/tests/TunerTest/Android.bp
new file mode 100644
index 0000000..cef8791
--- /dev/null
+++ b/media/tests/TunerTest/Android.bp
@@ -0,0 +1,18 @@
+android_test {
+ name: "mediatunertest",
+
+ srcs: ["**/*.java"],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ static_libs: [
+ "android-support-test",
+ "testng"
+ ],
+
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/media/tests/TunerTest/AndroidManifest.xml b/media/tests/TunerTest/AndroidManifest.xml
new file mode 100644
index 0000000..17e9f19
--- /dev/null
+++ b/media/tests/TunerTest/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.mediatunertest">
+
+ <uses-permission android:name="android.permission.ACCESS_TV_TUNER" />
+
+ <application android:label="@string/app_name">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.mediatunertest"
+ android:label="Media Tuner Tests"/>
+</manifest>
diff --git a/media/tests/TunerTest/AndroidTest.xml b/media/tests/TunerTest/AndroidTest.xml
new file mode 100644
index 0000000..d9c31f45
--- /dev/null
+++ b/media/tests/TunerTest/AndroidTest.xml
@@ -0,0 +1,17 @@
+<configuration description="Runs Media Tuner tests.">
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="MediaTunerTest"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="mediatunertest.apk"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.mediatunertest"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+ </test>
+</configuration>
diff --git a/media/tests/TunerTest/res/values/strings.xml b/media/tests/TunerTest/res/values/strings.xml
new file mode 100644
index 0000000..b313944
--- /dev/null
+++ b/media/tests/TunerTest/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- name of the app [CHAR LIMIT=25]-->
+ <string name="app_name">MediaTunerTest</string>
+</resources>
\ No newline at end of file
diff --git a/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java b/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java
new file mode 100644
index 0000000..cbfbf77
--- /dev/null
+++ b/media/tests/TunerTest/src/com/android/mediatunertest/TunerTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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 com.android.mediatunertest;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.media.tv.tuner.Descrambler;
+import android.media.tv.tuner.Tuner;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TunerTest {
+ private static final String TAG = "MediaTunerTest";
+
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test
+ public void testTunerConstructor() throws Exception {
+ Tuner tuner = new Tuner(mContext, "123", 1, null);
+ assertNotNull(tuner);
+ }
+
+ @Test
+ public void testOpenDescrambler() throws Exception {
+ Tuner tuner = new Tuner(mContext, "123", 1, null);
+ Descrambler descrambler = tuner.openDescrambler();
+ assertNotNull(descrambler);
+ }
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 6212493..3f42ad4 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -27,6 +27,7 @@
"SettingsLibRadioButtonPreference",
"WifiTrackerLib",
"SettingsLibDisplayDensityUtils",
+ "SettingsLibSchedulesProvider",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
index e6f8c01..dc6dddb 100644
--- a/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
+++ b/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
@@ -18,75 +18,39 @@
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/entity_header"
- style="@style/EntityHeader"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:orientation="horizontal">
+ style="@style/EntityHeader">
<LinearLayout
android:id="@+id/entity_header_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:paddingTop="32dp"
+ android:paddingBottom="32dp"
android:layout_centerHorizontal="true"
- android:gravity="center_horizontal"
- android:orientation="horizontal">
+ android:orientation="vertical">
<LinearLayout
android:id="@+id/entity_header_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:gravity="center_horizontal"
- android:orientation="vertical">
+ android:layout_gravity="center"
+ android:orientation="horizontal">
<ImageView
android:id="@+id/entity_header_icon_personal"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:scaleType="fitCenter"
- android:antialias="true"/>
+ style="@style/CrossProfileEntityHeaderIcon" />
- <TextView
- android:id="@+id/install_type"
- style="@style/TextAppearance.EntityHeaderSummary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="2dp"
- android:text="Personal"/>
- </LinearLayout>
-
- <ImageView
- android:id="@+id/entity_header_swap_horiz"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:scaleType="fitCenter"
- android:antialias="true"
- android:src="@drawable/ic_swap_horiz_grey"/>
-
- <LinearLayout
- android:id="@+id/entity_header_content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:gravity="center_horizontal"
- android:orientation="vertical">
+ <ImageView
+ android:id="@+id/entity_header_swap_horiz"
+ style="@style/CrossProfileSwapHorizIcon "/>
<ImageView
android:id="@+id/entity_header_icon_work"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:scaleType="fitCenter"
- android:antialias="true"/>
- <TextView
- android:id="@+id/install_type"
- style="@style/TextAppearance.EntityHeaderSummary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="2dp"
- android:text="Work"/>
+ style="@style/CrossProfileEntityHeaderIcon" />
</LinearLayout>
- </LinearLayout>
+ <TextView
+ android:id="@+id/entity_header_title"
+ style="@style/CrossProfileEntityHeaderTitle" />
+ </LinearLayout>
</RelativeLayout>
diff --git a/packages/SettingsLib/LayoutPreference/res/values/styles.xml b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
index 6a2b729..4a99e84 100644
--- a/packages/SettingsLib/LayoutPreference/res/values/styles.xml
+++ b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
@@ -37,4 +37,35 @@
<item name="android:singleLine">true</item>
<item name="android:ellipsize">marquee</item>
</style>
+
+ <style name="CrossProfileEntityHeaderIcon">
+ <item name="android:layout_width">48dp</item>
+ <item name="android:layout_height">48dp</item>
+ <item name="android:layout_gravity">center</item>
+ <item name="android:gravity">center</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:antialias">true</item>
+ </style>
+
+ <style name="CrossProfileSwapHorizIcon">
+ <item name="android:layout_width">24dp</item>
+ <item name="android:layout_height">24dp</item>
+ <item name="android:layout_gravity">center</item>
+ <item name="android:gravity">center</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:layout_marginStart">10dp</item>
+ <item name="android:layout_marginEnd">10dp</item>
+ <item name="android:src">@drawable/ic_swap_horiz_grey</item>
+ <item name="android:antialias">true</item>
+ </style>
+
+ <style name="CrossProfileEntityHeaderTitle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_gravity">center</item>
+ <item name="android:textSize">18sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:fontFamily">google-sans-medium</item>
+ <item name="android:layout_marginTop">8dp</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SchedulesProvider/Android.bp b/packages/SettingsLib/SchedulesProvider/Android.bp
new file mode 100644
index 0000000..ef59252
--- /dev/null
+++ b/packages/SettingsLib/SchedulesProvider/Android.bp
@@ -0,0 +1,12 @@
+android_library {
+ name: "SettingsLibSchedulesProvider",
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/SchedulesProvider/AndroidManifest.xml b/packages/SettingsLib/SchedulesProvider/AndroidManifest.xml
new file mode 100644
index 0000000..1b0e4bf
--- /dev/null
+++ b/packages/SettingsLib/SchedulesProvider/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.schedulesprovider">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java
new file mode 100644
index 0000000..7d2b8e2
--- /dev/null
+++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java
@@ -0,0 +1,167 @@
+/*
+ * 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 android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+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).
+ */
+public class ScheduleInfo implements Parcelable {
+ private static final String TAG = "ScheduleInfo";
+ private final String mTitle;
+ private final String mSummary;
+ private final Intent mIntent;
+
+ public ScheduleInfo(Builder builder) {
+ mTitle = builder.mTitle;
+ mSummary = builder.mSummary;
+ mIntent = builder.mIntent;
+ }
+
+ protected ScheduleInfo(Parcel in) {
+ mTitle = in.readString();
+ mSummary = in.readString();
+ mIntent = in.readParcelable(Intent.class.getClassLoader());
+ }
+
+ /**
+ * Returns the title text.
+ *
+ * @return The title.
+ */
+ public String getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Returns the summary text.
+ *
+ * @return The summary.
+ */
+ public String getSummary() {
+ return mSummary;
+ }
+
+ /**
+ * Returns an {@link Intent}.
+ */
+ public Intent getIntent() {
+ return mIntent;
+ }
+
+ /**
+ * Verify the member variables are valid.
+ *
+ * @return {@code true} if all member variables are valid.
+ */
+ public boolean isValid() {
+ return !TextUtils.isEmpty(mTitle) && !TextUtils.isEmpty(mSummary) && (mIntent != null);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mTitle);
+ dest.writeString(mSummary);
+ dest.writeParcelable(mIntent, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<ScheduleInfo> CREATOR = new Creator<ScheduleInfo>() {
+ @Override
+ public ScheduleInfo createFromParcel(Parcel in) {
+ return new ScheduleInfo(in);
+ }
+
+ @Override
+ public ScheduleInfo[] newArray(int size) {
+ return new ScheduleInfo[size];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "title : " + mTitle + " summary : " + mSummary + (mIntent == null
+ ? " and intent is null." : ".");
+ }
+
+ /**
+ * A simple builder for {@link ScheduleInfo}.
+ */
+ public static class Builder {
+ @NonNull
+ private String mTitle;
+ @NonNull
+ private String mSummary;
+ @NonNull
+ private Intent mIntent;
+
+ /**
+ * Sets the title.
+ *
+ * @param title The title of the preference item.
+ * @return This instance.
+ */
+ public Builder setTitle(@NonNull String title) {
+ mTitle = title;
+ return this;
+ }
+
+ /**
+ * Sets the summary.
+ *
+ * @param summary The summary of the preference summary.
+ * @return This instance.
+ */
+ public Builder setSummary(@NonNull String summary) {
+ mSummary = summary;
+ return this;
+ }
+
+ /**
+ * Sets the {@link Intent}.
+ *
+ * @param intent The action when user clicks the preference.
+ * @return This instance.
+ */
+ public Builder setIntent(@NonNull Intent intent) {
+ mIntent = intent;
+ return this;
+ }
+
+ /**
+ * Creates an instance of {@link ScheduleInfo}.
+ *
+ * @return The instance of {@link ScheduleInfo}.
+ */
+ public ScheduleInfo build() {
+ return new ScheduleInfo(this);
+ }
+ }
+}
diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java
new file mode 100644
index 0000000..a423e47
--- /dev/null
+++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.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.settingslib.schedulesprovider;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.SystemProperties;
+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}.
+ */
+public abstract class SchedulesProvider extends ContentProvider {
+ public static final String METHOD_GENERATE_SCHEDULE_INFO_LIST = "generateScheduleInfoList";
+ public static final String BUNDLE_SCHEDULE_INFO_LIST = "scheduleInfoList";
+ private static final String TAG = "SchedulesProvider";
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public final Cursor query(
+ Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException("Query operation is not supported currently.");
+ }
+
+ @Override
+ public final String getType(Uri uri) {
+ throw new UnsupportedOperationException("GetType operation is not supported currently.");
+ }
+
+ @Override
+ public final Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException("Insert operation is not supported currently.");
+ }
+
+ @Override
+ public final int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("Delete operation not supported currently.");
+ }
+
+ @Override
+ public final int update(Uri uri, ContentValues values, String selection,
+ String[] selectionArgs) {
+ throw new UnsupportedOperationException("Update operation is not supported currently.");
+ }
+
+ /**
+ * Return the list of the schedule information.
+ *
+ * @return a list of the {@link ScheduleInfo}.
+ */
+ public abstract ArrayList<ScheduleInfo> getScheduleInfoList();
+
+ /**
+ * Returns a bundle which contains a list of {@link ScheduleInfo} and data types:
+ * scheduleInfoList : ArrayList<ScheduleInfo>
+ */
+ @Override
+ public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
+ final Bundle bundle = new Bundle();
+ if (METHOD_GENERATE_SCHEDULE_INFO_LIST.equals(method)) {
+ final ArrayList<ScheduleInfo> scheduleInfoList = filterInvalidData(
+ getScheduleInfoList());
+ if (scheduleInfoList != null) {
+ bundle.putParcelableArrayList(BUNDLE_SCHEDULE_INFO_LIST, scheduleInfoList);
+ }
+ }
+ return bundle;
+ }
+
+ /**
+ * To filter the invalid schedule info.
+ *
+ * @param scheduleInfoList The list of the {@link ScheduleInfo}.
+ * @return The valid list of the {@link ScheduleInfo}.
+ */
+ private ArrayList<ScheduleInfo> filterInvalidData(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();
+ }
+ final List<ScheduleInfo> filteredList = scheduleInfoList
+ .stream()
+ .filter(scheduleInfo -> scheduleInfo.isValid())
+ .collect(Collectors.toList());
+
+ return new ArrayList<>(filteredList);
+ }
+
+ private void dumpInvalidData(ArrayList<ScheduleInfo> scheduleInfoList) {
+ Log.d(TAG, "package : " + getContext().getPackageName()
+ + " provided some scheduling data are invalid.");
+ scheduleInfoList
+ .stream()
+ .filter(scheduleInfo -> !scheduleInfo.isValid())
+ .forEach(scheduleInfo -> Log.d(TAG, scheduleInfo.toString()));
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index e910967..8e4a982 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -74,13 +74,6 @@
refreshDevices();
}
- @VisibleForTesting
- String getControlCategoryByPackageName(String packageName) {
- //TODO(b/117129183): Use package name to get ControlCategory.
- //Since api not ready, return fixed ControlCategory for prototype.
- return "com.google.android.gms.cast.CATEGORY_CAST";
- }
-
@Override
public void stopScan() {
mRouterManager.unregisterCallback(mMediaRouterCallback);
@@ -192,5 +185,10 @@
public void onRoutesChanged(List<MediaRoute2Info> routes) {
refreshDevices();
}
+
+ @Override
+ public void onRoutesRemoved(List<MediaRoute2Info> routes) {
+ refreshDevices();
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 984ab11..f8db70a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -16,6 +16,8 @@
package com.android.settingslib.media;
import android.app.Notification;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.text.TextUtils;
@@ -26,13 +28,13 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -66,9 +68,16 @@
@VisibleForTesting
List<MediaDevice> mMediaDevices = new ArrayList<>();
@VisibleForTesting
+ List<MediaDevice> mDisconnectedMediaDevices = new ArrayList<>();
+ @VisibleForTesting
MediaDevice mPhoneDevice;
@VisibleForTesting
MediaDevice mCurrentConnectedDevice;
+ @VisibleForTesting
+ DeviceAttributeChangeCallback mDeviceAttributeChangeCallback =
+ new DeviceAttributeChangeCallback();
+ @VisibleForTesting
+ BluetoothAdapter mBluetoothAdapter;
/**
* Register to start receiving callbacks for MediaDevice events.
@@ -89,6 +98,7 @@
mPackageName = packageName;
mLocalBluetoothManager =
LocalBluetoothManager.getInstance(context, /* onInitCallback= */ null);
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mLocalBluetoothManager == null) {
Log.e(TAG, "Bluetooth is not supported on this device");
return;
@@ -164,7 +174,7 @@
}
void dispatchDeviceListUpdate() {
- Collections.sort(mMediaDevices, COMPARATOR);
+ //TODO(b/149260820): Use new rule to rank device once device type api is ready.
for (DeviceCallback callback : getCallbacks()) {
callback.onDeviceListUpdate(new ArrayList<>(mMediaDevices));
}
@@ -278,12 +288,44 @@
public void onDeviceListAdded(List<MediaDevice> devices) {
mMediaDevices.clear();
mMediaDevices.addAll(devices);
+ mMediaDevices.addAll(buildDisconnectedBluetoothDevice());
+
final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice();
mCurrentConnectedDevice = infoMediaDevice != null
? infoMediaDevice : updateCurrentConnectedDevice();
dispatchDeviceListUpdate();
}
+ private List<MediaDevice> buildDisconnectedBluetoothDevice() {
+ for (MediaDevice device : mDisconnectedMediaDevices) {
+ ((BluetoothMediaDevice) device).getCachedDevice()
+ .unregisterCallback(mDeviceAttributeChangeCallback);
+ }
+ mDisconnectedMediaDevices.clear();
+ final List<BluetoothDevice> bluetoothDevices =
+ mBluetoothAdapter.getMostRecentlyConnectedDevices();
+ final CachedBluetoothDeviceManager cachedDeviceManager =
+ mLocalBluetoothManager.getCachedDeviceManager();
+
+ for (BluetoothDevice device : bluetoothDevices) {
+ final CachedBluetoothDevice cachedDevice =
+ cachedDeviceManager.findDevice(device);
+ if (cachedDevice != null) {
+ if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
+ && !cachedDevice.isConnected()) {
+ final MediaDevice mediaDevice = new BluetoothMediaDevice(mContext,
+ cachedDevice,
+ null, null, mPackageName);
+ if (!mMediaDevices.contains(mediaDevice)) {
+ cachedDevice.registerCallback(mDeviceAttributeChangeCallback);
+ mDisconnectedMediaDevices.add(mediaDevice);
+ }
+ }
+ }
+ }
+ return new ArrayList<>(mDisconnectedMediaDevices);
+ }
+
@Override
public void onDeviceRemoved(MediaDevice device) {
if (mMediaDevices.contains(device)) {
@@ -345,4 +387,17 @@
*/
default void onDeviceAttributesChanged() {};
}
+
+ /**
+ * This callback is for update {@link BluetoothMediaDevice} summary when
+ * {@link CachedBluetoothDevice} connection state is changed.
+ */
+ @VisibleForTesting
+ class DeviceAttributeChangeCallback implements CachedBluetoothDevice.Callback {
+
+ @Override
+ public void onDeviceAttributesChanged() {
+ dispatchDeviceAttributesChanged();
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 954eb9b..b47aa98 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1366,7 +1366,7 @@
mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
} else {
mConfig.allowedKeyManagement.set(KeyMgmt.OWE);
- mConfig.requirePMF = true;
+ mConfig.requirePmf = true;
}
}
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 05f5fa0..8b815bf 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
@@ -203,4 +203,46 @@
assertThat(mInfoMediaManager.connectDeviceWithoutPackageName(device)).isFalse();
}
+
+ @Test
+ public void onRoutesRemoved_getAvailableRoutes_shouldAddMediaDevice() {
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
+ when(info.getId()).thenReturn(TEST_ID);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ final List<MediaRoute2Info> routes = new ArrayList<>();
+ routes.add(info);
+ when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn(routes);
+
+ final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+ assertThat(mediaDevice).isNull();
+
+ mInfoMediaManager.mMediaRouterCallback.onRoutesRemoved(routes);
+
+ final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+ assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+ assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+ }
+
+ @Test
+ public void onRoutesRemoved_buildAllRoutes_shouldAddMediaDevice() {
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
+ when(info.getId()).thenReturn(TEST_ID);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ final List<MediaRoute2Info> routes = new ArrayList<>();
+ routes.add(info);
+ when(mRouterManager.getAllRoutes()).thenReturn(routes);
+
+ final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+ assertThat(mediaDevice).isNull();
+
+ mInfoMediaManager.mPackageName = "";
+ mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes);
+
+ final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+ assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 3d67ba0..3611dfe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -25,13 +25,17 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.content.Context;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
@@ -40,11 +44,14 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalMediaManagerTest {
private static final String TEST_DEVICE_ID_1 = "device_id_1";
@@ -69,11 +76,15 @@
private Context mContext;
private LocalMediaManager mLocalMediaManager;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
+ final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
@@ -81,6 +92,7 @@
mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
mInfoMediaManager, "com.test.packagename");
+ mLocalMediaManager.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
@Test
@@ -419,4 +431,50 @@
MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
assertThat(activeDevices).containsExactly(device3);
}
+
+ @Test
+ public void onDeviceAttributesChanged_shouldBeCalled() {
+ mLocalMediaManager.registerCallback(mCallback);
+
+ mLocalMediaManager.mDeviceAttributeChangeCallback.onDeviceAttributesChanged();
+
+ verify(mCallback).onDeviceAttributesChanged();
+ }
+
+ @Test
+ public void onDeviceListAdded_haveDisconnectedDevice_addDisconnectedDevice() {
+ final List<MediaDevice> devices = new ArrayList<>();
+ final MediaDevice device1 = mock(MediaDevice.class);
+ final MediaDevice device2 = mock(MediaDevice.class);
+ final MediaDevice device3 = mock(MediaDevice.class);
+ mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
+ devices.add(device1);
+ devices.add(device2);
+ mLocalMediaManager.mMediaDevices.add(device3);
+ mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
+
+ final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
+ final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
+ final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
+ final CachedBluetoothDeviceManager cachedManager = mock(CachedBluetoothDeviceManager.class);
+ bluetoothDevices.add(bluetoothDevice);
+ mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices);
+
+ when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(cachedManager);
+ when(cachedManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice);
+ when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(cachedDevice.isConnected()).thenReturn(false);
+
+ when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
+ when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
+ when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
+ when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
+
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+ mLocalMediaManager.registerCallback(mCallback);
+ mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
+
+ assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
+ verify(mCallback).onDeviceListUpdate(any());
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
index 906dba4..015ce149 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
@@ -17,6 +17,7 @@
package com.android.settingslib.testutils.shadow;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
@@ -29,6 +30,7 @@
public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBluetoothAdapter {
private List<Integer> mSupportedProfiles;
+ private List<BluetoothDevice> mMostRecentlyConnectedDevices;
private BluetoothProfile.ServiceListener mServiceListener;
@Implementation
@@ -50,4 +52,13 @@
public void setSupportedProfiles(List<Integer> supportedProfiles) {
mSupportedProfiles = supportedProfiles;
}
+
+ @Implementation
+ protected List<BluetoothDevice> getMostRecentlyConnectedDevices() {
+ return mMostRecentlyConnectedDevices;
+ }
+
+ public void setMostRecentlyConnectedDevices(List<BluetoothDevice> list) {
+ mMostRecentlyConnectedDevices = list;
+ }
}
diff --git a/packages/SettingsProvider/res/values/strings.xml b/packages/SettingsProvider/res/values/strings.xml
index 3787727..76bea31 100644
--- a/packages/SettingsProvider/res/values/strings.xml
+++ b/packages/SettingsProvider/res/values/strings.xml
@@ -23,15 +23,10 @@
<!-- A notification is shown when the user's softap config has been changed due to underlying
hardware restrictions. This is the notifications's title.
[CHAR_LIMIT=NONE] -->
- <string name="wifi_softap_config_change">Changes to your hotspot settings</string>
+ <string name="wifi_softap_config_change">Hotspot settings have changed</string>
<!-- A notification is shown when the user's softap config has been changed due to underlying
hardware restrictions. This is the notification's summary message.
[CHAR_LIMIT=NONE] -->
- <string name="wifi_softap_config_change_summary">Your hotspot band has changed.</string>
-
- <!-- A notification is shown when the user's softap config has been changed due to underlying
- hardware restrictions. This is the notification's full message.
- [CHAR_LIMIT=NONE] -->
- <string name="wifi_softap_config_change_detailed">This device doesn\u2019t support your preference for 5GHz only. Instead, this device will use the 5GHz band when available.</string>
+ <string name="wifi_softap_config_change_summary">Tap to see details</string>
</resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 8037266..c5d4fa9 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -126,6 +126,7 @@
VALIDATORS.put(System.SCREEN_OFF_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(System.SCREEN_BRIGHTNESS_FOR_VR, new InclusiveIntegerRangeValidator(0, 255));
VALIDATORS.put(System.SCREEN_BRIGHTNESS_MODE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.MODE_RINGER_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(System.MUTE_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(System.VIBRATE_ON, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java
index 1ee5f90..ca841a5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java
@@ -57,7 +57,6 @@
Resources resources = context.getResources();
CharSequence title = resources.getText(R.string.wifi_softap_config_change);
CharSequence contentSummary = resources.getText(R.string.wifi_softap_config_change_summary);
- CharSequence content = resources.getText(R.string.wifi_softap_config_change_detailed);
int color = resources.getColor(
android.R.color.system_notification_accent_color, context.getTheme());
@@ -73,7 +72,6 @@
.setLocalOnly(true)
.setColor(color)
.setStyle(new Notification.BigTextStyle()
- .bigText(content)
.setBigContentTitle(title)
.setSummaryText(contentSummary))
.setAutoCancel(true)
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index cc2c92b..d821050 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -253,7 +253,8 @@
<!-- Permission required for CTS test - ShortcutManagerUsageTest -->
<uses-permission android:name="android.permission.ACCESS_SHORTCUTS"/>
- <!-- Permission required for CTS test - UsageStatsTest -->
+ <!-- Permissions required for CTS test - UsageStatsTest -->
+ <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"/>
<!-- Permissions required to test ambient display. -->
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 1814593..530823a 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -606,26 +606,21 @@
BugreportInfo info = new BugreportInfo(mContext, baseName, name,
shareTitle, shareDescription, bugreportType, mBugreportsDir);
- ParcelFileDescriptor bugreportFd;
- ParcelFileDescriptor screenshotFd;
-
- try {
- bugreportFd = info.createAndGetBugreportFd();
- if (bugreportFd == null) {
- Log.e(TAG, "Bugreport parcel file descriptor is null.");
- return;
- }
- screenshotFd = info.createAndGetDefaultScreenshotFd();
- if (screenshotFd == null) {
- Log.e(TAG, "Screenshot parcel file descriptor is null. Deleting bugreport file");
- FileUtils.closeQuietly(bugreportFd);
- info.bugreportFile.delete();
- return;
- }
- } catch (IOException e) {
- Log.e(TAG, "Error in generating bugreport files: ", e);
+ ParcelFileDescriptor bugreportFd = info.getBugreportFd();
+ if (bugreportFd == null) {
+ Log.e(TAG, "Failed to start bugreport generation as "
+ + " bugreport parcel file descriptor is null.");
return;
}
+ ParcelFileDescriptor screenshotFd = info.getDefaultScreenshotFd();
+ if (screenshotFd == null) {
+ Log.e(TAG, "Failed to start bugreport generation as"
+ + " screenshot parcel file descriptor is null. Deleting bugreport file");
+ FileUtils.closeQuietly(bugreportFd);
+ info.bugreportFile.delete();
+ return;
+ }
+
mBugreportManager = (BugreportManager) mContext.getSystemService(
Context.BUGREPORT_SERVICE);
final Executor executor = ActivityThread.currentActivityThread().getExecutor();
@@ -639,7 +634,7 @@
mBugreportManager.startBugreport(bugreportFd, screenshotFd,
new BugreportParams(bugreportType), executor, bugreportCallback);
} catch (RuntimeException e) {
- Log.i(TAG, "error in generating bugreports: ", e);
+ Log.i(TAG, "Error in generating bugreports: ", e);
// The binder call didn't go through successfully, so need to close the fds.
// If the calls went through API takes ownership.
FileUtils.closeQuietly(bugreportFd);
@@ -657,11 +652,15 @@
return null;
}
- private static void createReadWriteFile(File file) throws IOException {
- if (!file.exists()) {
- file.createNewFile();
- file.setReadable(true, true);
- file.setWritable(true, true);
+ private static void createReadWriteFile(File file) {
+ try {
+ if (!file.exists()) {
+ file.createNewFile();
+ file.setReadable(true, true);
+ file.setWritable(true, true);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error in creating bugreport file: ", e);
}
}
@@ -1836,23 +1835,23 @@
void createBugreportFile(File bugreportsDir) {
bugreportFile = new File(bugreportsDir, getFileName(this, ".zip"));
+ createReadWriteFile(bugreportFile);
}
void createScreenshotFile(File bugreportsDir) {
File screenshotFile = new File(bugreportsDir, getScreenshotName("default"));
addScreenshot(screenshotFile);
+ createReadWriteFile(screenshotFile);
}
- ParcelFileDescriptor createAndGetBugreportFd() throws IOException {
- createReadWriteFile(bugreportFile);
+ ParcelFileDescriptor getBugreportFd() {
return getFd(bugreportFile);
}
- ParcelFileDescriptor createAndGetDefaultScreenshotFd() throws IOException {
+ ParcelFileDescriptor getDefaultScreenshotFd() {
if (screenshotFiles.isEmpty()) {
return null;
}
- createReadWriteFile(screenshotFiles.get(0));
return getFd(screenshotFiles.get(0));
}
diff --git a/packages/SystemUI/res/color/control_background.xml b/packages/SystemUI/res/color/control_background.xml
index 646fe5d..977310c 100644
--- a/packages/SystemUI/res/color/control_background.xml
+++ b/packages/SystemUI/res/color/control_background.xml
@@ -2,5 +2,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_background" />
- <item android:color="@color/GM2_blue_50" />
+ <item android:color="@color/GM2_blue_200"
+ android:alpha="0.2" />
</selector>
diff --git a/packages/SystemUI/res/color/control_foreground.xml b/packages/SystemUI/res/color/control_foreground.xml
index bf028f1..339f1e2 100644
--- a/packages/SystemUI/res/color/control_foreground.xml
+++ b/packages/SystemUI/res/color/control_foreground.xml
@@ -2,5 +2,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_foreground" />
- <item android:color="@color/GM2_blue_700" />
+ <item android:color="@color/GM2_blue_200" />
</selector>
diff --git a/packages/SystemUI/res/color/light_background.xml b/packages/SystemUI/res/color/light_background.xml
index 2effd99..1299464 100644
--- a/packages/SystemUI/res/color/light_background.xml
+++ b/packages/SystemUI/res/color/light_background.xml
@@ -2,5 +2,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_background" />
- <item android:color="@color/GM2_yellow_50" />
+ <item android:color="@color/GM2_yellow_200"
+ android:alpha="0.2" />
</selector>
diff --git a/packages/SystemUI/res/color/light_foreground.xml b/packages/SystemUI/res/color/light_foreground.xml
index 8143028..7c02075 100644
--- a/packages/SystemUI/res/color/light_foreground.xml
+++ b/packages/SystemUI/res/color/light_foreground.xml
@@ -2,5 +2,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_foreground" />
- <item android:color="@color/GM2_orange_900" />
+ <item android:color="@color/GM2_yellow_200" />
</selector>
diff --git a/packages/SystemUI/res/color/thermo_cool_background.xml b/packages/SystemUI/res/color/thermo_cool_background.xml
index 646fe5d..977310c 100644
--- a/packages/SystemUI/res/color/thermo_cool_background.xml
+++ b/packages/SystemUI/res/color/thermo_cool_background.xml
@@ -2,5 +2,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_background" />
- <item android:color="@color/GM2_blue_50" />
+ <item android:color="@color/GM2_blue_200"
+ android:alpha="0.2" />
</selector>
diff --git a/packages/SystemUI/res/color/thermo_cool_foreground.xml b/packages/SystemUI/res/color/thermo_cool_foreground.xml
index bf028f1..339f1e2 100644
--- a/packages/SystemUI/res/color/thermo_cool_foreground.xml
+++ b/packages/SystemUI/res/color/thermo_cool_foreground.xml
@@ -2,5 +2,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_foreground" />
- <item android:color="@color/GM2_blue_700" />
+ <item android:color="@color/GM2_blue_200" />
</selector>
diff --git a/packages/SystemUI/res/color/thermo_heat_background.xml b/packages/SystemUI/res/color/thermo_heat_background.xml
index 6f29ed5..2709ebe 100644
--- a/packages/SystemUI/res/color/thermo_heat_background.xml
+++ b/packages/SystemUI/res/color/thermo_heat_background.xml
@@ -2,5 +2,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_background" />
- <item android:color="@color/GM2_red_50" />
+ <item android:color="@color/GM2_red_200"
+ android:alpha="0.2" />
</selector>
diff --git a/packages/SystemUI/res/color/thermo_heat_foreground.xml b/packages/SystemUI/res/color/thermo_heat_foreground.xml
index 72f4b8d..ffcf550 100644
--- a/packages/SystemUI/res/color/thermo_heat_foreground.xml
+++ b/packages/SystemUI/res/color/thermo_heat_foreground.xml
@@ -2,5 +2,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_foreground" />
- <item android:color="@color/GM2_red_700" />
+ <item android:color="@color/GM2_red_200" />
</selector>
diff --git a/packages/SystemUI/res/drawable/control_background.xml b/packages/SystemUI/res/drawable/control_background.xml
index b246ea0..29b4efa 100644
--- a/packages/SystemUI/res/drawable/control_background.xml
+++ b/packages/SystemUI/res/drawable/control_background.xml
@@ -19,7 +19,7 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
- <solid android:color="?android:attr/colorBackgroundFloating"/>
+ <solid android:color="@color/control_default_background" />
<corners android:radius="@dimen/control_corner_radius" />
</shape>
</item>
diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
index 95f205a..481c4db 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_activity.xml
+++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
@@ -14,8 +14,45 @@
~ limitations under the License
-->
-<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/bubble_overflow_recycler"
- android:layout_gravity="center_horizontal"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/bubble_overflow_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_gravity="center_horizontal">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/bubble_overflow_recycler"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <LinearLayout
+ android:id="@+id/bubble_overflow_empty_state"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <TextView
+ android:id="@+id/bubble_overflow_empty_title"
+ android:text="@string/bubble_overflow_empty_title"
+ android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"
+ android:textColor="?android:attr/textColorSecondary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"/>
+
+ <TextView
+ android:id="@+id/bubble_overflow_empty_subtitle"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"
+ android:textColor="?android:attr/textColorSecondary"
+ android:text="@string/bubble_overflow_empty_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"/>
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index d0151ff..0b74a11a 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -76,6 +76,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@android:color/white"
+ android:elevation="8dp"
android:visibility="gone"/>
<com.android.systemui.screenshot.ScreenshotSelectorView
android:id="@+id/global_screenshot_selector"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 09058f2..43ceb4e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -201,6 +201,7 @@
<color name="GM2_grey_900">#202124</color>
<color name="GM2_red_50">#FCE8E6</color>
+ <color name="GM2_red_200">#F6AEA9</color>
<color name="GM2_red_300">#F28B82</color>
<color name="GM2_red_500">#B71C1C</color>
<color name="GM2_red_700">#C5221F</color>
@@ -213,6 +214,7 @@
<color name="GM2_blue_700">#1967D2</color>
<color name="GM2_yellow_50">#FEF7E0</color>
+ <color name="GM2_yellow_200">#FDE293</color>
<color name="GM2_yellow_500">#FFFBBC04</color>
<color name="GM2_green_500">#FF34A853</color>
@@ -222,6 +224,8 @@
<color name="magnification_border_color">#FF9900</color>
<!-- controls -->
- <color name="control_default_foreground">?android:attr/textColorPrimary</color>
- <color name="control_default_background">?android:attr/colorBackgroundFloating</color>
+ <color name="control_primary_text">@*android:color/foreground_material_dark</color>
+ <color name="control_secondary_text">@*android:color/dim_foreground_dark</color>
+ <color name="control_default_foreground">@*android:color/foreground_material_dark</color>
+ <color name="control_default_background">@*android:color/background_floating_material_dark</color>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6dd89d8..ef9e705 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1780,6 +1780,12 @@
<!-- [CHAR LIMIT=150] Notification Importance title: bubble level summary -->
<string name="notification_channel_summary_bubble">Keeps your attention with a floating shortcut to this content.</string>
+ <!-- [CHAR LIMIT=NONE] Empty overflow title -->
+ <string name="bubble_overflow_empty_title">No recent bubbles</string>
+
+ <!-- [CHAR LIMIT=NONE] Empty overflow subtitle -->
+ <string name="bubble_overflow_empty_subtitle">Recently dismissed bubbles will appear here for easy retrieval.</string>
+
<!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
<string name="notification_unblockable_desc">These notifications can\'t be modified.</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6fedac0..fecb75c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -635,16 +635,16 @@
<style name="TextAppearance.Control.Status">
<item name="android:textSize">12sp</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">@color/control_primary_text</item>
</style>
<style name="TextAppearance.Control.Title">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">@color/control_primary_text</item>
</style>
<style name="TextAppearance.Control.Subtitle">
<item name="android:textSize">12sp</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">@color/control_secondary_text</item>
</style>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
index 0f7f1be..023b74b 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
@@ -19,7 +19,6 @@
import android.annotation.StringRes;
import android.content.Context;
-import android.view.WindowManager;
import android.widget.Toast;
public class SysUIToast {
@@ -29,10 +28,7 @@
}
public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
- Toast toast = Toast.makeText(context, text, duration);
- toast.getWindowParams().privateFlags |=
- WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- return toast;
+ return Toast.makeText(context, text, duration);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 68b05e3..9de1040 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricPrompt;
@@ -33,6 +34,8 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -126,18 +129,18 @@
mHandler.postDelayed(mClearErrorRunnable, ERROR_DURATION_MS);
}
- private void setTextOrHide(TextView view, String string) {
- if (TextUtils.isEmpty(string)) {
+ private void setTextOrHide(TextView view, CharSequence text) {
+ if (TextUtils.isEmpty(text)) {
view.setVisibility(View.GONE);
} else {
- view.setText(string);
+ view.setText(text);
}
Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
}
- private void setText(TextView view, String string) {
- view.setText(string);
+ private void setText(TextView view, CharSequence text) {
+ view.setText(text);
}
void setEffectiveUserId(int effectiveUserId) {
@@ -173,11 +176,9 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- setText(mTitleView, mBiometricPromptBundle.getString(BiometricPrompt.KEY_TITLE));
- setTextOrHide(mSubtitleView,
- mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE));
- setTextOrHide(mDescriptionView,
- mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
+ setText(mTitleView, getTitle(mBiometricPromptBundle));
+ setTextOrHide(mSubtitleView, getSubtitle(mBiometricPromptBundle));
+ setTextOrHide(mDescriptionView, getDescription(mBiometricPromptBundle));
final boolean isManagedProfile = Utils.isManagedProfile(mContext, mEffectiveUserId);
final Drawable image;
@@ -279,4 +280,28 @@
}
}
}
+
+ @Nullable
+ private static CharSequence getTitle(@NonNull Bundle bundle) {
+ final CharSequence credentialTitle =
+ bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_TITLE);
+ return credentialTitle != null ? credentialTitle
+ : bundle.getCharSequence(BiometricPrompt.KEY_TITLE);
+ }
+
+ @Nullable
+ private static CharSequence getSubtitle(@NonNull Bundle bundle) {
+ final CharSequence credentialSubtitle =
+ bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_SUBTITLE);
+ return credentialSubtitle != null ? credentialSubtitle
+ : bundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
+ }
+
+ @Nullable
+ private static CharSequence getDescription(@NonNull Bundle bundle) {
+ final CharSequence credentialDescription =
+ bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_DESCRIPTION);
+ return credentialDescription != null ? credentialDescription
+ : bundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 45705b7..1e39954 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -226,6 +226,10 @@
mIconView.update(this);
}
+ void setInflated(boolean inflated) {
+ mInflated = inflated;
+ }
+
/**
* Set visibility of bubble in the expanded state.
*
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 05838ab..762e5f2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -749,7 +749,8 @@
}
void promoteBubbleFromOverflow(Bubble bubble) {
- mBubbleData.promoteBubbleFromOverflow(bubble);
+ bubble.setInflateSynchronously(mInflateSynchronously);
+ mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 673121f..8a5aad8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -199,16 +199,21 @@
dispatchPendingChanges();
}
- public void promoteBubbleFromOverflow(Bubble bubble) {
+ public void promoteBubbleFromOverflow(Bubble bubble, BubbleStackView stack,
+ BubbleIconFactory factory) {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "promoteBubbleFromOverflow: " + bubble);
}
- mOverflowBubbles.remove(bubble);
- doAdd(bubble);
- setSelectedBubbleInternal(bubble);
+
// Preserve new order for next repack, which sorts by last updated time.
bubble.markUpdatedAt(mTimeSource.currentTimeMillis());
- trim();
+ setSelectedBubbleInternal(bubble);
+ mOverflowBubbles.remove(bubble);
+
+ bubble.inflate(
+ b -> notificationEntryUpdated(bubble, /* suppressFlyout */
+ false, /* showInShade */ true),
+ mContext, stack, factory);
dispatchPendingChanges();
}
@@ -445,6 +450,10 @@
mOverflowBubbles.add(0, bubbleToRemove);
if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) {
// Remove oldest bubble.
+ if (DEBUG_BUBBLE_DATA) {
+ Log.d(TAG, "Overflow full. Remove bubble: " + mOverflowBubbles.get(
+ mOverflowBubbles.size() - 1));
+ }
mOverflowBubbles.remove(mOverflowBubbles.size() - 1);
}
}
@@ -511,7 +520,7 @@
if (Objects.equals(bubble, mSelectedBubble)) {
return;
}
- if (bubble != null && !mBubbles.contains(bubble)) {
+ if (bubble != null && !mBubbles.contains(bubble) && !mOverflowBubbles.contains(bubble)) {
Log.e(TAG, "Cannot select bubble which doesn't exist!"
+ " (" + bubble + ") bubbles=" + mBubbles);
return;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 0d5261d..fe191f4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -294,7 +294,8 @@
ta.recycle();
mPointerDrawable.setTint(bgColor);
- if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
+ if (mActivityView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+ mContext.getResources())) {
mActivityView.setCornerRadius(cornerRadius);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 2d55a1d..f3cfa83 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -26,7 +26,10 @@
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -46,6 +49,7 @@
public class BubbleOverflowActivity extends Activity {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES;
+ private LinearLayout mEmptyState;
private BubbleController mBubbleController;
private BubbleOverflowAdapter mAdapter;
private RecyclerView mRecyclerView;
@@ -64,6 +68,7 @@
setBackgroundColor();
mMaxBubbles = getResources().getInteger(R.integer.bubbles_max_rendered);
+ mEmptyState = findViewById(R.id.bubble_overflow_empty_state);
mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
mRecyclerView.setLayoutManager(
new GridLayoutManager(getApplicationContext(),
@@ -73,9 +78,9 @@
mBubbleController::promoteBubbleFromOverflow);
mRecyclerView.setAdapter(mAdapter);
- updateData(mBubbleController.getOverflowBubbles());
+ onDataChanged(mBubbleController.getOverflowBubbles());
mBubbleController.setOverflowCallback(() -> {
- updateData(mBubbleController.getOverflowBubbles());
+ onDataChanged(mBubbleController.getOverflowBubbles());
});
}
@@ -87,7 +92,7 @@
findViewById(android.R.id.content).setBackgroundColor(bgColor);
}
- void updateData(List<Bubble> bubbles) {
+ void onDataChanged(List<Bubble> bubbles) {
mOverflowBubbles.clear();
if (bubbles.size() > mMaxBubbles) {
mOverflowBubbles.addAll(bubbles.subList(mMaxBubbles, bubbles.size()));
@@ -96,6 +101,12 @@
}
mAdapter.notifyDataSetChanged();
+ if (mOverflowBubbles.isEmpty()) {
+ mEmptyState.setVisibility(View.VISIBLE);
+ } else {
+ mEmptyState.setVisibility(View.GONE);
+ }
+
if (DEBUG_OVERFLOW) {
Log.d(TAG, "Updated overflow bubbles:\n" + BubbleDebugConfig.formatBubblesString(
mOverflowBubbles, /*selected*/ null));
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index bce172b..cff78cf 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -528,6 +528,12 @@
mBubbleContainer.addView(mOverflowBtn, 0,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ setOverflowBtnTheme();
+ mOverflowBtn.setVisibility(GONE);
+ }
+
+ // TODO(b/149146374) Propagate theme change to bubbles in overflow.
+ private void setOverflowBtnTheme() {
TypedArray ta = mContext.obtainStyledAttributes(
new int[]{android.R.attr.colorBackgroundFloating});
int bgColor = ta.getColor(0, Color.WHITE /* default */);
@@ -537,8 +543,6 @@
ColorDrawable bg = new ColorDrawable(bgColor);
AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
mOverflowBtn.setImageDrawable(adaptiveIcon);
-
- mOverflowBtn.setVisibility(GONE);
}
void showExpandedViewContents(int displayId) {
@@ -568,6 +572,9 @@
*/
public void onThemeChanged() {
setUpFlyout();
+ if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
+ setOverflowBtnTheme();
+ }
}
/** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
@@ -795,6 +802,7 @@
if (removedIndex >= 0) {
mBubbleContainer.removeViewAt(removedIndex);
bubble.cleanupExpandedState();
+ bubble.setInflated(false);
logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
} else {
Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble);
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 55c1b6a..aec20751 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -17,14 +17,15 @@
package com.android.systemui.controls.ui
import android.content.Context
+import android.graphics.BlendMode
import android.graphics.drawable.ClipDrawable
-import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.Icon
import android.graphics.drawable.LayerDrawable
import android.service.controls.Control
import android.service.controls.actions.ControlAction
import android.service.controls.templates.ControlTemplate
import android.service.controls.templates.TemperatureControlTemplate
+import android.service.controls.templates.ThumbnailTemplate
import android.service.controls.templates.ToggleRangeTemplate
import android.service.controls.templates.ToggleTemplate
import android.view.View
@@ -41,7 +42,8 @@
class ControlViewHolder(
val layout: ViewGroup,
val controlsController: ControlsController,
- val uiExecutor: DelayableExecutor
+ val uiExecutor: DelayableExecutor,
+ val bgExecutor: DelayableExecutor
) {
val icon: ImageView = layout.requireViewById(R.id.icon)
val status: TextView = layout.requireViewById(R.id.status)
@@ -50,7 +52,6 @@
val subtitle: TextView = layout.requireViewById(R.id.subtitle)
val context: Context = layout.getContext()
val clipLayer: ClipDrawable
- val gd: GradientDrawable
lateinit var cws: ControlWithState
var cancelUpdate: Runnable? = null
@@ -58,7 +59,6 @@
val ld = layout.getBackground() as LayerDrawable
ld.mutate()
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
- gd = clipLayer.getDrawable() as GradientDrawable
}
fun bindData(cws: ControlWithState) {
@@ -121,6 +121,7 @@
template is ToggleTemplate -> ToggleBehavior()
template is ToggleRangeTemplate -> ToggleRangeBehavior()
template is TemperatureControlTemplate -> TemperatureControlBehavior()
+ template is ThumbnailTemplate -> StaticBehavior(uiExecutor, bgExecutor)
else -> {
object : Behavior {
override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
@@ -141,9 +142,9 @@
icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
icon.setImageTintList(fg)
- gd.setColor(bg)
+ clipLayer.getDrawable().setTintBlendMode(BlendMode.HUE)
+ clipLayer.getDrawable().setTintList(bg)
}
-
fun setEnabled(enabled: Boolean) {
status.setEnabled(enabled)
icon.setEnabled(enabled)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 9e6636f..98cdf28 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -223,7 +223,7 @@
val item = inflater.inflate(
R.layout.controls_base_item, lastRow, false) as ViewGroup
lastRow.addView(item)
- val cvh = ControlViewHolder(item, controlsController.get(), uiExecutor)
+ val cvh = ControlViewHolder(item, controlsController.get(), uiExecutor, bgExecutor)
val key = ControlKey(it.component, it.controlId)
cvh.bindData(controlsById.getValue(key))
controlViewsById.put(key, cvh)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/CornerDrawable.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/CornerDrawable.kt
new file mode 100644
index 0000000..af581e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/CornerDrawable.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.controls.ui
+
+import android.graphics.Canvas
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+
+/**
+ * Use a path to add mask for corners around the drawable, to match the radius
+ * of the underlying shape.
+ */
+class CornerDrawable(val wrapped: Drawable, val cornerRadius: Float) : DrawableWrapper(wrapped) {
+ val path: Path = Path()
+
+ init {
+ val b = getBounds()
+ updatePath(RectF(b))
+ }
+
+ override fun draw(canvas: Canvas) {
+ canvas.clipPath(path)
+ super.draw(canvas)
+ }
+
+ override fun setBounds(l: Int, t: Int, r: Int, b: Int) {
+ updatePath(RectF(l.toFloat(), t.toFloat(), r.toFloat(), b.toFloat()))
+ super.setBounds(l, t, r, b)
+ }
+
+ override fun setBounds(r: Rect) {
+ updatePath(RectF(r))
+ super.setBounds(r)
+ }
+
+ private fun updatePath(r: RectF) {
+ path.reset()
+ path.addRoundRect(r, cornerRadius, cornerRadius, Path.Direction.CW)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
new file mode 100644
index 0000000..340cdc0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.controls.ui
+
+import android.graphics.drawable.ClipDrawable
+import android.graphics.drawable.LayerDrawable
+import android.service.controls.Control
+import android.service.controls.templates.ThumbnailTemplate
+
+import com.android.systemui.R
+import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
+
+import java.util.concurrent.Executor
+
+/**
+ * Used for controls that cannot be interacted with. Information is presented to the user
+ * but no actions can be taken. If using a ThumbnailTemplate, the background image will
+ * be changed.
+ */
+class StaticBehavior(
+ val uiExecutor: Executor,
+ val bgExecutor: Executor
+) : Behavior {
+ lateinit var control: Control
+
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ this.control = cws.control!!
+
+ cvh.status.setText(control.getStatusText())
+
+ val ld = cvh.layout.getBackground() as LayerDrawable
+ val clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
+
+ clipLayer.setLevel(MAX_LEVEL)
+ cvh.setEnabled(true)
+ cvh.applyRenderInfo(RenderInfo.lookup(control.getDeviceType(), true))
+
+ val template = control.getControlTemplate()
+ if (template is ThumbnailTemplate) {
+ bgExecutor.execute {
+ // clear the default tinting in favor of only using alpha
+ val drawable = template.getThumbnail().loadDrawable(cvh.context)
+ drawable.setTintList(null)
+ drawable.setAlpha((0.45 * 255).toInt())
+ uiExecutor.execute {
+ val radius = cvh.context.getResources()
+ .getDimensionPixelSize(R.dimen.control_corner_radius).toFloat()
+ clipLayer.setDrawable(CornerDrawable(drawable, radius))
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
index 5a6e5b4..27f1b33 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
@@ -18,7 +18,7 @@
class UnknownBehavior : Behavior {
override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
- cvh.status.setText("Loading...")
+ cvh.status.setText(cvh.context.getString(com.android.internal.R.string.loading))
cvh.setEnabled(false)
cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d298b99..56cfe80 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -64,12 +64,12 @@
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.ContextThemeWrapper;
+import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -88,13 +88,13 @@
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.view.RotationPolicy;
import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.controls.ui.ControlsUiController;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
@@ -110,6 +110,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -155,6 +156,7 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final ContentResolver mContentResolver;
private final Resources mResources;
+ private final ConfigurationController mConfigurationController;
private final UserManager mUserManager;
private final TrustManager mTrustManager;
private final IActivityManager mIActivityManager;
@@ -186,6 +188,8 @@
private final NotificationShadeWindowController mNotificationShadeWindowController;
private GlobalActionsPanelPlugin mPanelPlugin;
private ControlsUiController mControlsUiController;
+ private final IWindowManager mIWindowManager;
+ private final Executor mBackgroundExecutor;
/**
* @param context everything needs a context :(
@@ -204,7 +208,8 @@
BlurUtils blurUtils, SysuiColorExtractor colorExtractor,
IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
- ControlsUiController controlsUiController) {
+ ControlsUiController controlsUiController, IWindowManager iWindowManager,
+ @Background Executor backgroundExecutor) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -215,6 +220,7 @@
mBroadcastDispatcher = broadcastDispatcher;
mContentResolver = contentResolver;
mResources = resources;
+ mConfigurationController = configurationController;
mUserManager = userManager;
mTrustManager = trustManager;
mIActivityManager = iActivityManager;
@@ -225,6 +231,8 @@
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
mControlsUiController = controlsUiController;
+ mIWindowManager = iWindowManager;
+ mBackgroundExecutor = backgroundExecutor;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -249,7 +257,7 @@
mScreenshotHelper = new ScreenshotHelper(context);
mScreenRecordHelper = new ScreenRecordHelper(context);
- configurationController.addCallback(this);
+ mConfigurationController.addCallback(this);
mActivityStarter = activityStarter;
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@@ -495,7 +503,7 @@
}
public void destroy() {
- Dependency.get(ConfigurationController.class).removeCallback(this);
+ mConfigurationController.removeCallback(this);
}
private final class PowerAction extends SinglePressAction implements LongPressAction {
@@ -836,14 +844,12 @@
@Override
public void onPress() {
- new LockPatternUtils(mContext)
- .requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
- UserHandle.USER_ALL);
+ mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
+ UserHandle.USER_ALL);
try {
- WindowManagerGlobal.getWindowManagerService().lockNow(null);
+ mIWindowManager.lockNow(null);
// Lock profiles (if any) on the background thread.
- final Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
- bgHandler.post(() -> lockProfiles());
+ mBackgroundExecutor.execute(() -> lockProfiles());
} catch (RemoteException e) {
Log.e(TAG, "Error while trying to lock device.", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index ab8de26..5c1d332 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -105,8 +105,8 @@
fun logTileUpdated(tileSpec: String, state: QSTile.State) {
log(VERBOSE, {
str1 = tileSpec
- str2 = state.label.toString()
- str3 = state.icon.toString()
+ str2 = state.label?.toString()
+ str3 = state.icon?.toString()
int1 = state.state
if (state is QSTile.SignalState) {
bool1 = true
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 573ea4d..9f64b39 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -352,6 +352,8 @@
try {
Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.putExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE,
+ AccessibilityManager.ACCESSIBILITY_BUTTON);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
} finally {
Binder.restoreCallingIdentity(token);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 197fe21..9e1e347 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -27,7 +27,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -58,6 +57,7 @@
import android.provider.DeviceConfig;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Slog;
import android.view.Display;
import android.view.LayoutInflater;
@@ -68,6 +68,7 @@
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
@@ -136,13 +137,11 @@
private static final String TAG = "GlobalScreenshot";
- private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
- private static final int SCREENSHOT_DROP_IN_DURATION = 430;
- private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
- private static final int SCREENSHOT_DROP_OUT_DURATION = 430;
- private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370;
- private static final float BACKGROUND_ALPHA = 0.5f;
- private static final float SCREENSHOT_DROP_IN_MIN_SCALE = 0.725f;
+ private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
+ private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217;
+ private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
+ private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
+ private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
private static final float ROUNDED_CORNER_RADIUS = .05f;
private static final long SCREENSHOT_CORNER_TIMEOUT_MILLIS = 6000;
private static final int MESSAGE_CORNER_TIMEOUT = 2;
@@ -166,18 +165,21 @@
private final FrameLayout mDismissButton;
private Bitmap mScreenBitmap;
- private AnimatorSet mScreenshotAnimation;
+ private Animator mScreenshotAnimation;
private float mScreenshotOffsetXPx;
private float mScreenshotOffsetYPx;
private float mScreenshotHeightPx;
- private float mCornerScale;
private float mDismissButtonSize;
+ private float mCornerSizeX;
private AsyncTask<Void, Void, Void> mSaveInBgTask;
private MediaActionSound mCameraSound;
+ // standard material ease
+ private final Interpolator mFastOutSlowIn;
+
private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
@@ -227,6 +229,8 @@
mScreenshotLayout.setFocusable(true);
mScreenshotSelectorView.setFocusable(true);
mScreenshotSelectorView.setFocusableInTouchMode(true);
+ mScreenshotView.setPivotX(0);
+ mScreenshotView.setPivotY(0);
// Setup the window that we are going to use
mWindowLayoutParams = new WindowManager.LayoutParams(
@@ -250,10 +254,12 @@
mScreenshotOffsetYPx = resources.getDimensionPixelSize(R.dimen.screenshot_offset_y);
mScreenshotHeightPx =
resources.getDimensionPixelSize(R.dimen.screenshot_action_container_offset_y);
- mCornerScale = resources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale)
- / (float) mDisplayMetrics.widthPixels;
mDismissButtonSize = resources.getDimensionPixelSize(
R.dimen.screenshot_dismiss_button_tappable_size);
+ mCornerSizeX = resources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale);
+
+ mFastOutSlowIn =
+ AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
// Setup the Camera shutter sound
mCameraSound = new MediaActionSound();
@@ -305,7 +311,9 @@
int width = crop.width();
int height = crop.height();
- takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, null);
+ Rect screenRect = new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels);
+
+ takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect);
}
private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect) {
@@ -325,7 +333,7 @@
mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
// Start the post-screenshot animation
- startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+ startAnimation(finisher, screenRect.width(), screenRect.height(),
screenRect);
}
@@ -406,7 +414,6 @@
mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
// Clear any references to the bitmap
- mScreenBitmap = null;
mScreenshotView.setImageBitmap(null);
mActionsContainer.setVisibility(View.GONE);
mBackgroundView.setVisibility(View.GONE);
@@ -430,21 +437,8 @@
// Add the view for the animation
mScreenshotView.setImageBitmap(mScreenBitmap);
- mScreenshotLayout.requestFocus();
- // Setup the animation with the screenshot just taken
- if (mScreenshotAnimation != null) {
- if (mScreenshotAnimation.isStarted()) {
- mScreenshotAnimation.end();
- }
- mScreenshotAnimation.removeAllListeners();
- }
-
- ValueAnimator screenshotDropInAnim = screenRect != null ? createRectAnimation(screenRect)
- : createScreenshotDropInAnimation();
- ValueAnimator screenshotToCornerAnimation = createScreenshotToCornerAnimation(w, h);
- mScreenshotAnimation = new AnimatorSet();
- mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotToCornerAnimation);
+ mScreenshotAnimation = createScreenshotDropInAnimation(w, h, screenRect);
saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
@Override
@@ -487,147 +481,64 @@
});
}
- private ValueAnimator createRectAnimation(Rect rect) {
- mScreenshotView.setAdjustViewBounds(true);
- mScreenshotView.setMaxHeight(rect.height());
- mScreenshotView.setMaxWidth(rect.width());
+ private AnimatorSet createScreenshotDropInAnimation(int width, int height, Rect bounds) {
+ float cornerScale = mCornerSizeX / (float) width;
- final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
- / SCREENSHOT_DROP_IN_DURATION);
- final float flashDurationPct = 2f * flashPeakDurationPct;
- final Interpolator scaleInterpolator = x -> {
- // We start scaling when the flash is at it's peak
- if (x < flashPeakDurationPct) {
- return 0;
+ AnimatorSet dropInAnimation = new AnimatorSet();
+ ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
+ flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS);
+ flashInAnimator.setInterpolator(mFastOutSlowIn);
+ flashInAnimator.addUpdateListener(animation ->
+ mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
+
+ ValueAnimator flashOutAnimator = ValueAnimator.ofFloat(1, 0);
+ flashOutAnimator.setDuration(SCREENSHOT_FLASH_OUT_DURATION_MS);
+ flashOutAnimator.setInterpolator(mFastOutSlowIn);
+ flashOutAnimator.addUpdateListener(animation ->
+ mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
+
+ final PointF startPos = new PointF((float) bounds.left, (float) bounds.top);
+ final PointF finalPos = new PointF(mScreenshotOffsetXPx,
+ mDisplayMetrics.heightPixels - mScreenshotOffsetYPx - height * cornerScale);
+
+ ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
+ toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
+ float xPositionPct =
+ SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+ float scalePct =
+ SCREENSHOT_TO_CORNER_SCALE_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+ toCorner.addUpdateListener(animation -> {
+ float t = animation.getAnimatedFraction();
+ if (t < scalePct) {
+ float scale = MathUtils.lerp(
+ 1, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct));
+ mScreenshotView.setScaleX(scale);
+ mScreenshotView.setScaleY(scale);
}
- return (x - flashDurationPct) / (1f - flashDurationPct);
- };
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
+ if (t < xPositionPct) {
+ mScreenshotView.setX(MathUtils.lerp(
+ startPos.x, finalPos.x, mFastOutSlowIn.getInterpolation(t / xPositionPct)));
+ }
+ mScreenshotView.setY(MathUtils.lerp(
+ startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t)));
+ });
+
+ toCorner.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- mBackgroundView.setAlpha(0f);
- mBackgroundView.setVisibility(View.VISIBLE);
- mScreenshotView.setAlpha(0f);
- mScreenshotView.setElevation(0f);
- mScreenshotView.setTranslationX(0f);
- mScreenshotView.setTranslationY(0f);
- mScreenshotView.setScaleX(1f);
- mScreenshotView.setScaleY(1f);
+ super.onAnimationStart(animation);
mScreenshotView.setVisibility(View.VISIBLE);
}
});
- anim.addUpdateListener(animation -> {
- float t = (Float) animation.getAnimatedValue();
- mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(t);
- });
- return anim;
- }
- private ValueAnimator createScreenshotDropInAnimation() {
- final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
- / SCREENSHOT_DROP_IN_DURATION);
- final float flashDurationPct = 2f * flashPeakDurationPct;
- final Interpolator flashAlphaInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- // Flash the flash view in and out quickly
- if (x <= flashDurationPct) {
- return (float) Math.sin(Math.PI * (x / flashDurationPct));
- }
- return 0;
- }
- };
- final Interpolator scaleInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- // We start scaling when the flash is at it's peak
- if (x < flashPeakDurationPct) {
- return 0;
- }
- return (x - flashDurationPct) / (1f - flashDurationPct);
- }
- };
+ mScreenshotFlash.setAlpha(0f);
+ mScreenshotFlash.setVisibility(View.VISIBLE);
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mBackgroundView.setAlpha(0f);
- mBackgroundView.setVisibility(View.VISIBLE);
- mScreenshotView.setAlpha(0f);
- mScreenshotView.setTranslationX(0f);
- mScreenshotView.setTranslationY(0f);
- mScreenshotView.setScaleX(1);
- mScreenshotView.setScaleY(1);
- mScreenshotView.setVisibility(View.VISIBLE);
- mScreenshotFlash.setAlpha(0f);
- mScreenshotFlash.setVisibility(View.VISIBLE);
- }
+ dropInAnimation.play(flashOutAnimator).after(flashInAnimator);
+ dropInAnimation.play(flashOutAnimator).with(toCorner);
- @Override
- public void onAnimationEnd(Animator animation) {
- mScreenshotFlash.setVisibility(View.GONE);
- }
- });
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = 1 - (scaleInterpolator.getInterpolation(t)
- * (1 - SCREENSHOT_DROP_IN_MIN_SCALE));
- mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(t);
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));
- }
- });
- return anim;
- }
-
- private ValueAnimator createScreenshotToCornerAnimation(int w, int h) {
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
-
- final float scaleDurationPct =
- (float) SCREENSHOT_DROP_OUT_SCALE_DURATION / SCREENSHOT_DROP_OUT_DURATION;
- final Interpolator scaleInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- if (x < scaleDurationPct) {
- // Decelerate, and scale the input accordingly
- return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));
- }
- return 1f;
- }
- };
-
- // Determine the bounds of how to scale
- float halfScreenWidth = w / 2f;
- float halfScreenHeight = h / 2f;
- final PointF finalPos = new PointF(
- -halfScreenWidth + mCornerScale * halfScreenWidth + mScreenshotOffsetXPx,
- halfScreenHeight - mCornerScale * halfScreenHeight - mScreenshotOffsetYPx);
-
- // Animate the screenshot to the bottom left corner
- anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
- anim.addUpdateListener(animation -> {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE)
- - scaleInterpolator.getInterpolation(t)
- * (SCREENSHOT_DROP_IN_MIN_SCALE - mCornerScale);
- mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- mScreenshotView.setTranslationX(t * finalPos.x);
- mScreenshotView.setTranslationY(t * finalPos.y);
- });
- anim.addListener(new AnimatorListenerAdapter() {
+ dropInAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
@@ -638,7 +549,8 @@
mDismissButton.setVisibility(View.VISIBLE);
}
});
- return anim;
+
+ return dropInAnimation;
}
private ValueAnimator createScreenshotActionsShadeAnimation(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 92236ae..7de70f5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -219,11 +219,6 @@
mParams.mActionsReadyListener.onActionsReady(null, null, null);
}
- // Recycle the bitmap data
- if (image != null) {
- image.recycle();
- }
-
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 6d4b13c..91d2de7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -638,9 +638,9 @@
private static boolean shouldDismissOnClearAll(
NotificationEntry entry,
@UserIdInt int userId) {
- // TODO: (b/149396544) add FLAG_BUBBLE check here + in NoManService
return userIdMatches(entry, userId)
&& entry.isClearable()
+ && !hasFlag(entry, Notification.FLAG_BUBBLE)
&& entry.getDismissState() != DISMISSED;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index 4f27c0f..5b4a927 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -26,26 +26,23 @@
import android.content.DialogInterface
import android.graphics.Color
import android.graphics.PixelFormat
-import android.graphics.drawable.Drawable
import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.Window
-import android.view.WindowInsets.Type
import android.view.WindowInsets.Type.statusBars
import android.view.WindowManager
import android.widget.TextView
import com.android.internal.annotations.VisibleForTesting
-
import com.android.systemui.R
-
import javax.inject.Inject
import javax.inject.Singleton
-const val TAG = "ChannelDialogController"
+private const val TAG = "ChannelDialogController"
/**
* ChannelEditorDialogController is the controller for the dialog half-shelf
@@ -149,9 +146,9 @@
val channels = groupList
.flatMap { group ->
group.channels.asSequence().filterNot { channel ->
- channel.isImportanceLockedByOEM
- || channel.importance == IMPORTANCE_NONE
- || channel.isImportanceLockedByCriticalDeviceFunction
+ channel.isImportanceLockedByOEM ||
+ channel.importance == IMPORTANCE_NONE ||
+ channel.isImportanceLockedByCriticalDeviceFunction
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
index e2513da..d744fc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
@@ -74,11 +74,15 @@
@Singleton
public final class NotifBindPipeline {
private final Map<NotificationEntry, BindEntry> mBindEntries = new ArrayMap<>();
+ private final NotifBindPipelineLogger mLogger;
private BindStage mStage;
@Inject
- NotifBindPipeline(CommonNotifCollection collection) {
+ NotifBindPipeline(
+ CommonNotifCollection collection,
+ NotifBindPipelineLogger logger) {
collection.addCollectionListener(mCollectionListener);
+ mLogger = logger;
}
/**
@@ -86,6 +90,8 @@
*/
public void setStage(
BindStage stage) {
+ mLogger.logStageSet(stage.getClass().getName());
+
mStage = stage;
mStage.setBindRequestListener(this::onBindRequested);
}
@@ -96,6 +102,8 @@
public void manageRow(
@NonNull NotificationEntry entry,
@NonNull ExpandableNotificationRow row) {
+ mLogger.logManagedRow(entry.getKey());
+
final BindEntry bindEntry = getBindEntry(entry);
bindEntry.row = row;
if (bindEntry.invalidated) {
@@ -130,6 +138,8 @@
* callbacks when the run finishes. If a run is already in progress, it is restarted.
*/
private void startPipeline(NotificationEntry entry) {
+ mLogger.logStartPipeline(entry.getKey());
+
if (mStage == null) {
throw new IllegalStateException("No stage was ever set on the pipeline");
}
@@ -147,10 +157,11 @@
private void onPipelineComplete(NotificationEntry entry) {
final BindEntry bindEntry = getBindEntry(entry);
+ final Set<BindCallback> callbacks = bindEntry.callbacks;
+
+ mLogger.logFinishedPipeline(entry.getKey(), callbacks.size());
bindEntry.invalidated = false;
-
- final Set<BindCallback> callbacks = bindEntry.callbacks;
for (BindCallback cb : callbacks) {
cb.onBindFinished(entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
new file mode 100644
index 0000000..2717d7a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
@@ -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.
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+class NotifBindPipelineLogger @Inject constructor(
+ @NotificationLog private val buffer: LogBuffer
+) {
+ fun logStageSet(stageName: String) {
+ buffer.log(TAG, INFO, {
+ str1 = stageName
+ }, {
+ "Stage set: $str1"
+ })
+ }
+
+ fun logManagedRow(notifKey: String) {
+ buffer.log(TAG, INFO, {
+ str1 = notifKey
+ }, {
+ "Row set for notif: $str1"
+ })
+ }
+
+ fun logStartPipeline(notifKey: String) {
+ buffer.log(TAG, INFO, {
+ str1 = notifKey
+ }, {
+ "Start pipeline for notif: $str1"
+ })
+ }
+
+ fun logFinishedPipeline(notifKey: String, numCallbacks: Int) {
+ buffer.log(TAG, INFO, {
+ str1 = notifKey
+ int1 = numCallbacks
+ }, {
+ "Finished pipeline for notif $str1 with $int1 callbacks"
+ })
+ }
+}
+
+private const val TAG = "NotifBindPipeline"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
index 5170d0b..88ed0bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
@@ -157,6 +157,15 @@
return mViewsNeedReinflation;
}
+ @Override
+ public String toString() {
+ return String.format("RowContentBindParams[mContentViews=%x mDirtyContentViews=%x "
+ + "mUseLowPriority=%b mUseChildInGroup=%b mUseIncreasedHeight=%b "
+ + "mUseIncreasedHeadsUpHeight=%b mViewsNeedReinflation=%b]",
+ mContentViews, mDirtyContentViews, mUseLowPriority, mUseChildInGroup,
+ mUseIncreasedHeight, mUseIncreasedHeadsUpHeight, mViewsNeedReinflation);
+ }
+
/**
* Content views that should be inflated by default for all notifications.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
index f783245..c632f3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
@@ -38,13 +38,16 @@
public class RowContentBindStage extends BindStage<RowContentBindParams> {
private final NotificationRowContentBinder mBinder;
private final NotifInflationErrorManager mNotifInflationErrorManager;
+ private final RowContentBindStageLogger mLogger;
@Inject
RowContentBindStage(
NotificationRowContentBinder binder,
- NotifInflationErrorManager errorManager) {
+ NotifInflationErrorManager errorManager,
+ RowContentBindStageLogger logger) {
mBinder = binder;
mNotifInflationErrorManager = errorManager;
+ mLogger = logger;
}
@Override
@@ -54,6 +57,8 @@
@NonNull StageCallback callback) {
RowContentBindParams params = getStageParams(entry);
+ mLogger.logStageParams(entry.getKey(), params.toString());
+
// Resolve content to bind/unbind.
@InflationFlag int inflationFlags = params.getContentViews();
@InflationFlag int invalidatedFlags = params.getDirtyContentViews();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
new file mode 100644
index 0000000..29cce33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.row
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+class RowContentBindStageLogger @Inject constructor(
+ @NotificationLog private val buffer: LogBuffer
+) {
+ fun logStageParams(notifKey: String, stageParams: String) {
+ buffer.log(TAG, INFO, {
+ str1 = notifKey
+ str2 = stageParams
+ }, {
+ "Invalidated notif $str1 with params: \n$str2"
+ })
+ }
+}
+
+private const val TAG = "RowContentBindStage"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 3f5215e..fd8c71b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -956,6 +956,8 @@
private boolean onAccessibilityLongClick(View v) {
Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.putExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE,
+ AccessibilityManager.ACCESSIBILITY_BUTTON);
v.getContext().startActivityAsUser(intent, UserHandle.CURRENT);
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 70b43bf..e1a20b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -365,8 +365,12 @@
} catch (RemoteException ex) {
// system process is dead if we're here.
}
+
if (!isBubble) {
if (parentToCancelFinal != null) {
+ // TODO: (b/145659174) remove - this cancels the parent if the notification clicked
+ // on will auto-cancel and is the only child in the group. This won't be
+ // necessary in the new pipeline due to group pruning in ShadeListBuilder.
removeNotification(parentToCancelFinal);
}
if (shouldAutoCancel(sbn)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 5a0a495..f1fba79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -282,6 +282,9 @@
.inflationCallback(any()))
.thenReturn(mExpandableNotificationRowComponentBuilder);
when(mExpandableNotificationRowComponentBuilder
+ .rowContentBindStage(any()))
+ .thenReturn(mExpandableNotificationRowComponentBuilder);
+ when(mExpandableNotificationRowComponentBuilder
.onExpandClickListener(any()))
.thenReturn(mExpandableNotificationRowComponentBuilder);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
index 408bba4..6408f7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
@@ -59,7 +59,7 @@
MockitoAnnotations.initMocks(this);
CommonNotifCollection collection = mock(CommonNotifCollection.class);
- mBindPipeline = new NotifBindPipeline(collection);
+ mBindPipeline = new NotifBindPipeline(collection, mock(NotifBindPipelineLogger.class));
mBindPipeline.setStage(mStage);
ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index fd5512d..7a1bd05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -111,11 +111,13 @@
mock(NotifRemoteViewCache.class),
mock(NotificationRemoteInputManager.class));
contentBinder.setInflateSynchronously(true);
- mBindStage = new RowContentBindStage(contentBinder, mock(NotifInflationErrorManager.class));
+ mBindStage = new RowContentBindStage(contentBinder,
+ mock(NotifInflationErrorManager.class),
+ mock(RowContentBindStageLogger.class));
CommonNotifCollection collection = mock(CommonNotifCollection.class);
- mBindPipeline = new NotifBindPipeline(collection);
+ mBindPipeline = new NotifBindPipeline(collection, mock(NotifBindPipelineLogger.class));
mBindPipeline.setStage(mBindStage);
ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
index d9fe655..0f2482c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -60,8 +60,10 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- mRowContentBindStage = new RowContentBindStage(mBinder,
- mock(NotifInflationErrorManager.class));
+ mRowContentBindStage = new RowContentBindStage(
+ mBinder,
+ mock(NotifInflationErrorManager.class),
+ mock(RowContentBindStageLogger.class));
mRowContentBindStage.createStageParams(mEntry);
}
diff --git a/packages/services/PacProcessor/jni/Android.bp b/packages/services/PacProcessor/jni/Android.bp
index 351e92c..0e7e101 100644
--- a/packages/services/PacProcessor/jni/Android.bp
+++ b/packages/services/PacProcessor/jni/Android.bp
@@ -37,8 +37,7 @@
"-Wunused",
"-Wunreachable-code",
],
- // Re-enable when b/145990493 is fixed
- // sanitize: {
- // cfi: true,
- // },
+ sanitize: {
+ cfi: true,
+ },
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 75ec4b0..6c8aaf4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -16,8 +16,9 @@
package com.android.server.accessibility;
-import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID;
+import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE;
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER;
+import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP;
import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
@@ -39,6 +40,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.graphics.GraphicBuffer;
+import android.graphics.ParcelableColorSpace;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -1021,13 +1023,15 @@
final GraphicBuffer graphicBuffer = screenshotBuffer.getGraphicBuffer();
final HardwareBuffer hardwareBuffer =
HardwareBuffer.createFromGraphicBuffer(graphicBuffer);
- final int colorSpaceId = screenshotBuffer.getColorSpace().getId();
+ final ParcelableColorSpace colorSpace =
+ new ParcelableColorSpace(screenshotBuffer.getColorSpace());
// Send back the result.
final Bundle payload = new Bundle();
payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
hardwareBuffer);
- payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID, colorSpaceId);
+ payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
+ payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP, SystemClock.uptimeMillis());
callback.sendResult(payload);
}, null).recycleOnUse());
} finally {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index b74be7e..a3b5a3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility.gestures;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
@@ -24,6 +25,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
@@ -31,6 +33,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT;
@@ -132,6 +135,9 @@
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
+ new MultiFingerMultiTapAndHold(
+ mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, this));
+ mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP, this));
// Three-finger taps.
mMultiFingerGestures.add(
@@ -139,6 +145,9 @@
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
+ new MultiFingerMultiTapAndHold(
+ mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD, this));
+ mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
// Four-finger taps.
mMultiFingerGestures.add(
@@ -146,6 +155,9 @@
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 4, 2, GESTURE_4_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
+ new MultiFingerMultiTapAndHold(
+ mContext, 4, 2, GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD, this));
+ mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 4, 3, GESTURE_4_FINGER_TRIPLE_TAP, this));
// Two-finger swipes.
mMultiFingerGestures.add(
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
index 20def61..e5340f1 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
@@ -42,10 +42,10 @@
// The acceptable distance the pointer can move and still count as a tap.
private int mTouchSlop;
// A tap counts when target number of fingers are down and up once.
- private int mCompletedTapCount;
+ protected int mCompletedTapCount;
// A flag set to true when target number of fingers have touched down at once before.
// Used to indicate what next finger action should be. Down when false and lift when true.
- private boolean mIsTargetFingerCountReached = false;
+ protected boolean mIsTargetFingerCountReached = false;
// Store initial down points for slop checking and update when next down if is inside slop.
private PointF[] mBases;
// The points in bases that already have slop checked when onDown or onPointerDown.
@@ -56,7 +56,11 @@
* @throws IllegalArgumentException if <code>fingers<code/> is less than 2
* or <code>taps<code/> is not positive.
*/
- MultiFingerMultiTap(Context context, int fingers, int taps, int gestureId,
+ MultiFingerMultiTap(
+ Context context,
+ int fingers,
+ int taps,
+ int gestureId,
GestureMatcher.StateChangeListener listener) {
super(gestureId, new Handler(context.getMainLooper()), listener);
Preconditions.checkArgument(fingers >= 2);
@@ -117,8 +121,7 @@
cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags);
final PointF nearest = findNearestPoint(rawEvent, mTouchSlop, false);
- if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR)
- && null != nearest) {
+ if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR) && null != nearest) {
// Increase current tap count when the user have all fingers lifted
// within the tap timeout since the target number of fingers are down.
if (mIsTargetFingerCountReached) {
@@ -169,8 +172,7 @@
} else {
nearest = findNearestPoint(rawEvent, mDoubleTapSlop, true);
}
- if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR)
- && nearest != null) {
+ if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR) && nearest != null) {
// The user have all fingers down within the tap timeout since first finger down,
// setting the timeout for fingers to be lifted.
if (currentFingerCount == mTargetFingerCount) {
@@ -227,11 +229,11 @@
}
/**
- * Find the nearest location to the given event in the bases.
- * If no one found, it could be not inside {@code slop}, filtered or empty bases.
- * When {@code filterMatched} is true, if the location of given event matches one of the points
- * in {@link #mExcludedPointsForDownSlopChecked} it would be ignored. Otherwise, the location
- * will be added to {@link #mExcludedPointsForDownSlopChecked}.
+ * Find the nearest location to the given event in the bases. If no one found, it could be not
+ * inside {@code slop}, filtered or empty bases. When {@code filterMatched} is true, if the
+ * location of given event matches one of the points in {@link
+ * #mExcludedPointsForDownSlopChecked} it would be ignored. Otherwise, the location will be
+ * added to {@link #mExcludedPointsForDownSlopChecked}.
*
* @param event to find nearest point in bases.
* @param slop to check to the given location of the event.
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
new file mode 100644
index 0000000..7824fd9
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.gestures;
+
+import android.content.Context;
+import android.view.MotionEvent;
+
+/**
+ * This class matches gestures of the form multi-finger multi-tap and hold. The number of fingers
+ * and taps for each instance is specified in the constructor.
+ */
+class MultiFingerMultiTapAndHold extends MultiFingerMultiTap {
+
+ MultiFingerMultiTapAndHold(
+ Context context,
+ int fingers,
+ int taps,
+ int gestureId,
+ GestureMatcher.StateChangeListener listener) {
+ super(context, fingers, taps, gestureId, listener);
+ }
+
+ @Override
+ protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ super.onPointerDown(event, rawEvent, policyFlags);
+ if (mIsTargetFingerCountReached && mCompletedTapCount + 1 == mTargetTapCount) {
+ completeAfterLongPressTimeout(event, rawEvent, policyFlags);
+ }
+ }
+
+ @Override
+ protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mCompletedTapCount + 1 == mTargetFingerCount) {
+ // Calling super.onUp would complete the multi-tap version of this.
+ cancelGesture(event, rawEvent, policyFlags);
+ } else {
+ super.onUp(event, rawEvent, policyFlags);
+ cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags);
+ }
+ }
+
+ @Override
+ public String getGestureName() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(mTargetFingerCount).append("-Finger ");
+ if (mTargetTapCount == 1) {
+ builder.append("Single");
+ } else if (mTargetTapCount == 2) {
+ builder.append("Double");
+ } else if (mTargetTapCount == 3) {
+ builder.append("Triple");
+ } else if (mTargetTapCount > 3) {
+ builder.append(mTargetTapCount);
+ }
+ return builder.append(" Tap and hold").toString();
+ }
+}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 4f49fb7..0b3899d 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -16,6 +16,8 @@
package com.android.server.appprediction;
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
@@ -30,12 +32,17 @@
import android.content.pm.ServiceInfo;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.provider.DeviceConfig;
import android.service.appprediction.AppPredictionService;
+import android.service.appprediction.IPredictionService;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AbstractRemoteService;
+import com.android.server.LocalServices;
import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.people.PeopleServiceInternal;
import java.util.function.Consumer;
@@ -47,6 +54,8 @@
implements RemoteAppPredictionService.RemoteAppPredictionServiceCallbacks {
private static final String TAG = AppPredictionPerUserService.class.getSimpleName();
+ private static final String PREDICT_USING_PEOPLE_SERVICE_PREFIX =
+ "predict_using_people_service_";
@Nullable
@GuardedBy("mLock")
@@ -104,14 +113,16 @@
@GuardedBy("mLock")
public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
@NonNull AppPredictionSessionId sessionId) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
- if (service != null) {
- service.onCreatePredictionSession(context, sessionId);
-
- if (!mSessionInfos.containsKey(sessionId)) {
- mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
- this::removeAppPredictionSessionInfo));
- }
+ if (!mSessionInfos.containsKey(sessionId)) {
+ mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
+ DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
+ PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false),
+ this::removeAppPredictionSessionInfo));
+ }
+ final boolean serviceExists = resolveService(sessionId, s ->
+ s.onCreatePredictionSession(context, sessionId));
+ if (!serviceExists) {
+ mSessionInfos.remove(sessionId);
}
}
@@ -121,10 +132,7 @@
@GuardedBy("mLock")
public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull AppTargetEvent event) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
- if (service != null) {
- service.notifyAppTargetEvent(sessionId, event);
- }
+ resolveService(sessionId, s -> s.notifyAppTargetEvent(sessionId, event));
}
/**
@@ -133,10 +141,8 @@
@GuardedBy("mLock")
public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
- if (service != null) {
- service.notifyLaunchLocationShown(sessionId, launchLocation, targetIds);
- }
+ resolveService(sessionId, s ->
+ s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds));
}
/**
@@ -145,10 +151,7 @@
@GuardedBy("mLock")
public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
- if (service != null) {
- service.sortAppTargets(sessionId, targets, callback);
- }
+ resolveService(sessionId, s -> s.sortAppTargets(sessionId, targets, callback));
}
/**
@@ -157,14 +160,11 @@
@GuardedBy("mLock")
public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull IPredictionCallback callback) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
- if (service != null) {
- service.registerPredictionUpdates(sessionId, callback);
-
- AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
- if (sessionInfo != null) {
- sessionInfo.addCallbackLocked(callback);
- }
+ final boolean serviceExists = resolveService(sessionId, s ->
+ s.registerPredictionUpdates(sessionId, callback));
+ final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ if (serviceExists && sessionInfo != null) {
+ sessionInfo.addCallbackLocked(callback);
}
}
@@ -174,14 +174,11 @@
@GuardedBy("mLock")
public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull IPredictionCallback callback) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
- if (service != null) {
- service.unregisterPredictionUpdates(sessionId, callback);
-
- AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
- if (sessionInfo != null) {
- sessionInfo.removeCallbackLocked(callback);
- }
+ final boolean serviceExists = resolveService(sessionId, s ->
+ s.unregisterPredictionUpdates(sessionId, callback));
+ final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ if (serviceExists && sessionInfo != null) {
+ sessionInfo.removeCallbackLocked(callback);
}
}
@@ -190,10 +187,7 @@
*/
@GuardedBy("mLock")
public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
- if (service != null) {
- service.requestPredictionUpdate(sessionId);
- }
+ resolveService(sessionId, s -> s.requestPredictionUpdate(sessionId));
}
/**
@@ -201,14 +195,11 @@
*/
@GuardedBy("mLock")
public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
- if (service != null) {
- service.onDestroyPredictionSession(sessionId);
-
- AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
- if (sessionInfo != null) {
- sessionInfo.destroy();
- }
+ final boolean serviceExists = resolveService(sessionId, s ->
+ s.onDestroyPredictionSession(sessionId));
+ final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ if (serviceExists && sessionInfo != null) {
+ sessionInfo.destroy();
}
}
@@ -312,6 +303,33 @@
@GuardedBy("mLock")
@Nullable
+ protected boolean resolveService(@NonNull final AppPredictionSessionId sessionId,
+ @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb) {
+ final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ if (sessionInfo == null) return false;
+ if (sessionInfo.mUsesPeopleService) {
+ final IPredictionService service =
+ LocalServices.getService(PeopleServiceInternal.class);
+ if (service != null) {
+ try {
+ cb.run(service);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Slog.w(TAG, "Failed to invoke service:" + service, e);
+ }
+ }
+ return service != null;
+ } else {
+ final RemoteAppPredictionService service = getRemoteServiceLocked();
+ if (service != null) {
+ service.scheduleOnResolvedService(cb);
+ }
+ return service != null;
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
private RemoteAppPredictionService getRemoteServiceLocked() {
if (mRemoteService == null) {
final String serviceName = getComponentNameLocked();
@@ -334,8 +352,12 @@
private static final class AppPredictionSessionInfo {
private static final boolean DEBUG = false; // Do not submit with true
+ @NonNull
private final AppPredictionSessionId mSessionId;
+ @NonNull
private final AppPredictionContext mPredictionContext;
+ private final boolean mUsesPeopleService;
+ @NonNull
private final Consumer<AppPredictionSessionId> mRemoveSessionInfoAction;
private final RemoteCallbackList<IPredictionCallback> mCallbacks =
@@ -352,13 +374,17 @@
}
};
- AppPredictionSessionInfo(AppPredictionSessionId id, AppPredictionContext predictionContext,
- Consumer<AppPredictionSessionId> removeSessionInfoAction) {
+ AppPredictionSessionInfo(
+ @NonNull final AppPredictionSessionId id,
+ @NonNull final AppPredictionContext predictionContext,
+ final boolean usesPeopleService,
+ @NonNull final Consumer<AppPredictionSessionId> removeSessionInfoAction) {
if (DEBUG) {
Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id);
}
mSessionId = id;
mPredictionContext = predictionContext;
+ mUsesPeopleService = usesPeopleService;
mRemoveSessionInfoAction = removeSessionInfoAction;
}
diff --git a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
index 04e0e7f..ceb1caf 100644
--- a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
+++ b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
@@ -16,13 +16,8 @@
package com.android.server.appprediction;
import android.annotation.NonNull;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionSessionId;
-import android.app.prediction.AppTargetEvent;
-import android.app.prediction.IPredictionCallback;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.ParceledListSlice;
import android.os.IBinder;
import android.service.appprediction.IPredictionService;
import android.text.format.DateUtils;
@@ -71,70 +66,6 @@
}
/**
- * Notifies the service of a new prediction session.
- */
- public void onCreatePredictionSession(@NonNull AppPredictionContext context,
- @NonNull AppPredictionSessionId sessionId) {
- scheduleAsyncRequest((s) -> s.onCreatePredictionSession(context, sessionId));
- }
-
- /**
- * Records an app target event to the service.
- */
- public void notifyAppTargetEvent(@NonNull AppPredictionSessionId sessionId,
- @NonNull AppTargetEvent event) {
- scheduleAsyncRequest((s) -> s.notifyAppTargetEvent(sessionId, event));
- }
-
- /**
- * Records when a launch location is shown.
- */
- public void notifyLaunchLocationShown(@NonNull AppPredictionSessionId sessionId,
- @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
- scheduleAsyncRequest((s)
- -> s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds));
- }
-
- /**
- * Requests the service to sort a list of apps or shortcuts.
- */
- public void sortAppTargets(@NonNull AppPredictionSessionId sessionId,
- @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
- scheduleAsyncRequest((s) -> s.sortAppTargets(sessionId, targets, callback));
- }
-
-
- /**
- * Registers a callback for continuous updates of predicted apps or shortcuts.
- */
- public void registerPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
- @NonNull IPredictionCallback callback) {
- scheduleAsyncRequest((s) -> s.registerPredictionUpdates(sessionId, callback));
- }
-
- /**
- * Unregisters a callback for continuous updates of predicted apps or shortcuts.
- */
- public void unregisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
- @NonNull IPredictionCallback callback) {
- scheduleAsyncRequest((s) -> s.unregisterPredictionUpdates(sessionId, callback));
- }
-
- /**
- * Requests a new set of predicted apps or shortcuts.
- */
- public void requestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) {
- scheduleAsyncRequest((s) -> s.requestPredictionUpdate(sessionId));
- }
-
- /**
- * Notifies the service of the end of an existing prediction session.
- */
- public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) {
- scheduleAsyncRequest((s) -> s.onDestroyPredictionSession(sessionId));
- }
-
- /**
* Schedules a request to bind to the remote service.
*/
public void reconnect() {
@@ -142,6 +73,13 @@
}
/**
+ * Schedule async request on remote service.
+ */
+ public void scheduleOnResolvedService(@NonNull AsyncRequest<IPredictionService> request) {
+ scheduleAsyncRequest(request);
+ }
+
+ /**
* Failure callback
*/
public interface RemoteAppPredictionServiceCallbacks
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 31ea5fa..9a33fc9 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -998,6 +998,11 @@
sendErrorSignal(mClientAdapterReference, serviceAdapterReference,
ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
+ } finally {
+ synchronized (parentService.mLock) {
+ parentService.mPackagesWithShareRequests
+ .remove(mDataShareRequest.getPackageName());
+ }
}
});
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index 442c9e5..f688759 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -216,17 +216,11 @@
/**
* Returns the events for the user in the given time period.
*
- * @param obfuscateInstantApps whether instant app package names need to be obfuscated in the
- * result.
- * @param hideShortcutInvocationEvents whether the {@link UsageEvents.Event#SHORTCUT_INVOCATION}
- * events need to be excluded from the result.
- * @param hideLocusIdEvents whether the {@link UsageEvents.Event#LOCUS_ID_SET}
- * events need to be excluded from the result.
- *
+ * @param flags defines the visibility of certain usage events - see flags defined in
+ * {@link UsageEvents}.
*/
public abstract UsageEvents queryEventsForUser(@UserIdInt int userId, long beginTime,
- long endTime, boolean obfuscateInstantApps, boolean hideShortcutInvocationEvents,
- boolean hideLocusIdEvents);
+ long endTime, int flags);
/**
* Used to persist the last time a job was run for this app, in order to make decisions later
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index c987620..9540f43 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1556,16 +1556,16 @@
}
Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
- switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
- case AppOpsManager.MODE_DEFAULT:
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
- break;
- case AppOpsManager.MODE_ALLOWED:
- return;
- default:
- throw new SecurityException("Request to ignore AppOps for non-legacy API");
+
+ // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
+ // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
+ // permission or is the System Server.
+ if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
+ TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
+ return;
}
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
}
private void createOrUpdateTransform(
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index e3c7325..aabe98b 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -168,6 +168,11 @@
public int getUserIdentifier() {
return mUserInfo.id;
}
+
+ @Override
+ public String toString() {
+ return Integer.toString(getUserIdentifier());
+ }
}
/**
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 0e5a6bb..f85fc28 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -61,6 +61,7 @@
import android.telephony.CellSignalStrengthWcdma;
import android.telephony.DataFailCause;
import android.telephony.DisconnectCause;
+import android.telephony.DisplayInfo;
import android.telephony.LocationAccessPolicy;
import android.telephony.PhoneCapability;
import android.telephony.PhoneStateListener;
@@ -205,6 +206,8 @@
private boolean[] mUserMobileDataState;
+ private DisplayInfo[] mDisplayInfos;
+
private SignalStrength[] mSignalStrength;
private boolean[] mMessageWaiting;
@@ -284,7 +287,8 @@
static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
| PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
- | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST;
+ | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
+ | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED;
static final int ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_PRECISE_CALL_STATE
@@ -443,6 +447,7 @@
mCallAttributes = copyOf(mCallAttributes, mNumPhones);
mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones);
mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones);
+ mDisplayInfos = copyOf(mDisplayInfos, mNumPhones);
// ds -> ss switch.
if (mNumPhones < oldNumPhones) {
@@ -482,6 +487,7 @@
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>());
mBarringInfo.add(i, new BarringInfo());
+ mDisplayInfos[i] = null;
}
}
@@ -540,6 +546,7 @@
mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones];
mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones];
mBarringInfo = new ArrayList<>();
+ mDisplayInfos = new DisplayInfo[numPhones];
for (int i = 0; i < numPhones; i++) {
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -568,6 +575,7 @@
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>());
mBarringInfo.add(i, new BarringInfo());
+ mDisplayInfos[i] = null;
}
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -978,6 +986,15 @@
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
+ try {
+ if (mDisplayInfos[phoneId] != null) {
+ r.callback.onDisplayInfoChanged(mDisplayInfos[phoneId]);
+ }
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
try {
r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
@@ -1501,6 +1518,45 @@
}
}
+ /**
+ * Notify display network info changed.
+ *
+ * @param phoneId Phone id
+ * @param subId Subscription id
+ * @param displayInfo Display network info
+ *
+ * @see PhoneStateListener#onDisplayInfoChanged(DisplayInfo)
+ */
+ public void notifyDisplayInfoChanged(int phoneId, int subId,
+ @NonNull DisplayInfo displayInfo) {
+ if (!checkNotifyPermission("notifyDisplayInfoChanged()")) {
+ return;
+ }
+ if (VDBG) {
+ log("notifyDisplayInfoChanged: PhoneId=" + phoneId
+ + " subId=" + subId + " displayInfo=" + displayInfo);
+ }
+ synchronized (mRecords) {
+ if (validatePhoneId(phoneId)) {
+ if (mDisplayInfos[phoneId] != null) {
+ mDisplayInfos[phoneId] = displayInfo;
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
+ && idMatch(r.subId, subId, phoneId)) {
+ try {
+ r.callback.onDisplayInfoChanged(displayInfo);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
public void notifyCallForwardingChanged(boolean cfi) {
notifyCallForwardingChangedForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cfi);
}
@@ -2730,6 +2786,20 @@
}
}
+ if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
+ try {
+ if (VDBG) {
+ log("checkPossibleMissNotify: onDisplayInfoChanged phoneId="
+ + phoneId + " dpi=" + mDisplayInfos[phoneId]);
+ }
+ if (mDisplayInfos[phoneId] != null) {
+ r.callback.onDisplayInfoChanged(mDisplayInfos[phoneId]);
+ }
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+
if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
try {
if (VDBG) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index abe0dd5..ffa7d92 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -59,6 +59,8 @@
import android.app.ApplicationExitInfo.SubReason;
import android.app.IApplicationThread;
import android.app.IUidObserver;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -119,6 +121,7 @@
import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.WindowManagerService;
+import dalvik.annotation.compat.VersionCodes;
import dalvik.system.VMRuntime;
import java.io.File;
@@ -327,6 +330,15 @@
*/
private static final int PROC_KILL_TIMEOUT = 2000; // 2 seconds;
+ /**
+ * Native heap allocations will now have a non-zero tag in the most significant byte.
+ * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
+ * Pointers</a>
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = VersionCodes.Q)
+ private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
+
ActivityManagerService mService = null;
// To kill process groups asynchronously
@@ -1768,6 +1780,13 @@
runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
}
+ // Enable heap pointer tagging, unless disabled by the app manifest, target sdk level,
+ // or the compat feature.
+ if (app.info.allowsNativeHeapPointerTagging()
+ && mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
+ runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+ }
+
String invokeWith = null;
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// Debuggable apps may include a wrapper script with their library directory.
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6911e64..0edc160 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -56,6 +56,7 @@
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
@@ -410,9 +411,9 @@
Slog.e(TAG, "Bad app ops settings", e);
}
TOP_STATE_SETTLE_TIME = mParser.getDurationMillis(
- KEY_TOP_STATE_SETTLE_TIME, 30 * 1000L);
+ KEY_TOP_STATE_SETTLE_TIME, 5 * 1000L);
FG_SERVICE_STATE_SETTLE_TIME = mParser.getDurationMillis(
- KEY_FG_SERVICE_STATE_SETTLE_TIME, 10 * 1000L);
+ KEY_FG_SERVICE_STATE_SETTLE_TIME, 5 * 1000L);
BG_STATE_SETTLE_TIME = mParser.getDurationMillis(
KEY_BG_STATE_SETTLE_TIME, 1 * 1000L);
}
@@ -3365,7 +3366,8 @@
return false;
}
- return permInfo.getProtection() == PROTECTION_DANGEROUS;
+ return permInfo.getProtection() == PROTECTION_DANGEROUS
+ || (permInfo.getProtectionFlags() & PROTECTION_FLAG_APPOP) != 0;
}
private void verifyIncomingUid(int uid) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 97a8b87..f41eeeb 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -128,6 +128,7 @@
import android.util.MathUtils;
import android.util.PrintWriterPrinter;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityManager;
@@ -247,6 +248,7 @@
// AudioHandler messages
private static final int MSG_SET_DEVICE_VOLUME = 0;
private static final int MSG_PERSIST_VOLUME = 1;
+ private static final int MSG_PERSIST_VOLUME_GROUP = 2;
private static final int MSG_PERSIST_RINGER_MODE = 3;
private static final int MSG_AUDIO_SERVER_DIED = 4;
private static final int MSG_PLAY_SOUND_EFFECT = 5;
@@ -780,6 +782,10 @@
mSettingsObserver = new SettingsObserver();
createStreamStates();
+ // must be called after createStreamStates() as it uses MUSIC volume as default if no
+ // persistent data
+ initVolumeGroupStates();
+
// mSafeUsbMediaVolumeIndex must be initialized after createStreamStates() because it
// relies on audio policy having correct ranges for volume indexes.
mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
@@ -995,11 +1001,14 @@
mDeviceBroker.onAudioServerDied();
// Restore call state
- if (AudioSystem.setPhoneState(mMode) == AudioSystem.AUDIO_STATUS_OK) {
- mModeLogger.log(new AudioEventLogger.StringEvent(
- "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode) + ")"));
+ synchronized (mDeviceBroker.mSetModeLock) {
+ if (AudioSystem.setPhoneState(mMode, getModeOwnerUid())
+ == AudioSystem.AUDIO_STATUS_OK) {
+ mModeLogger.log(new AudioEventLogger.StringEvent(
+ "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode)
+ + ", uid=" + getModeOwnerUid() + ")"));
+ }
}
-
final int forSys;
synchronized (mSettingsLock) {
forSys = mCameraSoundForced ?
@@ -1018,6 +1027,9 @@
streamState.applyAllVolumes();
}
+ // Restore audio volume groups
+ restoreVolumeGroups();
+
// Restore mono mode
updateMasterMono(mContentResolver);
@@ -2288,20 +2300,20 @@
String callingPackage) {
enforceModifyAudioRoutingPermission();
Objects.requireNonNull(attr, "attr must not be null");
- // @todo not hold the caller context, post message
- int stream = AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(attr);
- final int device = getDeviceForStream(stream);
-
- int oldIndex = AudioSystem.getVolumeIndexForAttributes(attr, device);
-
- AudioSystem.setVolumeIndexForAttributes(attr, index, device);
-
final int volumeGroup = getVolumeGroupIdForAttributes(attr);
- final AudioVolumeGroup avg = getAudioVolumeGroupById(volumeGroup);
- if (avg == null) {
+ if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
+ Log.e(TAG, ": no volume group found for attributes " + attr.toString());
return;
}
- for (final int groupedStream : avg.getLegacyStreamTypes()) {
+ final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
+
+ sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, attr, vgs.name(),
+ index/*val1*/, flags/*val2*/, callingPackage));
+
+ vgs.setVolumeIndex(index, flags);
+
+ // For legacy reason, propagate to all streams associated to this volume group
+ for (final int groupedStream : vgs.getLegacyStreamTypes()) {
setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage,
Binder.getCallingUid());
}
@@ -2323,10 +2335,12 @@
public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
enforceModifyAudioRoutingPermission();
Objects.requireNonNull(attr, "attr must not be null");
- int stream = AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(attr);
- final int device = getDeviceForStream(stream);
-
- return AudioSystem.getVolumeIndexForAttributes(attr, device);
+ final int volumeGroup = getVolumeGroupIdForAttributes(attr);
+ if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
+ throw new IllegalArgumentException("No volume group for attributes " + attr);
+ }
+ final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
+ return vgs.getVolumeIndex();
}
/** @see AudioManager#getMaxVolumeIndexForAttributes(attr) */
@@ -3414,14 +3428,30 @@
return modeOwnerPid;
}
+ /**
+ * Return the uid of the current audio mode owner
+ * @return 0 if nobody owns the mode
+ */
+ /*package*/ int getModeOwnerUid() {
+ int modeOwnerUid = 0;
+ try {
+ modeOwnerUid = mSetModeDeathHandlers.get(0).getUid();
+ } catch (Exception e) {
+ // nothing to do, modeOwnerUid is not modified
+ }
+ return modeOwnerUid;
+ }
+
private class SetModeDeathHandler implements IBinder.DeathRecipient {
- private IBinder mCb; // To be notified of client's death
- private int mPid;
+ private final IBinder mCb; // To be notified of client's death
+ private final int mPid;
+ private final int mUid;
private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
- SetModeDeathHandler(IBinder cb, int pid) {
+ SetModeDeathHandler(IBinder cb, int pid, int uid) {
mCb = cb;
mPid = pid;
+ mUid = uid;
}
public void binderDied() {
@@ -3434,7 +3464,7 @@
if (index < 0) {
Log.w(TAG, "unregistered setMode() client died");
} else {
- newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, TAG);
+ newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, mUid, TAG);
}
}
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
@@ -3459,6 +3489,10 @@
public IBinder getBinder() {
return mCb;
}
+
+ public int getUid() {
+ return mUid;
+ }
}
/** @see AudioManager#setMode(int) */
@@ -3507,7 +3541,8 @@
+ " without permission or being mode owner");
return;
}
- newModeOwnerPid = setModeInt(mode, cb, callingPid, callingPackage);
+ newModeOwnerPid = setModeInt(
+ mode, cb, callingPid, Binder.getCallingUid(), callingPackage);
}
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
// SCO connections not started by the application changing the mode when pid changes
@@ -3519,9 +3554,11 @@
// setModeInt() returns a valid PID if the audio mode was successfully set to
// any mode other than NORMAL.
@GuardedBy("mDeviceBroker.mSetModeLock")
- private int setModeInt(int mode, IBinder cb, int pid, String caller) {
- if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", caller="
- + caller + ")"); }
+ private int setModeInt(int mode, IBinder cb, int pid, int uid, String caller) {
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid
+ + ", uid=" + uid + ", caller=" + caller + ")");
+ }
int newModeOwnerPid = 0;
if (cb == null) {
Log.e(TAG, "setModeInt() called with null binder");
@@ -3558,7 +3595,7 @@
}
} else {
if (hdlr == null) {
- hdlr = new SetModeDeathHandler(cb, pid);
+ hdlr = new SetModeDeathHandler(cb, pid, uid);
}
// Register for client death notification
try {
@@ -3576,7 +3613,7 @@
if (actualMode != mMode) {
final long identity = Binder.clearCallingIdentity();
- status = AudioSystem.setPhoneState(actualMode);
+ status = AudioSystem.setPhoneState(actualMode, getModeOwnerUid());
Binder.restoreCallingIdentity(identity);
if (status == AudioSystem.AUDIO_STATUS_OK) {
if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
@@ -3754,6 +3791,8 @@
enforceSafeMediaVolume(TAG);
}
}
+
+ readVolumeGroupsSettings();
}
/** @see AudioManager#setSpeakerphoneOn(boolean) */
@@ -4654,6 +4693,310 @@
///////////////////////////////////////////////////////////////////////////
// Inner classes
///////////////////////////////////////////////////////////////////////////
+ /**
+ * Key is the AudioManager VolumeGroupId
+ * Value is the VolumeGroupState
+ */
+ private static final SparseArray<VolumeGroupState> sVolumeGroupStates = new SparseArray<>();
+
+ private void initVolumeGroupStates() {
+ for (final AudioVolumeGroup avg : getAudioVolumeGroups()) {
+ try {
+ // if no valid attributes, this volume group is not controllable, throw exception
+ ensureValidAttributes(avg);
+ } catch (IllegalArgumentException e) {
+ // Volume Groups without attributes are not controllable through set/get volume
+ // using attributes. Do not append them.
+ Log.d(TAG, "volume group " + avg.name() + " for internal policy needs");
+ continue;
+ }
+ sVolumeGroupStates.append(avg.getId(), new VolumeGroupState(avg));
+ }
+ for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+ final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+ vgs.applyAllVolumes();
+ }
+ }
+
+ private void ensureValidAttributes(AudioVolumeGroup avg) {
+ boolean hasAtLeastOneValidAudioAttributes = avg.getAudioAttributes().stream()
+ .anyMatch(aa -> !aa.equals(AudioProductStrategy.sDefaultAttributes));
+ if (!hasAtLeastOneValidAudioAttributes) {
+ throw new IllegalArgumentException("Volume Group " + avg.name()
+ + " has no valid audio attributes");
+ }
+ }
+
+ private void readVolumeGroupsSettings() {
+ Log.v(TAG, "readVolumeGroupsSettings");
+ for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+ final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+ vgs.readSettings();
+ vgs.applyAllVolumes();
+ }
+ }
+
+ // Called upon crash of AudioServer
+ private void restoreVolumeGroups() {
+ Log.v(TAG, "restoreVolumeGroups");
+ for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+ final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+ vgs.applyAllVolumes();
+ }
+ }
+
+ private void dumpVolumeGroups(PrintWriter pw) {
+ pw.println("\nVolume Groups (device: index)");
+ for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+ final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+ vgs.dump(pw);
+ pw.println("");
+ }
+ }
+
+ // NOTE: Locking order for synchronized objects related to volume management:
+ // 1 mSettingsLock
+ // 2 VolumeGroupState.class
+ private class VolumeGroupState {
+ private final AudioVolumeGroup mAudioVolumeGroup;
+ private final SparseIntArray mIndexMap = new SparseIntArray(8);
+ private int mIndexMin;
+ private int mIndexMax;
+ private int mLegacyStreamType = AudioSystem.STREAM_DEFAULT;
+ private int mPublicStreamType = AudioSystem.STREAM_MUSIC;
+ private AudioAttributes mAudioAttributes = AudioProductStrategy.sDefaultAttributes;
+
+ // No API in AudioSystem to get a device from strategy or from attributes.
+ // Need a valid public stream type to use current API getDeviceForStream
+ private int getDeviceForVolume() {
+ return getDeviceForStream(mPublicStreamType);
+ }
+
+ private VolumeGroupState(AudioVolumeGroup avg) {
+ mAudioVolumeGroup = avg;
+ Log.v(TAG, "VolumeGroupState for " + avg.toString());
+ for (final AudioAttributes aa : avg.getAudioAttributes()) {
+ if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ mAudioAttributes = aa;
+ break;
+ }
+ }
+ final int[] streamTypes = mAudioVolumeGroup.getLegacyStreamTypes();
+ if (streamTypes.length != 0) {
+ // Uses already initialized MIN / MAX if a stream type is attached to group
+ mLegacyStreamType = streamTypes[0];
+ for (final int streamType : streamTypes) {
+ if (streamType != AudioSystem.STREAM_DEFAULT
+ && streamType < AudioSystem.getNumStreamTypes()) {
+ mPublicStreamType = streamType;
+ break;
+ }
+ }
+ mIndexMin = MIN_STREAM_VOLUME[mPublicStreamType];
+ mIndexMax = MAX_STREAM_VOLUME[mPublicStreamType];
+ } else if (!avg.getAudioAttributes().isEmpty()) {
+ mIndexMin = AudioSystem.getMinVolumeIndexForAttributes(mAudioAttributes);
+ mIndexMax = AudioSystem.getMaxVolumeIndexForAttributes(mAudioAttributes);
+ } else {
+ Log.e(TAG, "volume group: " + mAudioVolumeGroup.name()
+ + " has neither valid attributes nor valid stream types assigned");
+ return;
+ }
+ // Load volume indexes from data base
+ readSettings();
+ }
+
+ public @NonNull int[] getLegacyStreamTypes() {
+ return mAudioVolumeGroup.getLegacyStreamTypes();
+ }
+
+ public String name() {
+ return mAudioVolumeGroup.name();
+ }
+
+ public int getVolumeIndex() {
+ return getIndex(getDeviceForVolume());
+ }
+
+ public void setVolumeIndex(int index, int flags) {
+ if (mUseFixedVolume) {
+ return;
+ }
+ setVolumeIndex(index, getDeviceForVolume(), flags);
+ }
+
+ private void setVolumeIndex(int index, int device, int flags) {
+ // Set the volume index
+ setVolumeIndexInt(index, device, flags);
+
+ // Update local cache
+ mIndexMap.put(device, index);
+
+ // update data base - post a persist volume group msg
+ sendMsg(mAudioHandler,
+ MSG_PERSIST_VOLUME_GROUP,
+ SENDMSG_QUEUE,
+ device,
+ 0,
+ this,
+ PERSIST_DELAY);
+ }
+
+ private void setVolumeIndexInt(int index, int device, int flags) {
+ // Set the volume index
+ AudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
+ }
+
+ public int getIndex(int device) {
+ synchronized (VolumeGroupState.class) {
+ int index = mIndexMap.get(device, -1);
+ // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
+ return (index != -1) ? index : mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
+ }
+ }
+
+ public boolean hasIndexForDevice(int device) {
+ synchronized (VolumeGroupState.class) {
+ return (mIndexMap.get(device, -1) != -1);
+ }
+ }
+
+ public int getMaxIndex() {
+ return mIndexMax;
+ }
+
+ public int getMinIndex() {
+ return mIndexMin;
+ }
+
+ public void applyAllVolumes() {
+ synchronized (VolumeGroupState.class) {
+ if (mLegacyStreamType != AudioSystem.STREAM_DEFAULT) {
+ // No-op to avoid regression with stream based volume management
+ return;
+ }
+ // apply device specific volumes first
+ int index;
+ for (int i = 0; i < mIndexMap.size(); i++) {
+ final int device = mIndexMap.keyAt(i);
+ if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
+ index = mIndexMap.valueAt(i);
+ Log.v(TAG, "applyAllVolumes: restore index " + index + " for group "
+ + mAudioVolumeGroup.name() + " and device "
+ + AudioSystem.getOutputDeviceName(device));
+ setVolumeIndexInt(index, device, 0 /*flags*/);
+ }
+ }
+ // apply default volume last: by convention , default device volume will be used
+ index = getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
+ Log.v(TAG, "applyAllVolumes: restore default device index " + index + " for group "
+ + mAudioVolumeGroup.name());
+ setVolumeIndexInt(index, AudioSystem.DEVICE_OUT_DEFAULT, 0 /*flags*/);
+ }
+ }
+
+ private void persistVolumeGroup(int device) {
+ if (mUseFixedVolume) {
+ return;
+ }
+ Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group "
+ + mAudioVolumeGroup.name() + " and device "
+ + AudioSystem.getOutputDeviceName(device));
+ boolean success = Settings.System.putIntForUser(mContentResolver,
+ getSettingNameForDevice(device),
+ getIndex(device),
+ UserHandle.USER_CURRENT);
+ if (!success) {
+ Log.e(TAG, "persistVolumeGroup failed for group " + mAudioVolumeGroup.name());
+ }
+ }
+
+ public void readSettings() {
+ synchronized (VolumeGroupState.class) {
+ // First clear previously loaded (previous user?) settings
+ mIndexMap.clear();
+ // force maximum volume on all streams if fixed volume property is set
+ if (mUseFixedVolume) {
+ mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
+ return;
+ }
+ for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {
+ // retrieve current volume for device
+ // if no volume stored for current volume group and device, use default volume
+ // if default device, continue otherwise
+ int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT)
+ ? AudioSystem.DEFAULT_STREAM_VOLUME[mPublicStreamType] : -1;
+ int index;
+ String name = getSettingNameForDevice(device);
+ index = Settings.System.getIntForUser(
+ mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
+ if (index == -1) {
+ Log.e(TAG, "readSettings: No index stored for group "
+ + mAudioVolumeGroup.name() + ", device " + name);
+ continue;
+ }
+ Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
+ + " for group " + mAudioVolumeGroup.name() + ", device: " + name);
+ mIndexMap.put(device, getValidIndex(index));
+ }
+ }
+ }
+
+ private int getValidIndex(int index) {
+ if (index < mIndexMin) {
+ return mIndexMin;
+ } else if (mUseFixedVolume || index > mIndexMax) {
+ return mIndexMax;
+ }
+ return index;
+ }
+
+ public @NonNull String getSettingNameForDevice(int device) {
+ final String suffix = AudioSystem.getOutputDeviceName(device);
+ if (suffix.isEmpty()) {
+ return mAudioVolumeGroup.name();
+ }
+ return mAudioVolumeGroup.name() + "_" + AudioSystem.getOutputDeviceName(device);
+ }
+
+ private void dump(PrintWriter pw) {
+ pw.println("- VOLUME GROUP " + mAudioVolumeGroup.name() + ":");
+ pw.print(" Min: ");
+ pw.println(mIndexMin);
+ pw.print(" Max: ");
+ pw.println(mIndexMax);
+ pw.print(" Current: ");
+ for (int i = 0; i < mIndexMap.size(); i++) {
+ if (i > 0) {
+ pw.print(", ");
+ }
+ final int device = mIndexMap.keyAt(i);
+ pw.print(Integer.toHexString(device));
+ final String deviceName = device == AudioSystem.DEVICE_OUT_DEFAULT ? "default"
+ : AudioSystem.getOutputDeviceName(device);
+ if (!deviceName.isEmpty()) {
+ pw.print(" (");
+ pw.print(deviceName);
+ pw.print(")");
+ }
+ pw.print(": ");
+ pw.print(mIndexMap.valueAt(i));
+ }
+ pw.println();
+ pw.print(" Devices: ");
+ int n = 0;
+ final int devices = getDeviceForVolume();
+ for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {
+ if ((devices & device) == device) {
+ if (n++ > 0) {
+ pw.print(", ");
+ }
+ pw.print(AudioSystem.getOutputDeviceName(device));
+ }
+ }
+ }
+ }
+
// NOTE: Locking order for synchronized objects related to volume or ringer mode management:
// 1 mScoclient OR mSafeMediaVolumeState
@@ -5298,6 +5641,11 @@
persistVolume((VolumeStreamState) msg.obj, msg.arg1);
break;
+ case MSG_PERSIST_VOLUME_GROUP:
+ final VolumeGroupState vgs = (VolumeGroupState) msg.obj;
+ vgs.persistVolumeGroup(msg.arg1);
+ break;
+
case MSG_PERSIST_RINGER_MODE:
// note that the value persisted is the current ringer mode, not the
// value of ringer mode as of the time the request was made to persist
@@ -6312,7 +6660,7 @@
static final int LOG_NB_EVENTS_DYN_POLICY = 10;
final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE,
- "phone state (logged after successfull call to AudioSystem.setPhoneState(int))");
+ "phone state (logged after successful call to AudioSystem.setPhoneState(int, int))");
// logs for wired + A2DP device connections:
// - wired: logged before onSetWiredDeviceConnectionState() is executed
@@ -6395,6 +6743,7 @@
}
mMediaFocusControl.dump(pw);
dumpStreamStates(pw);
+ dumpVolumeGroups(pw);
dumpRingerMode(pw);
pw.println("\nAudio routes:");
pw.print(" mMainType=0x"); pw.println(Integer.toHexString(
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index fcd8701..add620e 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -16,6 +16,7 @@
package com.android.server.audio;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -97,12 +98,15 @@
static final int VOL_ADJUST_VOL_UID = 5;
static final int VOL_VOICE_ACTIVITY_HEARING_AID = 6;
static final int VOL_MODE_CHANGE_HEARING_AID = 7;
+ static final int VOL_SET_GROUP_VOL = 8;
final int mOp;
final int mStream;
final int mVal1;
final int mVal2;
final String mCaller;
+ final String mGroupName;
+ final AudioAttributes mAudioAttributes;
/** used for VOL_ADJUST_VOL_UID,
* VOL_ADJUST_SUGG_VOL,
@@ -114,6 +118,8 @@
mVal1 = val1;
mVal2 = val2;
mCaller = caller;
+ mGroupName = null;
+ mAudioAttributes = null;
}
/** used for VOL_SET_HEARING_AID_VOL*/
@@ -124,6 +130,8 @@
// unused
mStream = -1;
mCaller = null;
+ mGroupName = null;
+ mAudioAttributes = null;
}
/** used for VOL_SET_AVRCP_VOL */
@@ -134,6 +142,8 @@
mVal2 = 0;
mStream = -1;
mCaller = null;
+ mGroupName = null;
+ mAudioAttributes = null;
}
/** used for VOL_VOICE_ACTIVITY_HEARING_AID */
@@ -144,6 +154,8 @@
mVal2 = voiceActive ? 1 : 0;
// unused
mCaller = null;
+ mGroupName = null;
+ mAudioAttributes = null;
}
/** used for VOL_MODE_CHANGE_HEARING_AID */
@@ -154,6 +166,19 @@
mVal2 = mode;
// unused
mCaller = null;
+ mGroupName = null;
+ mAudioAttributes = null;
+ }
+
+ /** used for VOL_SET_GROUP_VOL */
+ VolumeEvent(int op, AudioAttributes aa, String group, int index, int flags, String caller) {
+ mOp = op;
+ mStream = -1;
+ mVal1 = index;
+ mVal2 = flags;
+ mCaller = caller;
+ mGroupName = group;
+ mAudioAttributes = aa;
}
@Override
@@ -208,6 +233,14 @@
.append(") causes setting HEARING_AID volume to idx:").append(mVal1)
.append(" stream:").append(AudioSystem.streamToString(mStream))
.toString();
+ case VOL_SET_GROUP_VOL:
+ return new StringBuilder("setVolumeIndexForAttributes(attr:")
+ .append(mAudioAttributes.toString())
+ .append(" group: ").append(mGroupName)
+ .append(" index:").append(mVal1)
+ .append(" flags:0x").append(Integer.toHexString(mVal2))
+ .append(") from ").append(mCaller)
+ .toString();
default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index c9c2c96..204f072 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -153,7 +153,12 @@
// Only allow internal clients to enable non-public options.
if (bundle.getBoolean(BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS)
- || bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)) {
+ || bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)
+ || bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_TITLE) != null
+ || bundle.getCharSequence(
+ BiometricPrompt.KEY_DEVICE_CREDENTIAL_SUBTITLE) != null
+ || bundle.getCharSequence(
+ BiometricPrompt.KEY_DEVICE_CREDENTIAL_DESCRIPTION) != null) {
checkInternalPermission();
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index cb88c4e..1a68f1b 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -48,8 +48,12 @@
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
+import android.net.Ikev2VpnProfile;
import android.net.IpPrefix;
import android.net.IpSecManager;
+import android.net.IpSecManager.IpSecTunnelInterface;
+import android.net.IpSecManager.UdpEncapsulationSocket;
+import android.net.IpSecTransform;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LocalSocket;
@@ -65,6 +69,12 @@
import android.net.UidRange;
import android.net.VpnManager;
import android.net.VpnService;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.ChildSessionConfiguration;
+import android.net.ipsec.ike.ChildSessionParams;
+import android.net.ipsec.ike.IkeSession;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionParams;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -113,6 +123,7 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -122,6 +133,9 @@
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -176,14 +190,14 @@
private final Context mContext;
private final NetworkInfo mNetworkInfo;
- private String mPackage;
+ @VisibleForTesting protected String mPackage;
private int mOwnerUID;
private boolean mIsPackageTargetingAtLeastQ;
private String mInterface;
private Connection mConnection;
/** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */
- private VpnRunner mVpnRunner;
+ @VisibleForTesting protected VpnRunner mVpnRunner;
private PendingIntent mStatusIntent;
private volatile boolean mEnableTeardown = true;
@@ -196,6 +210,7 @@
@VisibleForTesting
protected final NetworkCapabilities mNetworkCapabilities;
private final SystemServices mSystemServices;
+ private final Ikev2SessionCreator mIkev2SessionCreator;
/**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
@@ -238,17 +253,20 @@
public Vpn(Looper looper, Context context, INetworkManagementService netService,
@UserIdInt int userHandle) {
- this(looper, context, netService, userHandle, new SystemServices(context));
+ this(looper, context, netService, userHandle,
+ new SystemServices(context), new Ikev2SessionCreator());
}
@VisibleForTesting
protected Vpn(Looper looper, Context context, INetworkManagementService netService,
- int userHandle, SystemServices systemServices) {
+ int userHandle, SystemServices systemServices,
+ Ikev2SessionCreator ikev2SessionCreator) {
mContext = context;
mNetd = netService;
mUserHandle = userHandle;
mLooper = looper;
mSystemServices = systemServices;
+ mIkev2SessionCreator = ikev2SessionCreator;
mPackage = VpnConfig.LEGACY_VPN;
mOwnerUID = getAppUid(mPackage, mUserHandle);
@@ -749,8 +767,9 @@
private boolean isCurrentPreparedPackage(String packageName) {
// We can't just check that packageName matches mPackage, because if the app was uninstalled
- // and reinstalled it will no longer be prepared. Instead check the UID.
- return getAppUid(packageName, mUserHandle) == mOwnerUID;
+ // and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the
+ // calling package may not be the same as the prepared package. Check both UID and package.
+ return getAppUid(packageName, mUserHandle) == mOwnerUID && mPackage.equals(packageName);
}
/** Prepare the VPN for the given package. Does not perform permission checks. */
@@ -979,7 +998,11 @@
}
lp.setDomains(buffer.toString().trim());
- // TODO: Stop setting the MTU in jniCreate and set it here.
+ if (mConfig.mtu > 0) {
+ lp.setMtu(mConfig.mtu);
+ }
+
+ // TODO: Stop setting the MTU in jniCreate
return lp;
}
@@ -2004,30 +2027,369 @@
protected abstract void exit();
}
- private class IkeV2VpnRunner extends VpnRunner {
- private static final String TAG = "IkeV2VpnRunner";
+ interface IkeV2VpnRunnerCallback {
+ void onDefaultNetworkChanged(@NonNull Network network);
- private final IpSecManager mIpSecManager;
- private final VpnProfile mProfile;
+ void onChildOpened(
+ @NonNull Network network, @NonNull ChildSessionConfiguration childConfig);
- IkeV2VpnRunner(VpnProfile profile) {
+ void onChildTransformCreated(
+ @NonNull Network network, @NonNull IpSecTransform transform, int direction);
+
+ void onSessionLost(@NonNull Network network);
+ }
+
+ /**
+ * Internal class managing IKEv2/IPsec VPN connectivity
+ *
+ * <p>The IKEv2 VPN will listen to, and run based on the lifecycle of Android's default Network.
+ * As a new default is selected, old IKE sessions will be torn down, and a new one will be
+ * started.
+ *
+ * <p>This class uses locking minimally - the Vpn instance lock is only ever held when fields of
+ * the outer class are modified. As such, care must be taken to ensure that no calls are added
+ * that might modify the outer class' state without acquiring a lock.
+ *
+ * <p>The overall structure of the Ikev2VpnRunner is as follows:
+ *
+ * <ol>
+ * <li>Upon startup, a NetworkRequest is registered with ConnectivityManager. This is called
+ * any time a new default network is selected
+ * <li>When a new default is connected, an IKE session is started on that Network. If there
+ * were any existing IKE sessions on other Networks, they are torn down before starting
+ * the new IKE session
+ * <li>Upon establishment, the onChildTransformCreated() callback is called twice, one for
+ * each direction, and finally onChildOpened() is called
+ * <li>Upon the onChildOpened() call, the VPN is fully set up.
+ * <li>Subsequent Network changes result in new onDefaultNetworkChanged() callbacks. See (2).
+ * </ol>
+ */
+ class IkeV2VpnRunner extends VpnRunner implements IkeV2VpnRunnerCallback {
+ @NonNull private static final String TAG = "IkeV2VpnRunner";
+
+ @NonNull private final IpSecManager mIpSecManager;
+ @NonNull private final Ikev2VpnProfile mProfile;
+ @NonNull private final ConnectivityManager.NetworkCallback mNetworkCallback;
+
+ /**
+ * Executor upon which ALL callbacks must be run.
+ *
+ * <p>This executor MUST be a single threaded executor, in order to ensure the consistency
+ * of the mutable Ikev2VpnRunner fields. The Ikev2VpnRunner is built mostly lock-free by
+ * virtue of everything being serialized on this executor.
+ */
+ @NonNull private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+
+ /** Signal to ensure shutdown is honored even if a new Network is connected. */
+ private boolean mIsRunning = true;
+
+ @Nullable private UdpEncapsulationSocket mEncapSocket;
+ @Nullable private IpSecTunnelInterface mTunnelIface;
+ @Nullable private IkeSession mSession;
+ @Nullable private Network mActiveNetwork;
+
+ IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) {
super(TAG);
mProfile = profile;
-
- // TODO: move this to startVpnRunnerPrivileged()
- mConfig = new VpnConfig();
- mIpSecManager = mContext.getSystemService(IpSecManager.class);
+ mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
+ mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this);
}
@Override
public void run() {
- // TODO: Build IKE config, start IKE session
+ // Explicitly use only the network that ConnectivityService thinks is the "best." In
+ // other words, only ever use the currently selected default network. This does mean
+ // that in both onLost() and onConnected(), any old sessions MUST be torn down. This
+ // does NOT include VPNs.
+ final ConnectivityManager cm = ConnectivityManager.from(mContext);
+ cm.requestNetwork(cm.getDefaultRequest(), mNetworkCallback);
+ }
+
+ private boolean isActiveNetwork(@Nullable Network network) {
+ return Objects.equals(mActiveNetwork, network) && mIsRunning;
+ }
+
+ /**
+ * Called when an IKE Child session has been opened, signalling completion of the startup.
+ *
+ * <p>This method is only ever called once per IkeSession, and MUST run on the mExecutor
+ * thread in order to ensure consistency of the Ikev2VpnRunner fields.
+ */
+ public void onChildOpened(
+ @NonNull Network network, @NonNull ChildSessionConfiguration childConfig) {
+ if (!isActiveNetwork(network)) {
+ Log.d(TAG, "onOpened called for obsolete network " + network);
+
+ // Do nothing; this signals that either: (1) a new/better Network was found,
+ // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
+ // IKE session was already shut down (exited, or an error was encountered somewhere
+ // else). In both cases, all resources and sessions are torn down via
+ // resetIkeState().
+ return;
+ }
+
+ try {
+ final String interfaceName = mTunnelIface.getInterfaceName();
+ final int maxMtu = mProfile.getMaxMtu();
+ final List<LinkAddress> internalAddresses = childConfig.getInternalAddresses();
+
+ final Collection<RouteInfo> newRoutes = VpnIkev2Utils.getRoutesFromTrafficSelectors(
+ childConfig.getOutboundTrafficSelectors());
+ for (final LinkAddress address : internalAddresses) {
+ mTunnelIface.addAddress(address.getAddress(), address.getPrefixLength());
+ }
+
+ final NetworkAgent networkAgent;
+ final LinkProperties lp;
+
+ synchronized (Vpn.this) {
+ mInterface = interfaceName;
+ mConfig.mtu = maxMtu;
+ mConfig.interfaze = mInterface;
+
+ mConfig.addresses.clear();
+ mConfig.addresses.addAll(internalAddresses);
+
+ mConfig.routes.clear();
+ mConfig.routes.addAll(newRoutes);
+
+ // TODO: Add DNS servers from negotiation
+
+ networkAgent = mNetworkAgent;
+
+ // The below must be done atomically with the mConfig update, otherwise
+ // isRunningLocked() will be racy.
+ if (networkAgent == null) {
+ if (isSettingsVpnLocked()) {
+ prepareStatusIntent();
+ }
+ agentConnect();
+ return; // Link properties are already sent.
+ }
+
+ lp = makeLinkProperties(); // Accesses VPN instance fields; must be locked
+ }
+
+ networkAgent.sendLinkProperties(lp);
+ } catch (Exception e) {
+ Log.d(TAG, "Error in ChildOpened for network " + network, e);
+ onSessionLost(network);
+ }
+ }
+
+ /**
+ * Called when an IPsec transform has been created, and should be applied.
+ *
+ * <p>This method is called multiple times over the lifetime of an IkeSession (or default
+ * network), and is MUST always be called on the mExecutor thread in order to ensure
+ * consistency of the Ikev2VpnRunner fields.
+ */
+ public void onChildTransformCreated(
+ @NonNull Network network, @NonNull IpSecTransform transform, int direction) {
+ if (!isActiveNetwork(network)) {
+ Log.d(TAG, "ChildTransformCreated for obsolete network " + network);
+
+ // Do nothing; this signals that either: (1) a new/better Network was found,
+ // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
+ // IKE session was already shut down (exited, or an error was encountered somewhere
+ // else). In both cases, all resources and sessions are torn down via
+ // resetIkeState().
+ return;
+ }
+
+ try {
+ // Transforms do not need to be persisted; the IkeSession will keep
+ // them alive for us
+ mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform);
+ } catch (IOException e) {
+ Log.d(TAG, "Transform application failed for network " + network, e);
+ onSessionLost(network);
+ }
+ }
+
+ /**
+ * Called when a new default network is connected.
+ *
+ * <p>The Ikev2VpnRunner will unconditionally switch to the new network, killing the old IKE
+ * state in the process, and starting a new IkeSession instance.
+ *
+ * <p>This method is called multiple times over the lifetime of the Ikev2VpnRunner, and is
+ * called on the ConnectivityService thread. Thus, the actual work MUST be proxied to the
+ * mExecutor thread in order to ensure consistency of the Ikev2VpnRunner fields.
+ */
+ public void onDefaultNetworkChanged(@NonNull Network network) {
+ Log.d(TAG, "Starting IKEv2/IPsec session on new network: " + network);
+
+ // Proxy to the Ikev2VpnRunner (single-thread) executor to ensure consistency in lieu
+ // of locking.
+ mExecutor.execute(() -> {
+ try {
+ if (!mIsRunning) {
+ Log.d(TAG, "onDefaultNetworkChanged after exit");
+ return; // VPN has been shut down.
+ }
+
+ // Without MOBIKE, we have no way to seamlessly migrate. Close on old
+ // (non-default) network, and start the new one.
+ resetIkeState();
+ mActiveNetwork = network;
+
+ // TODO(b/149356682): Update this based on new IKE API
+ mEncapSocket = mIpSecManager.openUdpEncapsulationSocket();
+
+ // TODO(b/149356682): Update this based on new IKE API
+ final IkeSessionParams ikeSessionParams =
+ VpnIkev2Utils.buildIkeSessionParams(mProfile, mEncapSocket);
+ final ChildSessionParams childSessionParams =
+ VpnIkev2Utils.buildChildSessionParams();
+
+ // TODO: Remove the need for adding two unused addresses with
+ // IPsec tunnels.
+ mTunnelIface =
+ mIpSecManager.createIpSecTunnelInterface(
+ ikeSessionParams.getServerAddress() /* unused */,
+ ikeSessionParams.getServerAddress() /* unused */,
+ network);
+ mNetd.setInterfaceUp(mTunnelIface.getInterfaceName());
+
+ // Socket must be bound to prevent network switches from causing
+ // the IKE teardown to fail/timeout.
+ // TODO(b/149356682): Update this based on new IKE API
+ network.bindSocket(mEncapSocket.getFileDescriptor());
+
+ mSession = mIkev2SessionCreator.createIkeSession(
+ mContext,
+ ikeSessionParams,
+ childSessionParams,
+ mExecutor,
+ new VpnIkev2Utils.IkeSessionCallbackImpl(
+ TAG, IkeV2VpnRunner.this, network),
+ new VpnIkev2Utils.ChildSessionCallbackImpl(
+ TAG, IkeV2VpnRunner.this, network));
+ Log.d(TAG, "Ike Session started for network " + network);
+ } catch (Exception e) {
+ Log.i(TAG, "Setup failed for network " + network + ". Aborting", e);
+ onSessionLost(network);
+ }
+ });
+ }
+
+ /**
+ * Handles loss of a session
+ *
+ * <p>The loss of a session might be due to an onLost() call, the IKE session getting torn
+ * down for any reason, or an error in updating state (transform application, VPN setup)
+ *
+ * <p>This method MUST always be called on the mExecutor thread in order to ensure
+ * consistency of the Ikev2VpnRunner fields.
+ */
+ public void onSessionLost(@NonNull Network network) {
+ if (!isActiveNetwork(network)) {
+ Log.d(TAG, "onSessionLost() called for obsolete network " + network);
+
+ // Do nothing; this signals that either: (1) a new/better Network was found,
+ // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
+ // IKE session was already shut down (exited, or an error was encountered somewhere
+ // else). In both cases, all resources and sessions are torn down via
+ // onSessionLost() and resetIkeState().
+ return;
+ }
+
+ mActiveNetwork = null;
+
+ // Close all obsolete state, but keep VPN alive incase a usable network comes up.
+ // (Mirrors VpnService behavior)
+ Log.d(TAG, "Resetting state for network: " + network);
+
+ synchronized (Vpn.this) {
+ // Since this method handles non-fatal errors only, set mInterface to null to
+ // prevent the NetworkManagementEventObserver from killing this VPN based on the
+ // interface going down (which we expect).
+ mInterface = null;
+ mConfig.interfaze = null;
+
+ // Set as unroutable to prevent traffic leaking while the interface is down.
+ if (mConfig != null && mConfig.routes != null) {
+ final List<RouteInfo> oldRoutes = new ArrayList<>(mConfig.routes);
+
+ mConfig.routes.clear();
+ for (final RouteInfo route : oldRoutes) {
+ mConfig.routes.add(new RouteInfo(route.getDestination(), RTN_UNREACHABLE));
+ }
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendLinkProperties(makeLinkProperties());
+ }
+ }
+ }
+
+ resetIkeState();
+ }
+
+ /**
+ * Cleans up all IKE state
+ *
+ * <p>This method MUST always be called on the mExecutor thread in order to ensure
+ * consistency of the Ikev2VpnRunner fields.
+ */
+ private void resetIkeState() {
+ if (mTunnelIface != null) {
+ // No need to call setInterfaceDown(); the IpSecInterface is being fully torn down.
+ mTunnelIface.close();
+ mTunnelIface = null;
+ }
+ if (mSession != null) {
+ mSession.kill(); // Kill here to make sure all resources are released immediately
+ mSession = null;
+ }
+
+ // TODO(b/149356682): Update this based on new IKE API
+ if (mEncapSocket != null) {
+ try {
+ mEncapSocket.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close encap socket", e);
+ }
+ mEncapSocket = null;
+ }
+ }
+
+ /**
+ * Triggers cleanup of outer class' state
+ *
+ * <p>Can be called from any thread, as it does not mutate state in the Ikev2VpnRunner.
+ */
+ private void cleanupVpnState() {
+ synchronized (Vpn.this) {
+ agentDisconnect();
+ }
+ }
+
+ /**
+ * Cleans up all Ikev2VpnRunner internal state
+ *
+ * <p>This method MUST always be called on the mExecutor thread in order to ensure
+ * consistency of the Ikev2VpnRunner fields.
+ */
+ private void shutdownVpnRunner() {
+ mActiveNetwork = null;
+ mIsRunning = false;
+
+ resetIkeState();
+
+ final ConnectivityManager cm = ConnectivityManager.from(mContext);
+ cm.unregisterNetworkCallback(mNetworkCallback);
+
+ mExecutor.shutdown();
}
@Override
public void exit() {
- // TODO: Teardown IKE session & any resources.
- agentDisconnect();
+ // Cleanup outer class' state immediately, otherwise race conditions may ensue.
+ cleanupVpnState();
+
+ mExecutor.execute(() -> {
+ shutdownVpnRunner();
+ });
}
}
@@ -2488,12 +2850,46 @@
throw new IllegalArgumentException("No profile found for " + packageName);
}
- startVpnProfilePrivileged(profile);
+ startVpnProfilePrivileged(profile, packageName);
});
}
- private void startVpnProfilePrivileged(@NonNull VpnProfile profile) {
- // TODO: Start PlatformVpnRunner
+ private void startVpnProfilePrivileged(
+ @NonNull VpnProfile profile, @NonNull String packageName) {
+ // Ensure that no other previous instance is running.
+ if (mVpnRunner != null) {
+ mVpnRunner.exit();
+ mVpnRunner = null;
+ }
+ updateState(DetailedState.CONNECTING, "startPlatformVpn");
+
+ try {
+ // Build basic config
+ mConfig = new VpnConfig();
+ mConfig.user = packageName;
+ mConfig.isMetered = profile.isMetered;
+ mConfig.startTime = SystemClock.elapsedRealtime();
+ mConfig.proxyInfo = profile.proxy;
+
+ switch (profile.type) {
+ case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
+ case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+ case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+ mVpnRunner = new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile));
+ mVpnRunner.start();
+ break;
+ default:
+ updateState(DetailedState.FAILED, "Invalid platform VPN type");
+ Log.d(TAG, "Unknown VPN profile type: " + profile.type);
+ break;
+ }
+ } catch (IOException | GeneralSecurityException e) {
+ // Reset mConfig
+ mConfig = null;
+
+ updateState(DetailedState.FAILED, "VPN startup failed");
+ throw new IllegalArgumentException("VPN startup failed", e);
+ }
}
/**
@@ -2507,13 +2903,37 @@
public synchronized void stopVpnProfile(@NonNull String packageName) {
checkNotNull(packageName, "No package name provided");
- // To stop the VPN profile, the caller must be the current prepared package. Otherwise,
- // the app is not prepared, and we can just return.
- if (!isCurrentPreparedPackage(packageName)) {
- // TODO: Also check to make sure that the running VPN is a VPN profile.
+ // To stop the VPN profile, the caller must be the current prepared package and must be
+ // running an Ikev2VpnProfile.
+ if (!isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner) {
return;
}
prepareInternal(VpnConfig.LEGACY_VPN);
}
+
+ /**
+ * Proxy to allow testing
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static class Ikev2SessionCreator {
+ /** Creates a IKE session */
+ public IkeSession createIkeSession(
+ @NonNull Context context,
+ @NonNull IkeSessionParams ikeSessionParams,
+ @NonNull ChildSessionParams firstChildSessionParams,
+ @NonNull Executor userCbExecutor,
+ @NonNull IkeSessionCallback ikeSessionCallback,
+ @NonNull ChildSessionCallback firstChildSessionCallback) {
+ return new IkeSession(
+ context,
+ ikeSessionParams,
+ firstChildSessionParams,
+ userCbExecutor,
+ ikeSessionCallback,
+ firstChildSessionCallback);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
new file mode 100644
index 0000000..33fc32b
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -0,0 +1,390 @@
+/*
+ * 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.connectivity;
+
+import static android.net.ConnectivityManager.NetworkCallback;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_1024_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_192;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1;
+
+import android.annotation.NonNull;
+import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.IpSecManager.UdpEncapsulationSocket;
+import android.net.IpSecTransform;
+import android.net.Network;
+import android.net.RouteInfo;
+import android.net.eap.EapSessionConfig;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.ChildSessionConfiguration;
+import android.net.ipsec.ike.ChildSessionParams;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIdentification;
+import android.net.ipsec.ike.IkeIpv4AddrIdentification;
+import android.net.ipsec.ike.IkeIpv6AddrIdentification;
+import android.net.ipsec.ike.IkeKeyIdIdentification;
+import android.net.ipsec.ike.IkeRfc822AddrIdentification;
+import android.net.ipsec.ike.IkeSaProposal;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.net.util.IpRange;
+import android.system.OsConstants;
+import android.util.Log;
+
+import com.android.internal.net.VpnProfile;
+import com.android.internal.util.HexDump;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Utility class to build and convert IKEv2/IPsec parameters.
+ *
+ * @hide
+ */
+public class VpnIkev2Utils {
+ static IkeSessionParams buildIkeSessionParams(
+ @NonNull Ikev2VpnProfile profile, @NonNull UdpEncapsulationSocket socket) {
+ // TODO(b/149356682): Update this based on new IKE API. Only numeric addresses supported
+ // until then. All others throw IAE (caught by caller).
+ final InetAddress serverAddr = InetAddresses.parseNumericAddress(profile.getServerAddr());
+ final IkeIdentification localId = parseIkeIdentification(profile.getUserIdentity());
+ final IkeIdentification remoteId = parseIkeIdentification(profile.getServerAddr());
+
+ // TODO(b/149356682): Update this based on new IKE API.
+ final IkeSessionParams.Builder ikeOptionsBuilder =
+ new IkeSessionParams.Builder()
+ .setServerAddress(serverAddr)
+ .setUdpEncapsulationSocket(socket)
+ .setLocalIdentification(localId)
+ .setRemoteIdentification(remoteId);
+ setIkeAuth(profile, ikeOptionsBuilder);
+
+ for (final IkeSaProposal ikeProposal : getIkeSaProposals()) {
+ ikeOptionsBuilder.addSaProposal(ikeProposal);
+ }
+
+ return ikeOptionsBuilder.build();
+ }
+
+ static ChildSessionParams buildChildSessionParams() {
+ final TunnelModeChildSessionParams.Builder childOptionsBuilder =
+ new TunnelModeChildSessionParams.Builder();
+
+ for (final ChildSaProposal childProposal : getChildSaProposals()) {
+ childOptionsBuilder.addSaProposal(childProposal);
+ }
+
+ childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET);
+ childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET6);
+ childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET);
+ childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET6);
+
+ return childOptionsBuilder.build();
+ }
+
+ private static void setIkeAuth(
+ @NonNull Ikev2VpnProfile profile, @NonNull IkeSessionParams.Builder builder) {
+ switch (profile.getType()) {
+ case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
+ final EapSessionConfig eapConfig =
+ new EapSessionConfig.Builder()
+ .setEapMsChapV2Config(profile.getUsername(), profile.getPassword())
+ .build();
+ builder.setAuthEap(profile.getServerRootCaCert(), eapConfig);
+ break;
+ case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+ builder.setAuthPsk(profile.getPresharedKey());
+ break;
+ case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+ builder.setAuthDigitalSignature(
+ profile.getServerRootCaCert(),
+ profile.getUserCert(),
+ profile.getRsaPrivateKey());
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown auth method set");
+ }
+ }
+
+ private static List<IkeSaProposal> getIkeSaProposals() {
+ // TODO: filter this based on allowedAlgorithms
+ final List<IkeSaProposal> proposals = new ArrayList<>();
+
+ // Encryption Algorithms: Currently only AES_CBC is supported.
+ final IkeSaProposal.Builder normalModeBuilder = new IkeSaProposal.Builder();
+
+ // Currently only AES_CBC is supported.
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
+
+ // Authentication/Integrity Algorithms
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96);
+
+ // Add AEAD options
+ final IkeSaProposal.Builder aeadBuilder = new IkeSaProposal.Builder();
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
+
+ // Add dh, prf for both builders
+ for (final IkeSaProposal.Builder builder : Arrays.asList(normalModeBuilder, aeadBuilder)) {
+ builder.addDhGroup(DH_GROUP_2048_BIT_MODP);
+ builder.addDhGroup(DH_GROUP_1024_BIT_MODP);
+ builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC);
+ builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_HMAC_SHA1);
+ }
+
+ proposals.add(normalModeBuilder.build());
+ proposals.add(aeadBuilder.build());
+ return proposals;
+ }
+
+ private static List<ChildSaProposal> getChildSaProposals() {
+ // TODO: filter this based on allowedAlgorithms
+ final List<ChildSaProposal> proposals = new ArrayList<>();
+
+ // Add non-AEAD options
+ final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder();
+
+ // Encryption Algorithms: Currently only AES_CBC is supported.
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
+
+ // Authentication/Integrity Algorithms
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96);
+
+ // Add AEAD options
+ final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder();
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
+
+ proposals.add(normalModeBuilder.build());
+ proposals.add(aeadBuilder.build());
+ return proposals;
+ }
+
+ static class IkeSessionCallbackImpl implements IkeSessionCallback {
+ private final String mTag;
+ private final Vpn.IkeV2VpnRunnerCallback mCallback;
+ private final Network mNetwork;
+
+ IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) {
+ mTag = tag;
+ mCallback = callback;
+ mNetwork = network;
+ }
+
+ @Override
+ public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
+ Log.d(mTag, "IkeOpened for network " + mNetwork);
+ // Nothing to do here.
+ }
+
+ @Override
+ public void onClosed() {
+ Log.d(mTag, "IkeClosed for network " + mNetwork);
+ mCallback.onSessionLost(mNetwork); // Server requested session closure. Retry?
+ }
+
+ @Override
+ public void onClosedExceptionally(@NonNull IkeException exception) {
+ Log.d(mTag, "IkeClosedExceptionally for network " + mNetwork, exception);
+ mCallback.onSessionLost(mNetwork);
+ }
+
+ @Override
+ public void onError(@NonNull IkeProtocolException exception) {
+ Log.d(mTag, "IkeError for network " + mNetwork, exception);
+ // Non-fatal, log and continue.
+ }
+ }
+
+ static class ChildSessionCallbackImpl implements ChildSessionCallback {
+ private final String mTag;
+ private final Vpn.IkeV2VpnRunnerCallback mCallback;
+ private final Network mNetwork;
+
+ ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) {
+ mTag = tag;
+ mCallback = callback;
+ mNetwork = network;
+ }
+
+ @Override
+ public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
+ Log.d(mTag, "ChildOpened for network " + mNetwork);
+ mCallback.onChildOpened(mNetwork, childConfig);
+ }
+
+ @Override
+ public void onClosed() {
+ Log.d(mTag, "ChildClosed for network " + mNetwork);
+ mCallback.onSessionLost(mNetwork);
+ }
+
+ @Override
+ public void onClosedExceptionally(@NonNull IkeException exception) {
+ Log.d(mTag, "ChildClosedExceptionally for network " + mNetwork, exception);
+ mCallback.onSessionLost(mNetwork);
+ }
+
+ @Override
+ public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) {
+ Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; network " + mNetwork);
+ mCallback.onChildTransformCreated(mNetwork, transform, direction);
+ }
+
+ @Override
+ public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) {
+ // Nothing to be done; no references to the IpSecTransform are held by the
+ // Ikev2VpnRunner (or this callback class), and this transform will be closed by the
+ // IKE library.
+ Log.d(mTag,
+ "ChildTransformDeleted; Direction: " + direction + "; for network " + mNetwork);
+ }
+ }
+
+ static class Ikev2VpnNetworkCallback extends NetworkCallback {
+ private final String mTag;
+ private final Vpn.IkeV2VpnRunnerCallback mCallback;
+
+ Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback) {
+ mTag = tag;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onAvailable(@NonNull Network network) {
+ Log.d(mTag, "Starting IKEv2/IPsec session on new network: " + network);
+ mCallback.onDefaultNetworkChanged(network);
+ }
+
+ @Override
+ public void onLost(@NonNull Network network) {
+ Log.d(mTag, "Tearing down; lost network: " + network);
+ mCallback.onSessionLost(network);
+ }
+ }
+
+ /**
+ * Identity parsing logic using similar logic to open source implementations of IKEv2
+ *
+ * <p>This method does NOT support using type-prefixes (eg 'fqdn:' or 'keyid'), or ASN.1 encoded
+ * identities.
+ */
+ private static IkeIdentification parseIkeIdentification(@NonNull String identityStr) {
+ // TODO: Add identity formatting to public API javadocs.
+ if (identityStr.contains("@")) {
+ if (identityStr.startsWith("@#")) {
+ // KEY_ID
+ final String hexStr = identityStr.substring(2);
+ return new IkeKeyIdIdentification(HexDump.hexStringToByteArray(hexStr));
+ } else if (identityStr.startsWith("@@")) {
+ // RFC822 (USER_FQDN)
+ return new IkeRfc822AddrIdentification(identityStr.substring(2));
+ } else if (identityStr.startsWith("@")) {
+ // FQDN
+ return new IkeFqdnIdentification(identityStr.substring(1));
+ } else {
+ // RFC822 (USER_FQDN)
+ return new IkeRfc822AddrIdentification(identityStr);
+ }
+ } else if (InetAddresses.isNumericAddress(identityStr)) {
+ final InetAddress addr = InetAddresses.parseNumericAddress(identityStr);
+ if (addr instanceof Inet4Address) {
+ // IPv4
+ return new IkeIpv4AddrIdentification((Inet4Address) addr);
+ } else if (addr instanceof Inet6Address) {
+ // IPv6
+ return new IkeIpv6AddrIdentification((Inet6Address) addr);
+ } else {
+ throw new IllegalArgumentException("IP version not supported");
+ }
+ } else {
+ if (identityStr.contains(":")) {
+ // KEY_ID
+ return new IkeKeyIdIdentification(identityStr.getBytes());
+ } else {
+ // FQDN
+ return new IkeFqdnIdentification(identityStr);
+ }
+ }
+ }
+
+ static Collection<RouteInfo> getRoutesFromTrafficSelectors(
+ List<IkeTrafficSelector> trafficSelectors) {
+ final HashSet<RouteInfo> routes = new HashSet<>();
+
+ for (final IkeTrafficSelector selector : trafficSelectors) {
+ for (final IpPrefix prefix :
+ new IpRange(selector.startingAddress, selector.endingAddress).asIpPrefixes()) {
+ routes.add(new RouteInfo(prefix, null));
+ }
+ }
+
+ return routes;
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 7c2ec78..d9e3025 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -105,32 +105,57 @@
*/
public class HdmiControlService extends SystemService {
private static final String TAG = "HdmiControlService";
- private final Locale HONG_KONG = new Locale("zh", "HK");
- private final Locale MACAU = new Locale("zh", "MO");
+ private static final Locale HONG_KONG = new Locale("zh", "HK");
+ private static final Locale MACAU = new Locale("zh", "MO");
- private static final Map<String, String> mTerminologyToBibliographicMap;
- static {
- mTerminologyToBibliographicMap = new HashMap<>();
+ private static final Map<String, String> sTerminologyToBibliographicMap =
+ createsTerminologyToBibliographicMap();
+
+ private static Map<String, String> createsTerminologyToBibliographicMap() {
+ Map<String, String> temp = new HashMap<>();
// NOTE: (TERMINOLOGY_CODE, BIBLIOGRAPHIC_CODE)
- mTerminologyToBibliographicMap.put("sqi", "alb"); // Albanian
- mTerminologyToBibliographicMap.put("hye", "arm"); // Armenian
- mTerminologyToBibliographicMap.put("eus", "baq"); // Basque
- mTerminologyToBibliographicMap.put("mya", "bur"); // Burmese
- mTerminologyToBibliographicMap.put("ces", "cze"); // Czech
- mTerminologyToBibliographicMap.put("nld", "dut"); // Dutch
- mTerminologyToBibliographicMap.put("kat", "geo"); // Georgian
- mTerminologyToBibliographicMap.put("deu", "ger"); // German
- mTerminologyToBibliographicMap.put("ell", "gre"); // Greek
- mTerminologyToBibliographicMap.put("fra", "fre"); // French
- mTerminologyToBibliographicMap.put("isl", "ice"); // Icelandic
- mTerminologyToBibliographicMap.put("mkd", "mac"); // Macedonian
- mTerminologyToBibliographicMap.put("mri", "mao"); // Maori
- mTerminologyToBibliographicMap.put("msa", "may"); // Malay
- mTerminologyToBibliographicMap.put("fas", "per"); // Persian
- mTerminologyToBibliographicMap.put("ron", "rum"); // Romanian
- mTerminologyToBibliographicMap.put("slk", "slo"); // Slovak
- mTerminologyToBibliographicMap.put("bod", "tib"); // Tibetan
- mTerminologyToBibliographicMap.put("cym", "wel"); // Welsh
+ temp.put("sqi", "alb"); // Albanian
+ temp.put("hye", "arm"); // Armenian
+ temp.put("eus", "baq"); // Basque
+ temp.put("mya", "bur"); // Burmese
+ temp.put("ces", "cze"); // Czech
+ temp.put("nld", "dut"); // Dutch
+ temp.put("kat", "geo"); // Georgian
+ temp.put("deu", "ger"); // German
+ temp.put("ell", "gre"); // Greek
+ temp.put("fra", "fre"); // French
+ temp.put("isl", "ice"); // Icelandic
+ temp.put("mkd", "mac"); // Macedonian
+ temp.put("mri", "mao"); // Maori
+ temp.put("msa", "may"); // Malay
+ temp.put("fas", "per"); // Persian
+ temp.put("ron", "rum"); // Romanian
+ temp.put("slk", "slo"); // Slovak
+ temp.put("bod", "tib"); // Tibetan
+ temp.put("cym", "wel"); // Welsh
+ return Collections.unmodifiableMap(temp);
+ }
+
+ @VisibleForTesting static String localeToMenuLanguage(Locale locale) {
+ if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) {
+ // Android always returns "zho" for all Chinese variants.
+ // Use "bibliographic" code defined in CEC639-2 for traditional
+ // Chinese used in Taiwan/Hong Kong/Macau.
+ return "chi";
+ } else {
+ String language = locale.getISO3Language();
+
+ // locale.getISO3Language() returns terminology code and need to
+ // send it as bibliographic code instead since the Bibliographic
+ // codes of ISO/FDIS 639-2 shall be used.
+ // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi"
+ // But, as it depends on the locale, is not handled here.
+ if (sTerminologyToBibliographicMap.containsKey(language)) {
+ language = sTerminologyToBibliographicMap.get(language);
+ }
+
+ return language;
+ }
}
static final String PERMISSION = "android.permission.HDMI_CEC";
@@ -208,8 +233,8 @@
}
break;
case Intent.ACTION_CONFIGURATION_CHANGED:
- String language = getMenuLanguage();
- if (!mLanguage.equals(language)) {
+ String language = HdmiControlService.localeToMenuLanguage(Locale.getDefault());
+ if (!mMenuLanguage.equals(language)) {
onLanguageChanged(language);
}
break;
@@ -221,28 +246,6 @@
}
}
- private String getMenuLanguage() {
- Locale locale = Locale.getDefault();
- if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) {
- // Android always returns "zho" for all Chinese variants.
- // Use "bibliographic" code defined in CEC639-2 for traditional
- // Chinese used in Taiwan/Hong Kong/Macau.
- return "chi";
- } else {
- String language = locale.getISO3Language();
-
- // locale.getISO3Language() returns terminology code and need to
- // send it as bibliographic code instead since the Bibliographic
- // codes of ISO/FDIS 639-2 shall be used.
- // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi"
- // But, as it depends on the locale, is not handled here.
- if (mTerminologyToBibliographicMap.containsKey(language)) {
- language = mTerminologyToBibliographicMap.get(language);
- }
-
- return language;
- }
- }
}
// A thread to handle synchronous IO of CEC and MHL control service.
@@ -339,7 +342,7 @@
private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
@ServiceThreadOnly
- private String mLanguage = Locale.getDefault().getISO3Language();
+ private String mMenuLanguage = localeToMenuLanguage(Locale.getDefault());
@ServiceThreadOnly
private boolean mStandbyMessageReceived = false;
@@ -759,7 +762,7 @@
private void initializeCec(int initiatedBy) {
mAddressAllocated = false;
mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
- mCecController.setLanguage(mLanguage);
+ mCecController.setLanguage(mMenuLanguage);
initializeLocalDevices(initiatedBy);
}
@@ -2818,7 +2821,7 @@
@ServiceThreadOnly
private void onLanguageChanged(String language) {
assertRunOnServiceThread();
- mLanguage = language;
+ mMenuLanguage = language;
if (isTvDeviceEnabled()) {
tv().broadcastMenuLanguage(language);
@@ -2826,10 +2829,19 @@
}
}
+ /**
+ * Gets the CEC menu language.
+ *
+ * <p>This is the ISO/FDIS 639-2 3 letter language code sent in the CEC message @{code <Set Menu
+ * Language>}.
+ * See HDMI 1.4b section CEC 13.6.2
+ *
+ * @see {@link Locale#getISO3Language()}
+ */
@ServiceThreadOnly
String getLanguage() {
assertRunOnServiceThread();
- return mLanguage;
+ return mMenuLanguage;
}
private void disableDevices(PendingActionClearedCallback callback) {
@@ -2838,7 +2850,6 @@
device.disableDevice(mStandbyMessageReceived, callback);
}
}
-
mMhlController.clearAllLocalDevices();
}
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
new file mode 100644
index 0000000..25ef9fa
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -0,0 +1,6 @@
+set noparent
+
+ogunwale@google.com
+yukawa@google.com
+tarandeep@google.com
+lumark@google.com
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 18383c4..ae11c70 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -257,12 +257,12 @@
builder.addSelectedRoute(mSelectedRouteId);
if (!TextUtils.isEmpty(activeBtDeviceAddress)) {
- builder.addTransferrableRoute(mDefaultRoute.getId());
+ builder.addTransferableRoute(mDefaultRoute.getId());
}
for (MediaRoute2Info route : mBluetoothRoutes) {
if (!TextUtils.equals(mSelectedRouteId, route.getId())) {
- builder.addTransferrableRoute(route.getId());
+ builder.addTransferableRoute(route.getId());
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 916b63b..d0d0f5a 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -932,7 +932,7 @@
StatusBarNotification sbn = r.getSbn();
cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
sbn.getId(), Notification.FLAG_AUTO_CANCEL,
- FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
+ FLAG_FOREGROUND_SERVICE | FLAG_BUBBLE, false, r.getUserId(),
REASON_CLICK, nv.rank, nv.count, null);
nv.recycle();
reportUserInteraction(r);
@@ -1055,7 +1055,7 @@
if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
reportSeen(r);
}
- r.setVisibility(true, nv.rank, nv.count);
+ r.setVisibility(true, nv.rank, nv.count, mNotificationRecordLogger);
mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), true);
boolean isHun = (nv.location
== NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP);
@@ -1074,7 +1074,7 @@
for (NotificationVisibility nv : noLongerVisibleKeys) {
NotificationRecord r = mNotificationsByKey.get(nv.key);
if (r == null) continue;
- r.setVisibility(false, nv.rank, nv.count);
+ r.setVisibility(false, nv.rank, nv.count, mNotificationRecordLogger);
mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), false);
nv.recycle();
}
@@ -3979,6 +3979,29 @@
}
/**
+ * Allows the notification assistant to un-snooze a single notification.
+ *
+ * @param token The binder for the listener, to check that the caller is allowed
+ */
+ @Override
+ public void unsnoozeNotificationFromSystemListener(INotificationListener token,
+ String key) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mNotificationLock) {
+ final ManagedServiceInfo info =
+ mListeners.checkServiceTokenLocked(token);
+ if (!info.isSystem) {
+ throw new SecurityException("Not allowed to unsnooze before deadline");
+ }
+ unsnoozeNotificationInt(key, info);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Allow an INotificationListener to simulate clearing (dismissing) a single notification.
*
* {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
@@ -6468,7 +6491,7 @@
mUsageStats.registerPostedByApp(r);
r.setInterruptive(isVisuallyInterruptive(null, r));
} else {
- old = mNotificationList.get(index);
+ old = mNotificationList.get(index); // Potentially *changes* old
mNotificationList.set(index, r);
mUsageStats.registerUpdatedByApp(r, old);
// Make sure we don't lose the foreground service state.
@@ -6537,7 +6560,7 @@
maybeRecordInterruptionLocked(r);
// Log event to statsd
- mNotificationRecordLogger.logNotificationReported(r, old, position,
+ mNotificationRecordLogger.maybeLogNotificationPosted(r, old, position,
buzzBeepBlinkLoggingCode);
} finally {
int N = mEnqueuedNotifications.size();
@@ -7986,7 +8009,8 @@
FlagChecker flagChecker = (int flags) -> {
int flagsToCheck = FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
- if (REASON_LISTENER_CANCEL_ALL == reason) {
+ if (REASON_LISTENER_CANCEL_ALL == reason
+ || REASON_CANCEL_ALL == reason) {
flagsToCheck |= FLAG_BUBBLE;
}
if ((flags & flagsToCheck) != 0) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 4785da9..0ada58e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -909,7 +909,8 @@
/**
* Set the visibility of the notification.
*/
- public void setVisibility(boolean visible, int rank, int count) {
+ public void setVisibility(boolean visible, int rank, int count,
+ NotificationRecordLogger notificationRecordLogger) {
final long now = System.currentTimeMillis();
mVisibleSinceMs = visible ? now : mVisibleSinceMs;
stats.onVisibilityChanged(visible);
@@ -927,6 +928,7 @@
getFreshnessMs(now),
0, // exposure time
rank);
+ notificationRecordLogger.logNotificationVisibility(this, visible);
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 2f78542..eaca066f 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -41,13 +41,14 @@
public interface NotificationRecordLogger {
/**
- * Logs a NotificationReported atom reflecting the posting or update of a notification.
+ * May log a NotificationReported atom reflecting the posting or update of a notification.
* @param r The new NotificationRecord. If null, no action is taken.
* @param old The previous NotificationRecord. Null if there was no previous record.
* @param position The position at which this notification is ranked.
* @param buzzBeepBlink Logging code reflecting whether this notification alerted the user.
*/
- void logNotificationReported(@Nullable NotificationRecord r, @Nullable NotificationRecord old,
+ void maybeLogNotificationPosted(@Nullable NotificationRecord r,
+ @Nullable NotificationRecord old,
int position, int buzzBeepBlink);
/**
@@ -62,6 +63,14 @@
@NotificationStats.DismissalSurface int dismissalSurface);
/**
+ * Logs a notification visibility change event using UiEventReported (event ids from the
+ * NotificationEvents enum).
+ * @param r The NotificationRecord. If null, no action is taken.
+ * @param visible True if the notification became visible.
+ */
+ void logNotificationVisibility(@Nullable NotificationRecord r, boolean visible);
+
+ /**
* The UiEvent enums that this class can log.
*/
enum NotificationReportedEvent implements UiEventLogger.UiEventEnum {
@@ -145,6 +154,7 @@
@Override public int getId() {
return mId;
}
+
public static NotificationCancelledEvent fromCancelReason(
@NotificationListenerService.NotificationCancelReason int reason,
@NotificationStats.DismissalSurface int surface) {
@@ -191,6 +201,24 @@
}
}
+ enum NotificationEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Notification became visible.")
+ NOTIFICATION_OPEN(197),
+ @UiEvent(doc = "Notification stopped being visible.")
+ NOTIFICATION_CLOSE(198);
+
+ private final int mId;
+ NotificationEvent(int id) {
+ mId = id;
+ }
+ @Override public int getId() {
+ return mId;
+ }
+
+ public static NotificationEvent fromVisibility(boolean visible) {
+ return visible ? NOTIFICATION_OPEN : NOTIFICATION_CLOSE;
+ }
+ }
/**
* A helper for extracting logging information from one or two NotificationRecords.
*/
@@ -209,7 +237,7 @@
/**
* @return True if old is null, alerted, or important logged fields have changed.
*/
- boolean shouldLog(int buzzBeepBlink) {
+ boolean shouldLogReported(int buzzBeepBlink) {
if (r == null) {
return false;
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index bb23d1e..9fcac25 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -26,13 +26,13 @@
*/
public class NotificationRecordLoggerImpl implements NotificationRecordLogger {
- UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+ private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
@Override
- public void logNotificationReported(NotificationRecord r, NotificationRecord old,
+ public void maybeLogNotificationPosted(NotificationRecord r, NotificationRecord old,
int position, int buzzBeepBlink) {
NotificationRecordPair p = new NotificationRecordPair(r, old);
- if (!p.shouldLog(buzzBeepBlink)) {
+ if (!p.shouldLogReported(buzzBeepBlink)) {
return;
}
FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED,
@@ -66,8 +66,19 @@
@Override
public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) {
- mUiEventLogger.logWithInstanceId(
- NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface),
- r.getUid(), r.getSbn().getPackageName(), r.getSbn().getInstanceId());
+ log(NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), r);
+ }
+
+ @Override
+ public void logNotificationVisibility(NotificationRecord r, boolean visible) {
+ log(NotificationEvent.fromVisibility(visible), r);
+ }
+
+ void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
+ if (r == null) {
+ return;
+ }
+ mUiEventLogger.logWithInstanceId(event, r.getUid(), r.getSbn().getPackageName(),
+ r.getSbn().getInstanceId());
}
}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 661297a..bae1dd3 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -42,7 +43,9 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.sql.Array;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -77,26 +80,26 @@
private static final String REPOST_ACTION = SnoozeHelper.class.getSimpleName() + ".EVALUATE";
private static final int REQUEST_CODE_REPOST = 1;
private static final String REPOST_SCHEME = "repost";
- private static final String EXTRA_KEY = "key";
+ static final String EXTRA_KEY = "key";
private static final String EXTRA_USER_ID = "userId";
private final Context mContext;
private AlarmManager mAm;
private final ManagedServices.UserProfiles mUserProfiles;
- // User id : package name : notification key : record.
- private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>>
+ // User id | package name : notification key : record.
+ private ArrayMap<String, ArrayMap<String, NotificationRecord>>
mSnoozedNotifications = new ArrayMap<>();
- // User id : package name : notification key : time-milliseconds .
+ // User id | package name : notification key : time-milliseconds .
// This member stores persisted snoozed notification trigger times. it persists through reboots
// It should have the notifications that haven't expired or re-posted yet
- private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, Long>>>
+ private final ArrayMap<String, ArrayMap<String, Long>>
mPersistedSnoozedNotifications = new ArrayMap<>();
- // User id : package name : notification key : creation ID .
+ // User id | package name : notification key : creation ID .
// This member stores persisted snoozed notification trigger context for the assistant
// it persists through reboots.
// It should have the notifications that haven't expired or re-posted yet
- private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, String>>>
+ private final ArrayMap<String, ArrayMap<String, String>>
mPersistedSnoozedNotificationsWithContext = new ArrayMap<>();
// notification key : package.
private ArrayMap<String, String> mPackages = new ArrayMap<>();
@@ -115,6 +118,10 @@
mUserProfiles = userProfiles;
}
+ private String getPkgKey(@UserIdInt int userId, String pkg) {
+ return userId + "|" + pkg;
+ }
+
void cleanupPersistedContext(String key){
int userId = mUsers.get(key);
String pkg = mPackages.get(key);
@@ -144,15 +151,13 @@
}
protected boolean isSnoozed(int userId, String pkg, String key) {
- return mSnoozedNotifications.containsKey(userId)
- && mSnoozedNotifications.get(userId).containsKey(pkg)
- && mSnoozedNotifications.get(userId).get(pkg).containsKey(key);
+ return mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))
+ && mSnoozedNotifications.get(getPkgKey(userId, pkg)).containsKey(key);
}
protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) {
- if (mSnoozedNotifications.containsKey(userId)
- && mSnoozedNotifications.get(userId).containsKey(pkg)) {
- return mSnoozedNotifications.get(userId).get(pkg).values();
+ if (mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))) {
+ return mSnoozedNotifications.get(getPkgKey(userId, pkg)).values();
}
return Collections.EMPTY_LIST;
}
@@ -161,14 +166,14 @@
ArrayList<NotificationRecord> getNotifications(String pkg,
String groupKey, Integer userId) {
ArrayList<NotificationRecord> records = new ArrayList<>();
- if (mSnoozedNotifications.containsKey(userId)
- && mSnoozedNotifications.get(userId).containsKey(pkg)) {
- ArrayMap<String, NotificationRecord> packages =
- mSnoozedNotifications.get(userId).get(pkg);
- for (int i = 0; i < packages.size(); i++) {
- String currentGroupKey = packages.valueAt(i).getSbn().getGroup();
+ ArrayMap<String, NotificationRecord> allRecords =
+ mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (allRecords != null) {
+ for (int i = 0; i < allRecords.size(); i++) {
+ NotificationRecord r = allRecords.valueAt(i);
+ String currentGroupKey = r.getSbn().getGroup();
if (currentGroupKey.equals(groupKey)) {
- records.add(packages.valueAt(i));
+ records.add(r);
}
}
}
@@ -176,47 +181,30 @@
}
protected NotificationRecord getNotification(String key) {
- List<NotificationRecord> snoozedForUser = new ArrayList<>();
- IntArray userIds = mUserProfiles.getCurrentProfileIds();
- if (userIds != null) {
- final int userIdsSize = userIds.size();
- for (int i = 0; i < userIdsSize; i++) {
- final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
- mSnoozedNotifications.get(userIds.get(i));
- if (snoozedPkgs != null) {
- final int snoozedPkgsSize = snoozedPkgs.size();
- for (int j = 0; j < snoozedPkgsSize; j++) {
- final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j);
- if (records != null) {
- return records.get(key);
- }
- }
- }
- }
+ if (!mUsers.containsKey(key) || !mPackages.containsKey(key)) {
+ Slog.w(TAG, "Snoozed data sets no longer agree for " + key);
+ return null;
}
- return null;
+ int userId = mUsers.get(key);
+ String pkg = mPackages.get(key);
+ ArrayMap<String, NotificationRecord> snoozed =
+ mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (snoozed == null) {
+ return null;
+ }
+ return snoozed.get(key);
}
protected @NonNull List<NotificationRecord> getSnoozed() {
- List<NotificationRecord> snoozedForUser = new ArrayList<>();
- IntArray userIds = mUserProfiles.getCurrentProfileIds();
- if (userIds != null) {
- final int N = userIds.size();
- for (int i = 0; i < N; i++) {
- final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
- mSnoozedNotifications.get(userIds.get(i));
- if (snoozedPkgs != null) {
- final int M = snoozedPkgs.size();
- for (int j = 0; j < M; j++) {
- final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j);
- if (records != null) {
- snoozedForUser.addAll(records.values());
- }
- }
- }
- }
+ // caller filters records based on the current user profiles and listener access, so just
+ // return everything
+ List<NotificationRecord> snoozed= new ArrayList<>();
+ for (String userPkgKey : mSnoozedNotifications.keySet()) {
+ ArrayMap<String, NotificationRecord> snoozedRecords =
+ mSnoozedNotifications.get(userPkgKey);
+ snoozed.addAll(snoozedRecords.values());
}
- return snoozedForUser;
+ return snoozed;
}
/**
@@ -261,120 +249,88 @@
}
private <T> void storeRecord(String pkg, String key, Integer userId,
- ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, T object) {
+ ArrayMap<String, ArrayMap<String, T>> targets, T object) {
- ArrayMap<String, ArrayMap<String, T>> records =
- targets.get(userId);
- if (records == null) {
- records = new ArrayMap<>();
+ ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg));
+ if (keyToValue == null) {
+ keyToValue = new ArrayMap<>();
}
- ArrayMap<String, T> pkgRecords = records.get(pkg);
- if (pkgRecords == null) {
- pkgRecords = new ArrayMap<>();
- }
- pkgRecords.put(key, object);
- records.put(pkg, pkgRecords);
- targets.put(userId, records);
+ keyToValue.put(key, object);
+ targets.put(getPkgKey(userId, pkg), keyToValue);
}
private <T> T removeRecord(String pkg, String key, Integer userId,
- ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets) {
+ ArrayMap<String, ArrayMap<String, T>> targets) {
T object = null;
-
- ArrayMap<String, ArrayMap<String, T>> records =
- targets.get(userId);
- if (records == null) {
+ ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg));
+ if (keyToValue == null) {
return null;
}
- ArrayMap<String, T> pkgRecords = records.get(pkg);
- if (pkgRecords == null) {
- return null;
- }
- object = pkgRecords.remove(key);
- if (pkgRecords.size() == 0) {
- records.remove(pkg);
- }
- if (records.size() == 0) {
- targets.remove(userId);
+ object = keyToValue.remove(key);
+ if (keyToValue.size() == 0) {
+ targets.remove(getPkgKey(userId, pkg));
}
return object;
}
protected boolean cancel(int userId, String pkg, String tag, int id) {
- if (mSnoozedNotifications.containsKey(userId)) {
- ArrayMap<String, NotificationRecord> recordsForPkg =
- mSnoozedNotifications.get(userId).get(pkg);
- if (recordsForPkg != null) {
- final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
- for (Map.Entry<String, NotificationRecord> record : records) {
- final StatusBarNotification sbn = record.getValue().getSbn();
- if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
- record.getValue().isCanceled = true;
- return true;
- }
+ ArrayMap<String, NotificationRecord> recordsForPkg =
+ mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (recordsForPkg != null) {
+ final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
+ for (Map.Entry<String, NotificationRecord> record : records) {
+ final StatusBarNotification sbn = record.getValue().getSbn();
+ if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
+ record.getValue().isCanceled = true;
+ return true;
}
}
}
return false;
}
- protected boolean cancel(int userId, boolean includeCurrentProfiles) {
- int[] userIds = {userId};
- if (includeCurrentProfiles) {
- userIds = mUserProfiles.getCurrentProfileIds().toArray();
+ protected void cancel(int userId, boolean includeCurrentProfiles) {
+ if (mSnoozedNotifications.size() == 0) {
+ return;
}
- final int N = userIds.length;
- for (int i = 0; i < N; i++) {
- final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
- mSnoozedNotifications.get(userIds[i]);
- if (snoozedPkgs != null) {
- final int M = snoozedPkgs.size();
- for (int j = 0; j < M; j++) {
- final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j);
- if (records != null) {
- int P = records.size();
- for (int k = 0; k < P; k++) {
- records.valueAt(k).isCanceled = true;
- }
- }
+ IntArray userIds = new IntArray();
+ userIds.add(userId);
+ if (includeCurrentProfiles) {
+ userIds = mUserProfiles.getCurrentProfileIds();
+ }
+ for (ArrayMap<String, NotificationRecord> snoozedRecords : mSnoozedNotifications.values()) {
+ for (NotificationRecord r : snoozedRecords.values()) {
+ if (userIds.binarySearch(r.getUserId()) >= 0) {
+ r.isCanceled = true;
}
- return true;
}
}
- return false;
}
protected boolean cancel(int userId, String pkg) {
- if (mSnoozedNotifications.containsKey(userId)) {
- if (mSnoozedNotifications.get(userId).containsKey(pkg)) {
- ArrayMap<String, NotificationRecord> records =
- mSnoozedNotifications.get(userId).get(pkg);
- int N = records.size();
- for (int i = 0; i < N; i++) {
- records.valueAt(i).isCanceled = true;
- }
- return true;
- }
+ ArrayMap<String, NotificationRecord> records =
+ mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (records == null) {
+ return false;
}
- return false;
+ int N = records.size();
+ for (int i = 0; i < N; i++) {
+ records.valueAt(i).isCanceled = true;
+ }
+ return true;
}
/**
* Updates the notification record so the most up to date information is shown on re-post.
*/
protected void update(int userId, NotificationRecord record) {
- ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
- mSnoozedNotifications.get(userId);
+ ArrayMap<String, NotificationRecord> records =
+ mSnoozedNotifications.get(getPkgKey(userId, record.getSbn().getPackageName()));
if (records == null) {
return;
}
- ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.getSbn().getPackageName());
- if (pkgRecords == null) {
- return;
- }
- NotificationRecord existing = pkgRecords.get(record.getKey());
- pkgRecords.put(record.getKey(), record);
+ records.put(record.getKey(), record);
}
protected void repost(String key) {
@@ -386,20 +342,18 @@
protected void repost(String key, int userId) {
final String pkg = mPackages.remove(key);
- ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
- mSnoozedNotifications.get(userId);
+ ArrayMap<String, NotificationRecord> records =
+ mSnoozedNotifications.get(getPkgKey(userId, pkg));
if (records == null) {
return;
}
- ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg);
- if (pkgRecords == null) {
- return;
- }
- final NotificationRecord record = pkgRecords.remove(key);
+ final NotificationRecord record = records.remove(key);
mPackages.remove(key);
mUsers.remove(key);
if (record != null && !record.isCanceled) {
+ final PendingIntent pi = createPendingIntent(pkg, record.getKey(), userId);
+ mAm.cancel(pi);
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
.setType(MetricsProto.MetricsEvent.TYPE_OPEN));
@@ -408,55 +362,46 @@
}
protected void repostGroupSummary(String pkg, int userId, String groupKey) {
- if (mSnoozedNotifications.containsKey(userId)) {
- ArrayMap<String, ArrayMap<String, NotificationRecord>> keysByPackage
- = mSnoozedNotifications.get(userId);
+ ArrayMap<String, NotificationRecord> recordsByKey
+ = mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (recordsByKey == null) {
+ return;
+ }
- if (keysByPackage != null && keysByPackage.containsKey(pkg)) {
- ArrayMap<String, NotificationRecord> recordsByKey = keysByPackage.get(pkg);
+ String groupSummaryKey = null;
+ int N = recordsByKey.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i);
+ if (potentialGroupSummary.getSbn().isGroup()
+ && potentialGroupSummary.getNotification().isGroupSummary()
+ && groupKey.equals(potentialGroupSummary.getGroupKey())) {
+ groupSummaryKey = potentialGroupSummary.getKey();
+ break;
+ }
+ }
- if (recordsByKey != null) {
- String groupSummaryKey = null;
- int N = recordsByKey.size();
- for (int i = 0; i < N; i++) {
- final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i);
- if (potentialGroupSummary.getSbn().isGroup()
- && potentialGroupSummary.getNotification().isGroupSummary()
- && groupKey.equals(potentialGroupSummary.getGroupKey())) {
- groupSummaryKey = potentialGroupSummary.getKey();
- break;
- }
- }
+ if (groupSummaryKey != null) {
+ NotificationRecord record = recordsByKey.remove(groupSummaryKey);
+ mPackages.remove(groupSummaryKey);
+ mUsers.remove(groupSummaryKey);
- if (groupSummaryKey != null) {
- NotificationRecord record = recordsByKey.remove(groupSummaryKey);
- mPackages.remove(groupSummaryKey);
- mUsers.remove(groupSummaryKey);
-
- if (record != null && !record.isCanceled) {
- MetricsLogger.action(record.getLogMaker()
- .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
- .setType(MetricsProto.MetricsEvent.TYPE_OPEN));
- mCallback.repost(userId, record);
- }
- }
- }
+ if (record != null && !record.isCanceled) {
+ MetricsLogger.action(record.getLogMaker()
+ .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
+ .setType(MetricsProto.MetricsEvent.TYPE_OPEN));
+ mCallback.repost(userId, record);
}
}
}
protected void clearData(int userId, String pkg) {
- ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
- mSnoozedNotifications.get(userId);
+ ArrayMap<String, NotificationRecord> records =
+ mSnoozedNotifications.get(getPkgKey(userId, pkg));
if (records == null) {
return;
}
- ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg);
- if (pkgRecords == null) {
- return;
- }
- for (int i = pkgRecords.size() - 1; i >= 0; i--) {
- final NotificationRecord r = pkgRecords.removeAt(i);
+ for (int i = records.size() - 1; i >= 0; i--) {
+ final NotificationRecord r = records.removeAt(i);
if (r != null) {
mPackages.remove(r.getKey());
mUsers.remove(r.getKey());
@@ -495,22 +440,36 @@
public void dump(PrintWriter pw, NotificationManagerService.DumpFilter filter) {
pw.println("\n Snoozed notifications:");
- for (int userId : mSnoozedNotifications.keySet()) {
+ for (String userPkgKey : mSnoozedNotifications.keySet()) {
pw.print(INDENT);
- pw.println("user: " + userId);
- ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
- mSnoozedNotifications.get(userId);
- for (String pkg : snoozedPkgs.keySet()) {
+ pw.println("key: " + userPkgKey);
+ ArrayMap<String, NotificationRecord> snoozedRecords =
+ mSnoozedNotifications.get(userPkgKey);
+ Set<String> snoozedKeys = snoozedRecords.keySet();
+ for (String key : snoozedKeys) {
pw.print(INDENT);
pw.print(INDENT);
- pw.println("package: " + pkg);
- Set<String> snoozedKeys = snoozedPkgs.get(pkg).keySet();
- for (String key : snoozedKeys) {
- pw.print(INDENT);
- pw.print(INDENT);
- pw.print(INDENT);
- pw.println(key);
- }
+ pw.print(INDENT);
+ pw.println(key);
+ }
+ }
+ pw.println("\n Pending snoozed notifications");
+ for (String userPkgKey : mPersistedSnoozedNotifications.keySet()) {
+ pw.print(INDENT);
+ pw.println("key: " + userPkgKey);
+ ArrayMap<String, Long> snoozedRecords =
+ mPersistedSnoozedNotifications.get(userPkgKey);
+ if (snoozedRecords == null) {
+ continue;
+ }
+ Set<String> snoozedKeys = snoozedRecords.keySet();
+ for (String key : snoozedKeys) {
+ pw.print(INDENT);
+ pw.print(INDENT);
+ pw.print(INDENT);
+ pw.print(key);
+ pw.print(INDENT);
+ pw.println(snoozedRecords.get(key));
}
}
}
@@ -538,41 +497,34 @@
void insert(T t) throws IOException;
}
private <T> void writeXml(XmlSerializer out,
- ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, String tag,
+ ArrayMap<String, ArrayMap<String, T>> targets, String tag,
Inserter<T> attributeInserter)
throws IOException {
synchronized (targets) {
final int M = targets.size();
for (int i = 0; i < M; i++) {
- final ArrayMap<String, ArrayMap<String, T>> packages =
- targets.valueAt(i);
- if (packages == null) {
- continue;
- }
- final int N = packages.size();
- for (int j = 0; j < N; j++) {
- final ArrayMap<String, T> keyToValue = packages.valueAt(j);
- if (keyToValue == null) {
- continue;
- }
- final int O = keyToValue.size();
- for (int k = 0; k < O; k++) {
- T value = keyToValue.valueAt(k);
+ String userIdPkgKey = targets.keyAt(i);
+ // T is a String (snoozed until context) or Long (snoozed until time)
+ ArrayMap<String, T> keyToValue = targets.valueAt(i);
+ for (int j = 0; j < keyToValue.size(); j++) {
+ String key = keyToValue.keyAt(j);
+ T value = keyToValue.valueAt(j);
- out.startTag(null, tag);
+ out.startTag(null, tag);
- attributeInserter.insert(value);
+ attributeInserter.insert(value);
- out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
- XML_SNOOZED_NOTIFICATION_VERSION);
- out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, keyToValue.keyAt(k));
- out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, packages.keyAt(j));
- out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
- targets.keyAt(i).toString());
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
+ XML_SNOOZED_NOTIFICATION_VERSION);
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key);
- out.endTag(null, tag);
+ String pkg = mPackages.get(key);
+ int userId = mUsers.get(key);
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg);
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
+ String.valueOf(userId));
- }
+ out.endTag(null, tag);
}
}
}
@@ -606,7 +558,6 @@
}
scheduleRepost(pkg, key, userId, time - System.currentTimeMillis());
}
- continue;
}
if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) {
final String creationId = parser.getAttributeValue(
@@ -615,18 +566,9 @@
storeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext,
creationId);
}
- continue;
}
-
-
} catch (Exception e) {
- //we dont cre if it is a number format exception or a null pointer exception.
- //we just want to debug it and continue with our lives
- if (DEBUG) {
- Slog.d(TAG,
- "Exception in reading snooze data from policy xml: "
- + e.getMessage());
- }
+ Slog.e(TAG, "Exception in reading snooze data from policy xml", e);
}
}
}
diff --git a/services/core/java/com/android/server/people/PeopleServiceInternal.java b/services/core/java/com/android/server/people/PeopleServiceInternal.java
index c5b868f..31fa4d1 100644
--- a/services/core/java/com/android/server/people/PeopleServiceInternal.java
+++ b/services/core/java/com/android/server/people/PeopleServiceInternal.java
@@ -17,6 +17,8 @@
package com.android.server.people;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.os.CancellationSignal;
import android.service.appprediction.IPredictionService;
/**
@@ -25,16 +27,23 @@
public abstract class PeopleServiceInternal extends IPredictionService.Stub {
/**
+ * Prunes the data for the specified user. Called by {@link
+ * com.android.server.people.data.DataMaintenanceService} when the device is idle.
+ */
+ public abstract void pruneDataForUser(@UserIdInt int userId,
+ @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.
*/
- public abstract byte[] backupConversationInfos(@NonNull int userId);
+ public abstract byte[] backupConversationInfos(@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.
*/
- public abstract void restoreConversationInfos(@NonNull int userId, @NonNull String key,
+ public abstract void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
@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 d629b54..0fb889c 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -69,7 +69,7 @@
// Logs all filtering instead of enforcing
private static final boolean DEBUG_ALLOW_ALL = false;
private static final boolean DEBUG_LOGGING = false;
- private static final boolean FEATURE_ENABLED_BY_DEFAULT = false;
+ private static final boolean FEATURE_ENABLED_BY_DEFAULT = true;
/**
* This contains a list of app UIDs that are implicitly queryable because another app explicitly
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2c85d06..8c6e6916 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4600,7 +4600,7 @@
synchronized (mLock) {
final AndroidPackage p = mPackages.get(packageName);
if (p != null && p.isMatch(flags)) {
- PackageSetting ps = getPackageSetting(p.getPackageName());
+ PackageSetting ps = getPackageSettingInternal(p.getPackageName(), callingUid);
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
return -1;
}
@@ -5924,7 +5924,10 @@
*/
@Override
public String[] getPackagesForUid(int uid) {
- final int callingUid = Binder.getCallingUid();
+ return getPackagesForUidInternal(uid, Binder.getCallingUid());
+ }
+
+ private String[] getPackagesForUidInternal(int uid, int callingUid) {
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
@@ -14704,8 +14707,7 @@
void handleVerificationFinished() {
if (!mVerificationCompleted) {
mVerificationCompleted = true;
- if (mIntegrityVerificationCompleted || mRet != INSTALL_SUCCEEDED) {
- mIntegrityVerificationCompleted = true;
+ if (mIntegrityVerificationCompleted) {
handleReturnCode();
}
// integrity verification still pending.
@@ -14715,8 +14717,7 @@
void handleIntegrityVerificationFinished() {
if (!mIntegrityVerificationCompleted) {
mIntegrityVerificationCompleted = true;
- if (mVerificationCompleted || mRet != INSTALL_SUCCEEDED) {
- mVerificationCompleted = true;
+ if (mVerificationCompleted) {
handleReturnCode();
}
// verifier still pending
@@ -17380,6 +17381,13 @@
@GuardedBy("mLock")
private String resolveInternalPackageNameLPr(String packageName, long versionCode) {
+ final int callingUid = Binder.getCallingUid();
+ return resolveInternalPackageNameInternalLocked(packageName, versionCode,
+ callingUid);
+ }
+
+ private String resolveInternalPackageNameInternalLocked(
+ String packageName, long versionCode, int callingUid) {
// Handle renamed packages
String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
packageName = normalizedPackageName != null ? normalizedPackageName : packageName;
@@ -17393,12 +17401,12 @@
// Figure out which lib versions the caller can see
LongSparseLongArray versionsCallerCanSee = null;
- final int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
+ final int callingAppId = UserHandle.getAppId(callingUid);
if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID
&& callingAppId != Process.ROOT_UID) {
versionsCallerCanSee = new LongSparseLongArray();
String libName = versionedLib.valueAt(0).getName();
- String[] uidPackages = getPackagesForUid(Binder.getCallingUid());
+ String[] uidPackages = getPackagesForUidInternal(callingUid, callingUid);
if (uidPackages != null) {
for (String uidPackage : uidPackages) {
PackageSetting ps = mSettings.getPackageLPr(uidPackage);
@@ -23003,7 +23011,7 @@
@Override
public AndroidPackage getPackage(int uid) {
synchronized (mLock) {
- final String[] packageNames = getPackagesForUid(uid);
+ final String[] packageNames = getPackagesForUidInternal(uid, Process.SYSTEM_UID);
AndroidPackage pkg = null;
final int numPackages = packageNames == null ? 0 : packageNames.length;
for (int i = 0; pkg == null && i < numPackages; i++) {
@@ -24017,9 +24025,13 @@
@Nullable
public PackageSetting getPackageSetting(String packageName) {
+ return getPackageSettingInternal(packageName, Binder.getCallingUid());
+ }
+
+ private PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
synchronized (mLock) {
- packageName = resolveInternalPackageNameLPr(
- packageName, PackageManager.VERSION_CODE_HIGHEST);
+ packageName = resolveInternalPackageNameInternalLocked(
+ packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid);
return mSettings.mPackages.get(packageName);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index c267cea..f1e403b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -119,6 +119,7 @@
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -135,7 +136,6 @@
class PackageManagerShellCommand extends ShellCommand {
/** Path for streaming APK content */
private static final String STDIN_PATH = "-";
- private static final byte[] STDIN_PATH_BYTES = "-".getBytes(StandardCharsets.UTF_8);
/** Path where ART profiles snapshots are dumped for the shell user */
private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/";
private static final int DEFAULT_WAIT_MS = 60 * 1000;
@@ -2988,8 +2988,10 @@
try {
// 1. Single file from stdin.
if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
- String name = "base." + (isApex ? "apex" : "apk");
- session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes, STDIN_PATH_BYTES, null);
+ final String name = "base." + (isApex ? "apex" : "apk");
+ final String metadata = "-" + name;
+ session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes,
+ metadata.getBytes(StandardCharsets.UTF_8), null);
return 0;
}
@@ -2998,24 +3000,58 @@
// 2. File with specified size read from stdin.
if (delimLocation != -1) {
- String name = arg.substring(0, delimLocation);
- String sizeStr = arg.substring(delimLocation + 1);
- long sizeBytes;
+ final String[] fileDesc = arg.split(":");
+ String name = null;
+ long sizeBytes = -1;
+ String metadata;
+ byte[] signature = null;
+
+ try {
+ if (fileDesc.length > 0) {
+ name = fileDesc[0];
+ }
+ if (fileDesc.length > 1) {
+ sizeBytes = Long.parseUnsignedLong(fileDesc[1]);
+ }
+ if (fileDesc.length > 2 && !TextUtils.isEmpty(fileDesc[2])) {
+ metadata = fileDesc[2];
+ } else {
+ metadata = name;
+ }
+ if (fileDesc.length > 3) {
+ signature = Base64.getDecoder().decode(fileDesc[3]);
+ }
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Unable to parse file parameters: " + arg + ", reason: " + e);
+ return 1;
+ }
if (TextUtils.isEmpty(name)) {
getErrPrintWriter().println("Empty file name in: " + arg);
return 1;
}
+
+ if (signature != null) {
+ // Streaming/adb mode.
+ metadata = "+" + metadata;
+ } else {
+ // Singleshot read from stdin.
+ metadata = "-" + metadata;
+ }
+
try {
- sizeBytes = Long.parseUnsignedLong(sizeStr);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Unable to parse size from: " + arg);
+ if (V4Signature.readFrom(signature) == null) {
+ getErrPrintWriter().println("V4 signature is invalid in: " + arg);
+ return 1;
+ }
+ } catch (Exception e) {
+ getErrPrintWriter().println("V4 signature is invalid: " + e + " in " + arg);
return 1;
}
- // Incremental requires unique metadatas, let's add a name to the dash.
session.addFile(LOCATION_DATA_APP, name, sizeBytes,
- ("-" + name).getBytes(StandardCharsets.UTF_8), null);
+ metadata.getBytes(StandardCharsets.UTF_8), signature);
continue;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 0fb4cb0..832c9b7 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4393,6 +4393,7 @@
ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT, "SYSTEM_EXT",
ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD, "VIRTUAL_PRELOAD",
ApplicationInfo.PRIVATE_FLAG_ODM, "ODM",
+ ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING, "PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING",
};
void dumpVersionLPr(IndentingPrintWriter pw) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 67a22d3..39093ae 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -866,6 +866,10 @@
}
}
+ default int getMaxWindowLayer() {
+ return 35;
+ }
+
/**
* Return how to Z-order sub-windows in relation to the window they are attached to.
* Return positive to have them ordered in front, negative for behind.
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 9e150fd..f9981d0 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -802,7 +802,6 @@
newRollback = getRollbackForSessionLocked(packageSession.getSessionId());
if (newRollback == null) {
newRollback = createNewRollbackLocked(parentSession);
- mRollbacks.add(newRollback);
}
}
newRollback.addToken(token);
@@ -1002,11 +1001,10 @@
}
}
- Rollback rollback = completeEnableRollback(newRollback);
- if (rollback == null) {
+ if (!completeEnableRollback(newRollback)) {
result.offer(-1);
} else {
- result.offer(rollback.info.getRollbackId());
+ result.offer(newRollback.info.getRollbackId());
}
});
@@ -1158,19 +1156,10 @@
Rollback rollback;
synchronized (mLock) {
rollback = getRollbackForSessionLocked(sessionId);
- if (rollback == null || rollback.isStaged() || !rollback.isEnabling()
- || !rollback.notifySessionWithSuccess()) {
- return;
- }
- // All child sessions finished with success. We can enable this rollback now.
- // TODO: refactor #completeEnableRollback so we won't remove 'rollback' from
- // mRollbacks here and add it back in #completeEnableRollback later.
- mRollbacks.remove(rollback);
}
- // TODO: Now #completeEnableRollback returns the same rollback object as the
- // parameter on success. It would be more readable to return a boolean to indicate
- // success or failure.
- if (completeEnableRollback(rollback) != null) {
+ if (rollback != null && !rollback.isStaged() && rollback.isEnabling()
+ && rollback.notifySessionWithSuccess()
+ && completeEnableRollback(rollback)) {
makeRollbackAvailable(rollback);
}
} else {
@@ -1188,13 +1177,14 @@
}
/**
- * Add a rollback to the list of rollbacks. It does not make the rollback available yet.
+ * Persist a rollback as enable-completed. It does not make the rollback available yet.
+ * This rollback will be deleted and removed from {@link #mRollbacks} should any error happens.
*
- * @return the Rollback instance for a successfully enable-completed rollback,
- * or null on error.
+ * @return {code true} if {code rollback} is successfully enable-completed,
+ * or {code false} otherwise.
*/
@WorkerThread
- private Rollback completeEnableRollback(Rollback rollback) {
+ private boolean completeEnableRollback(Rollback rollback) {
if (LOCAL_LOGV) {
Slog.v(TAG, "completeEnableRollback id=" + rollback.info.getRollbackId());
}
@@ -1205,26 +1195,24 @@
// rollback for the embedded apk-in-apex, if any.
if (!rollback.allPackagesEnabled()) {
Slog.e(TAG, "Failed to enable rollback for all packages in session.");
+ mRollbacks.remove(rollback);
rollback.delete(mAppDataRollbackHelper);
- return null;
+ return false;
}
+ // Note: There is a small window of time between when
+ // the session has been committed by the package
+ // manager and when we make the rollback available
+ // here. Presumably the window is small enough that
+ // nobody will want to roll back the newly installed
+ // package before we make the rollback available.
+ // TODO: We'll lose the rollback if the
+ // device reboots between when the session is
+ // committed and this point. Revisit this after
+ // adding support for rollback of staged installs.
rollback.saveRollback();
- synchronized (mLock) {
- // Note: There is a small window of time between when
- // the session has been committed by the package
- // manager and when we make the rollback available
- // here. Presumably the window is small enough that
- // nobody will want to roll back the newly installed
- // package before we make the rollback available.
- // TODO: We'll lose the rollback if the
- // device reboots between when the session is
- // committed and this point. Revisit this after
- // adding support for rollback of staged installs.
- mRollbacks.add(rollback);
- }
- return rollback;
+ return true;
}
@WorkerThread
@@ -1304,6 +1292,10 @@
}
}
+ /**
+ * Creates and returns a Rollback according to the given SessionInfo
+ * and adds it to {@link #mRollbacks}.
+ */
@WorkerThread
@GuardedBy("mLock")
private Rollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
@@ -1338,6 +1330,7 @@
installerPackageName, packageSessionIds);
}
+ mRollbacks.add(rollback);
return rollback;
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e26dac6..084800c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2679,7 +2679,8 @@
// DisplayContent#topRunningActivity().
final ActivityRecord next = display.topRunningActivity();
final boolean isLastStackOverEmptyHome =
- next == null && stack.isFocusedStackOnDisplay() && display.getRootHomeTask() != null;
+ next == null && stack.isFocusedStackOnDisplay()
+ && display.getOrCreateRootHomeTask() != null;
if (isLastStackOverEmptyHome) {
// Don't destroy activity immediately if this is the last activity on the display and
// the display contains home stack. Although there is no next activity at the moment,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 8ed798c..e77d8ae 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1374,7 +1374,7 @@
break;
case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
final ActivityStack homeStack =
- startedActivityStack.getDisplay().getRootHomeTask();
+ startedActivityStack.getDisplay().getOrCreateRootHomeTask();
if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) {
mService.mWindowManager.showRecentApps();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 1859fae..f2917c5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5568,11 +5568,20 @@
if (mLastResumedActivity != null && r.mUserId != mLastResumedActivity.mUserId) {
mAmInternal.sendForegroundProfileChanged(r.mUserId);
}
+ final Task prevTask = mLastResumedActivity != null ? mLastResumedActivity.getTask() : null;
+
updateResumedAppTrace(r);
mLastResumedActivity = r;
r.getDisplay().setFocusedApp(r, true);
+ if (prevTask == null || task != prevTask) {
+ if (prevTask != null) {
+ mTaskChangeNotificationController.notifyTaskFocusChanged(prevTask.mTaskId, false);
+ }
+ mTaskChangeNotificationController.notifyTaskFocusChanged(task.mTaskId, true);
+ }
+
applyUpdateLockStateLocked(r);
applyUpdateVrModeLocked(r);
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index b3edc91..0d365b1 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -102,6 +102,11 @@
}
@Override
+ public String toString() {
+ return mName + "@" + System.identityHashCode(this);
+ }
+
+ @Override
public final void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) {
final long token = proto.start(fieldId);
super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 9e93e14..0ec0c7b 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-
import android.content.res.Resources;
import android.text.TextUtils;
@@ -43,7 +41,7 @@
/**
* The Tasks container. Tasks etc. are automatically added to this container.
*/
- protected final TaskContainers mTaskContainers;
+ protected final DisplayArea<? extends ActivityStack> mTaskContainers;
/**
* Construct a new {@link DisplayAreaPolicy}
@@ -58,7 +56,8 @@
*/
protected DisplayAreaPolicy(WindowManagerService wmService,
DisplayContent content, DisplayArea.Root root,
- DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) {
+ DisplayArea<? extends WindowContainer> imeContainer,
+ DisplayArea<? extends ActivityStack> taskContainers) {
mWmService = wmService;
mContent = content;
mRoot = root;
@@ -83,67 +82,15 @@
*/
public abstract void addWindow(WindowToken token);
- /**
- * Default policy that has no special features.
- */
- public static class Default extends DisplayAreaPolicy {
-
- public Default(WindowManagerService wmService, DisplayContent content,
- DisplayArea.Root root,
+ /** Provider for platform-default display area policy. */
+ static final class DefaultProvider implements DisplayAreaPolicy.Provider {
+ @Override
+ public DisplayAreaPolicy instantiate(WindowManagerService wmService,
+ DisplayContent content, DisplayArea.Root root,
DisplayArea<? extends WindowContainer> imeContainer,
TaskContainers taskContainers) {
- super(wmService, content, root, imeContainer, taskContainers);
- }
-
- private final DisplayArea.Tokens mBelow = new DisplayArea.Tokens(mWmService,
- DisplayArea.Type.BELOW_TASKS, "BelowTasks");
- private final DisplayArea<DisplayArea> mAbove = new DisplayArea<>(mWmService,
- DisplayArea.Type.ABOVE_TASKS, "AboveTasks");
- private final DisplayArea.Tokens mAboveBelowIme = new DisplayArea.Tokens(mWmService,
- DisplayArea.Type.ABOVE_TASKS, "AboveTasksBelowIme");
- private final DisplayArea.Tokens mAboveAboveIme = new DisplayArea.Tokens(mWmService,
- DisplayArea.Type.ABOVE_TASKS, "AboveTasksAboveIme");
-
- @Override
- public void attachDisplayAreas() {
- mRoot.addChild(mBelow, 0);
- mRoot.addChild(mTaskContainers, 1);
- mRoot.addChild(mAbove, 2);
-
- mAbove.addChild(mAboveBelowIme, 0);
- mAbove.addChild(mImeContainer, 1);
- mAbove.addChild(mAboveAboveIme, 2);
- }
-
- @Override
- public void addWindow(WindowToken token) {
- switch (DisplayArea.Type.typeOf(token)) {
- case ABOVE_TASKS:
- if (token.getWindowLayerFromType()
- < mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)) {
- mAboveBelowIme.addChild(token);
- } else {
- mAboveAboveIme.addChild(token);
- }
- break;
- case BELOW_TASKS:
- mBelow.addChild(token);
- break;
- default:
- throw new IllegalArgumentException("don't know how to sort " + token);
- }
- }
-
- /** Provider for {@link DisplayAreaPolicy.Default platform-default display area policy}. */
- static class Provider implements DisplayAreaPolicy.Provider {
- @Override
- public DisplayAreaPolicy instantiate(WindowManagerService wmService,
- DisplayContent content, DisplayArea.Root root,
- DisplayArea<? extends WindowContainer> imeContainer,
- TaskContainers taskContainers) {
- return new DisplayAreaPolicy.Default(wmService, content, root, imeContainer,
- taskContainers);
- }
+ return new DisplayAreaPolicyBuilder()
+ .build(wmService, content, root, imeContainer, taskContainers);
}
}
@@ -172,7 +119,7 @@
String name = res.getString(
com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider);
if (TextUtils.isEmpty(name)) {
- return new DisplayAreaPolicy.Default.Provider();
+ return new DisplayAreaPolicy.DefaultProvider();
}
try {
return (Provider) Class.forName(name).newInstance();
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
new file mode 100644
index 0000000..885456a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -0,0 +1,371 @@
+/*
+ * 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 android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.policy.WindowManagerPolicy;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A builder for instantiating a complex {@link DisplayAreaPolicy}
+ *
+ * <p>Given a set of features (that each target a set of window types), it builds the necessary
+ * DisplayArea hierarchy.
+ *
+ * <p>Example: <br />
+ *
+ * <pre>
+ * // Feature for targeting everything below the magnification overlay:
+ * new DisplayAreaPolicyBuilder(...)
+ * .addFeature(new Feature.Builder(..., "Magnification")
+ * .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
+ * .build())
+ * .build(...)
+ *
+ * // Builds a policy with the following hierarchy:
+ * - DisplayArea.Root
+ * - Magnification
+ * - DisplayArea.Tokens (Wallpapers are attached here)
+ * - TaskContainers
+ * - DisplayArea.Tokens (windows above Tasks up to IME are attached here)
+ * - ImeContainers
+ * - DisplayArea.Tokens (windows above IME up to TYPE_ACCESSIBILITY_OVERLAY attached here)
+ * - DisplayArea.Tokens (TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY and up are attached here)
+ *
+ * </pre>
+ *
+ * // TODO(display-area): document more complex scenarios where we need multiple areas per feature.
+ */
+class DisplayAreaPolicyBuilder {
+
+ private final ArrayList<Feature> mFeatures = new ArrayList<>();
+
+ /**
+ * A feature that requires {@link DisplayArea DisplayArea(s)}.
+ */
+ static class Feature {
+ private final String mName;
+ private final boolean[] mWindowLayers;
+
+ private Feature(String name, boolean[] windowLayers) {
+ mName = name;
+ mWindowLayers = windowLayers;
+ }
+
+ static class Builder {
+ private final WindowManagerPolicy mPolicy;
+ private final String mName;
+ private final boolean[] mLayers;
+
+ /**
+ * Build a new feature that applies to a set of window types as specified by the builder
+ * methods.
+ *
+ * <p>The set of types is updated iteratively in the order of the method invocations.
+ * For example, {@code all().except(TYPE_STATUS_BAR)} expresses that a feature should
+ * apply to all types except TYPE_STATUS_BAR.
+ *
+ * The builder starts out with the feature not applying to any types.
+ *
+ * @param name the name of the feature.
+ */
+ Builder(WindowManagerPolicy policy, String name) {
+ mPolicy = policy;
+ mName = name;
+ mLayers = new boolean[mPolicy.getMaxWindowLayer()];
+ }
+
+ /**
+ * Set that the feature applies to all window types.
+ */
+ Builder all() {
+ Arrays.fill(mLayers, true);
+ return this;
+ }
+
+ /**
+ * Set that the feature applies to the given window types.
+ */
+ Builder and(int... types) {
+ for (int i = 0; i < types.length; i++) {
+ int type = types[i];
+ set(type, true);
+ }
+ return this;
+ }
+
+ /**
+ * Set that the feature does not apply to the given window types.
+ */
+ Builder except(int... types) {
+ for (int i = 0; i < types.length; i++) {
+ int type = types[i];
+ set(type, false);
+ }
+ return this;
+ }
+
+ /**
+ * Set that the feature applies window types that are layerd at or below the layer of
+ * the given window type.
+ */
+ Builder upTo(int typeInclusive) {
+ final int max = layerFromType(typeInclusive, false);
+ for (int i = 0; i < max; i++) {
+ mLayers[i] = true;
+ }
+ set(typeInclusive, true);
+ return this;
+ }
+
+ Feature build() {
+ return new Feature(mName, mLayers.clone());
+ }
+
+ private void set(int type, boolean value) {
+ mLayers[layerFromType(type, true)] = value;
+ if (type == TYPE_APPLICATION_OVERLAY) {
+ mLayers[layerFromType(type, true)] = value;
+ mLayers[layerFromType(TYPE_SYSTEM_ALERT, false)] = value;
+ mLayers[layerFromType(TYPE_SYSTEM_OVERLAY, false)] = value;
+ mLayers[layerFromType(TYPE_SYSTEM_ERROR, false)] = value;
+ }
+ }
+
+ private int layerFromType(int type, boolean internalWindows) {
+ return mPolicy.getWindowLayerFromTypeLw(type, internalWindows);
+ }
+ }
+ }
+
+ static class Result extends DisplayAreaPolicy {
+ private static final int LEAF_TYPE_TASK_CONTAINERS = 1;
+ private static final int LEAF_TYPE_IME_CONTAINERS = 2;
+ private static final int LEAF_TYPE_TOKENS = 0;
+
+ private final int mMaxWindowLayer = mWmService.mPolicy.getMaxWindowLayer();
+
+ private final ArrayList<Feature> mFeatures;
+ private final Map<Feature, List<DisplayArea<? extends WindowContainer>>> mAreas;
+ private final DisplayArea.Tokens[] mAreaForLayer = new DisplayArea.Tokens[mMaxWindowLayer];
+
+ Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root,
+ DisplayArea<? extends WindowContainer> imeContainer,
+ DisplayArea<? extends ActivityStack> taskStacks, ArrayList<Feature> features) {
+ super(wmService, content, root, imeContainer, taskStacks);
+ mFeatures = features;
+ mAreas = new HashMap<>(features.size());
+ for (int i = 0; i < mFeatures.size(); i++) {
+ mAreas.put(mFeatures.get(i), new ArrayList<>());
+ }
+ }
+
+ @Override
+ public void attachDisplayAreas() {
+ // This method constructs the layer hierarchy with the following properties:
+ // (1) Every feature maps to a set of DisplayAreas
+ // (2) After adding a window, for every feature the window's type belongs to,
+ // it is a descendant of one of the corresponding DisplayAreas of the feature.
+ // (3) Z-order is maintained, i.e. if z-range(area) denotes the set of layers of windows
+ // within a DisplayArea:
+ // for every pair of DisplayArea siblings (a,b), where a is below b, it holds that
+ // max(z-range(a)) <= min(z-range(b))
+ //
+ // The algorithm below iteratively creates such a hierarchy:
+ // - Initially, all windows are attached to the root.
+ // - For each feature we create a set of DisplayAreas, by looping over the layers
+ // - if the feature does apply to the current layer, we need to find a DisplayArea
+ // for it to satisfy (2)
+ // - we can re-use the previous layer's area if:
+ // the current feature also applies to the previous layer, (to satisfy (3))
+ // and the last feature that applied to the previous layer is the same as
+ // the last feature that applied to the current layer (to satisfy (2))
+ // - otherwise we create a new DisplayArea below the last feature that applied
+ // to the current layer
+
+
+ PendingArea[] areaForLayer = new PendingArea[mMaxWindowLayer];
+ final PendingArea root = new PendingArea(null, 0, null);
+ Arrays.fill(areaForLayer, root);
+
+ final int size = mFeatures.size();
+ for (int i = 0; i < size; i++) {
+ PendingArea featureArea = null;
+ for (int layer = 0; layer < mMaxWindowLayer; layer++) {
+ final Feature feature = mFeatures.get(i);
+ if (feature.mWindowLayers[layer]) {
+ if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
+ // No suitable DisplayArea - create a new one under the previous area
+ // for this layer.
+ featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
+ areaForLayer[layer].mChildren.add(featureArea);
+ }
+ areaForLayer[layer] = featureArea;
+ } else {
+ featureArea = null;
+ }
+ }
+ }
+
+ PendingArea leafArea = null;
+ int leafType = LEAF_TYPE_TOKENS;
+ for (int layer = 0; layer < mMaxWindowLayer; layer++) {
+ int type = typeOfLayer(mWmService.mPolicy, layer);
+ if (leafArea == null || leafArea.mParent != areaForLayer[layer]
+ || type != leafType) {
+ leafArea = new PendingArea(null, layer, areaForLayer[layer]);
+ areaForLayer[layer].mChildren.add(leafArea);
+ leafType = type;
+ if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
+ leafArea.mExisting = mTaskContainers;
+ } else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
+ leafArea.mExisting = mImeContainer;
+ }
+ }
+ leafArea.mMaxLayer = layer;
+ }
+ root.computeMaxLayer();
+ root.instantiateChildren(mRoot, mAreaForLayer, 0, mAreas);
+ }
+
+ @Override
+ public void addWindow(WindowToken token) {
+ DisplayArea.Tokens area = findAreaForToken(token);
+ area.addChild(token);
+ }
+
+ @VisibleForTesting
+ DisplayArea.Tokens findAreaForToken(WindowToken token) {
+ int windowLayerFromType = token.getWindowLayerFromType();
+ if (windowLayerFromType == APPLICATION_LAYER) {
+ // TODO(display-area): Better handle AboveAppWindows in APPLICATION_LAYER
+ windowLayerFromType += 1;
+ } else if (token.mRoundedCornerOverlay) {
+ windowLayerFromType = mMaxWindowLayer - 1;
+ }
+ return mAreaForLayer[windowLayerFromType];
+ }
+
+ public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(Feature feature) {
+ return mAreas.get(feature);
+ }
+
+ private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
+ if (layer == APPLICATION_LAYER) {
+ return LEAF_TYPE_TASK_CONTAINERS;
+ } else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)
+ || layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) {
+ return LEAF_TYPE_IME_CONTAINERS;
+ } else {
+ return LEAF_TYPE_TOKENS;
+ }
+ }
+ }
+
+ DisplayAreaPolicyBuilder addFeature(Feature feature) {
+ mFeatures.add(feature);
+ return this;
+ }
+
+ Result build(WindowManagerService wmService,
+ DisplayContent content, DisplayArea.Root root,
+ DisplayArea<? extends WindowContainer> imeContainer,
+ DisplayArea<? extends ActivityStack> taskContainers) {
+
+ return new Result(wmService, content, root, imeContainer, taskContainers, new ArrayList<>(
+ mFeatures));
+ }
+
+ static class PendingArea {
+ final int mMinLayer;
+ final ArrayList<PendingArea> mChildren = new ArrayList<>();
+ final Feature mFeature;
+ final PendingArea mParent;
+ int mMaxLayer;
+ DisplayArea mExisting;
+
+ PendingArea(Feature feature,
+ int minLayer,
+ PendingArea parent) {
+ mMinLayer = minLayer;
+ mFeature = feature;
+ mParent = parent;
+ }
+
+ int computeMaxLayer() {
+ for (int i = 0; i < mChildren.size(); i++) {
+ mMaxLayer = Math.max(mMaxLayer, mChildren.get(i).computeMaxLayer());
+ }
+ return mMaxLayer;
+ }
+
+ void instantiateChildren(DisplayArea<DisplayArea> parent,
+ DisplayArea.Tokens[] areaForLayer, int level, Map<Feature, List<DisplayArea<?
+ extends WindowContainer>>> areas) {
+ mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));
+ for (int i = 0; i < mChildren.size(); i++) {
+ final PendingArea child = mChildren.get(i);
+ final DisplayArea area = child.createArea(parent, areaForLayer);
+ parent.addChild(area, WindowContainer.POSITION_TOP);
+ if (mFeature != null) {
+ areas.get(mFeature).add(area);
+ }
+ child.instantiateChildren(area, areaForLayer, level + 1, areas);
+ }
+ }
+
+ private DisplayArea createArea(DisplayArea<DisplayArea> parent,
+ DisplayArea.Tokens[] areaForLayer) {
+ if (mExisting != null) {
+ return mExisting;
+ }
+ DisplayArea.Type type;
+ if (mMinLayer > APPLICATION_LAYER) {
+ type = DisplayArea.Type.ABOVE_TASKS;
+ } else if (mMaxLayer < APPLICATION_LAYER) {
+ type = DisplayArea.Type.BELOW_TASKS;
+ } else {
+ type = DisplayArea.Type.ANY;
+ }
+ if (mFeature == null) {
+ final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type,
+ "Leaf:" + mMinLayer + ":" + mMaxLayer);
+ for (int i = mMinLayer; i <= mMaxLayer; i++) {
+ areaForLayer[i] = leaf;
+ }
+ return leaf;
+ } else {
+ return new DisplayArea(parent.mWmService, type, mFeature.mName + ":"
+ + mMinLayer + ":" + mMaxLayer);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3b658c0..b0c6c64 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
@@ -84,9 +83,6 @@
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
-import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
-import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -105,12 +101,15 @@
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;
import static com.android.server.wm.DisplayContentProto.ID;
import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.OVERLAY_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
+import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
import static com.android.server.wm.DisplayContentProto.TASKS;
import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
@@ -1938,6 +1937,20 @@
return mTaskContainers.getRootHomeTask();
}
+ /**
+ * Returns the existing home stack or creates and returns a new one if it should exist for the
+ * display.
+ */
+ @Nullable
+ ActivityStack getOrCreateRootHomeTask() {
+ ActivityStack homeTask = getRootHomeTask();
+ if (homeTask == null && supportsSystemDecorations() && !isUntrustedVirtualDisplay()) {
+ homeTask = createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME,
+ false /* onTop */);
+ }
+ return homeTask;
+ }
+
/** @return The primary split-screen task, and {@code null} otherwise. */
ActivityStack getRootSplitScreenPrimaryTask() {
return mTaskContainers.getRootSplitScreenPrimaryTask();
@@ -3255,9 +3268,9 @@
mWmService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
mInputMethodWindow.getDisplayId());
}
- computeImeTarget(true /* updateImeTarget */);
mInsetsStateController.getSourceProvider(ITYPE_IME).setWindow(win,
null /* frameProvider */, null /* imeFrameProvider */);
+ computeImeTarget(true /* updateImeTarget */);
}
/**
@@ -3817,6 +3830,10 @@
}
prepareSurfaces();
+ // This should be called after the insets have been dispatched to clients and we have
+ // committed finish drawing windows.
+ mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
+
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
mLastHasContent,
@@ -4255,9 +4272,6 @@
}
ActivityStack getRootHomeTask() {
- if (mRootHomeTask == null && mDisplayId == DEFAULT_DISPLAY) {
- Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
- }
return mRootHomeTask;
}
@@ -6008,7 +6022,7 @@
} finally {
final ActivityStack topFullscreenStack =
getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
- final ActivityStack homeStack = getRootHomeTask();
+ final ActivityStack homeStack = getOrCreateRootHomeTask();
if (topFullscreenStack != null && homeStack != null && !isTopStack(homeStack)) {
// Whenever split-screen is dismissed we want the home stack directly behind the
// current top fullscreen stack so it shows up when the top stack is finished.
@@ -6574,7 +6588,7 @@
}
void moveHomeStackToFront(String reason) {
- final ActivityStack homeStack = getRootHomeTask();
+ final ActivityStack homeStack = getOrCreateRootHomeTask();
if (homeStack != null) {
homeStack.moveToFront(reason);
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 3ff1b95..370d125 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -39,13 +39,6 @@
}
/**
- * Called when Insets have been dispatched to client. This gets called just after onPostLayout.
- */
- void onPostInsetsDispatched() {
- checkShowImePostLayout();
- }
-
- /**
* Called from {@link WindowManagerInternal#showImePostLayout} when {@link InputMethodService}
* requests to show IME on {@param imeTarget}.
*
@@ -80,7 +73,7 @@
};
}
- private void checkShowImePostLayout() {
+ void checkShowImePostLayout() {
// check if IME is drawn
if (mIsImeLayoutDrawn
|| (mImeTargetFromIme != null
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 2d7d3f1..a8fe349 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -168,7 +168,6 @@
mLastState.set(mState, true /* copySources */);
notifyInsetsChanged();
}
- getImeSourceProvider().onPostInsetsDispatched();
}
void onInsetsModified(InsetsControlTarget windowState, InsetsState state) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2196d89..2596452 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1998,8 +1998,6 @@
removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
mUserStackInFront.put(mCurrentUser, focusStackId);
- final int restoreStackId =
- mUserStackInFront.get(userId, getDefaultDisplay().getRootHomeTask().getRootTaskId());
mCurrentUser = userId;
mStackSupervisor.mStartingUsers.add(uss);
@@ -2008,16 +2006,13 @@
for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getStackAt(stackNdx);
stack.switchUser(userId);
- Task task = stack.getTopMostTask();
- if (task != null && task != stack) {
- stack.positionChildAtTop(task);
- }
}
}
+ final int restoreStackId = mUserStackInFront.get(userId);
ActivityStack stack = getStack(restoreStackId);
if (stack == null) {
- stack = getDefaultDisplay().getRootHomeTask();
+ stack = getDefaultDisplay().getOrCreateRootHomeTask();
}
final boolean homeInFront = stack.isActivityTypeHome();
if (stack.isOnHomeDisplay()) {
@@ -2039,8 +2034,11 @@
*/
void updateUserStack(int userId, ActivityStack stack) {
if (userId != mCurrentUser) {
- mUserStackInFront.put(userId, stack != null ? stack.getRootTaskId()
- : getDefaultDisplay().getRootHomeTask().getRootTaskId());
+ if (stack == null) {
+ stack = getDefaultDisplay().getOrCreateRootHomeTask();
+ }
+
+ mUserStackInFront.put(userId, stack.getRootTaskId());
}
}
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 2dde0ba..f715d8f 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -59,6 +59,7 @@
private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 24;
private static final int NOTIFY_SINGLE_TASK_DISPLAY_EMPTY = 25;
private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 26;
+ private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 27;
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -179,6 +180,10 @@
l.onRecentTaskListFrozenChanged(m.arg1 != 0);
};
+ private final TaskStackConsumer mNotifyTaskFocusChanged = (l, m) -> {
+ l.onTaskFocusChanged(m.arg1, m.arg2 != 0);
+ };
+
@FunctionalInterface
public interface TaskStackConsumer {
void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -273,6 +278,9 @@
case NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG:
forAllRemoteListeners(mNotifyTaskListFrozen, msg);
break;
+ case NOTIFY_TASK_FOCUS_CHANGED_MSG:
+ forAllRemoteListeners(mNotifyTaskFocusChanged, msg);
+ break;
}
}
}
@@ -565,4 +573,12 @@
forAllLocalListeners(mNotifyTaskListFrozen, msg);
msg.sendToTarget();
}
+
+ /** @see ITaskStackListener#onTaskFocusChanged(int, boolean) */
+ void notifyTaskFocusChanged(int taskId, boolean focused) {
+ final Message msg = mHandler.obtainMessage(NOTIFY_TASK_FOCUS_CHANGED_MSG,
+ taskId, focused ? 1 : 0);
+ forAllLocalListeners(mNotifyTaskFocusChanged, msg);
+ msg.sendToTarget();
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8f9caea..2bb6703 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7317,9 +7317,11 @@
Slog.w(TAG_WM, "updateInputMethodTargetWindow: imeToken=" + imeToken
+ " imeTargetWindowToken=" + imeTargetWindowToken);
}
- final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
- if (imeTarget != null) {
- imeTarget.getDisplayContent().updateImeControlTarget(imeTarget);
+ synchronized (mGlobalLock) {
+ final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
+ if (imeTarget != null) {
+ imeTarget.getDisplayContent().updateImeControlTarget(imeTarget);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b336f8d..46081c5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1493,7 +1493,8 @@
// Some system windows (e.g. "Power off" dialog) don't have a task, but we would still
// associate them with some stack to enable dimming.
final DisplayContent dc = getDisplayContent();
- return mAttrs.type >= FIRST_SYSTEM_WINDOW && dc != null ? dc.getRootHomeTask() : null;
+ return mAttrs.type >= FIRST_SYSTEM_WINDOW && dc != null
+ ? dc.getOrCreateRootHomeTask() : null;
}
/**
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 7e6f79f..70a9c09 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -18,18 +18,27 @@
#define LOG_TAG "PackageManagerShellCommandDataLoader-jni"
#include <android-base/logging.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
-#include <nativehelper/JNIHelp.h>
-#include "android-base/file.h"
+#include <cutils/trace.h>
+#include <sys/eventfd.h>
+#include <sys/poll.h>
-#include <endian.h>
+#include <nativehelper/JNIHelp.h>
#include <core_jni_helpers.h>
+#include <endian.h>
#include "dataloader.h"
+#include <charconv>
#include <chrono>
+#include <span>
+#include <string>
#include <thread>
+#include <unordered_map>
+#include <unordered_set>
namespace android {
@@ -39,9 +48,26 @@
using android::base::ReadFully;
using android::base::unique_fd;
+using namespace std::literals;
+
+using BlockSize = int16_t;
+using FileIdx = int16_t;
+using BlockIdx = int32_t;
+using NumBlocks = int32_t;
+using CompressionType = int16_t;
+using RequestType = int16_t;
+using MagicType = uint32_t;
+
static constexpr int BUFFER_SIZE = 256 * 1024;
static constexpr int BLOCKS_COUNT = BUFFER_SIZE / INCFS_DATA_FILE_BLOCK_SIZE;
+static constexpr int COMMAND_SIZE = 4 + 2 + 2 + 4; // bytes
+static constexpr int HEADER_SIZE = 2 + 2 + 4 + 2; // bytes
+static constexpr std::string_view OKAY = "OKAY"sv;
+static constexpr MagicType INCR = 0x52434e49; // BE INCR
+
+static constexpr auto PollTimeoutMs = 5000;
+
struct JniIds {
jclass packageManagerShellCommandDataLoader;
jmethodID pmscdLookupShellCommand;
@@ -85,6 +111,70 @@
return ids;
}
+struct BlockHeader {
+ FileIdx fileIdx = -1;
+ CompressionType compressionType = -1;
+ BlockIdx blockIdx = -1;
+ BlockSize blockSize = -1;
+} __attribute__((packed));
+
+static_assert(sizeof(BlockHeader) == HEADER_SIZE);
+
+static constexpr RequestType EXIT = 0;
+static constexpr RequestType BLOCK_MISSING = 1;
+static constexpr RequestType PREFETCH = 2;
+
+struct RequestCommand {
+ MagicType magic;
+ RequestType requestType;
+ FileIdx fileIdx;
+ BlockIdx blockIdx;
+} __attribute__((packed));
+
+static_assert(COMMAND_SIZE == sizeof(RequestCommand));
+
+static bool sendRequest(int fd, RequestType requestType, FileIdx fileIdx = -1,
+ BlockIdx blockIdx = -1) {
+ const RequestCommand command{.magic = INCR,
+ .requestType = static_cast<int16_t>(be16toh(requestType)),
+ .fileIdx = static_cast<int16_t>(be16toh(fileIdx)),
+ .blockIdx = static_cast<int32_t>(be32toh(blockIdx))};
+ return android::base::WriteFully(fd, &command, sizeof(command));
+}
+
+static int waitForDataOrSignal(int fd, int event_fd) {
+ struct pollfd pfds[2] = {{fd, POLLIN, 0}, {event_fd, POLLIN, 0}};
+ // Wait indefinitely until either data is ready or stop signal is received
+ int res = poll(pfds, 2, PollTimeoutMs);
+ if (res <= 0) {
+ return res;
+ }
+ // First check if there is a stop signal
+ if (pfds[1].revents == POLLIN) {
+ return event_fd;
+ }
+ // Otherwise check if incoming data is ready
+ if (pfds[0].revents == POLLIN) {
+ return fd;
+ }
+ return -1;
+}
+
+static bool readChunk(int fd, std::vector<uint8_t>& data) {
+ int32_t size;
+ if (!android::base::ReadFully(fd, &size, sizeof(size))) {
+ return false;
+ }
+ size = int32_t(be32toh(size));
+ if (size <= 0) {
+ return false;
+ }
+ data.resize(size);
+ return android::base::ReadFully(fd, data.data(), data.size());
+}
+
+BlockHeader readHeader(std::span<uint8_t>& data);
+
static inline int32_t readBEInt32(borrowed_fd fd) {
int32_t result;
ReadFully(fd, &result, sizeof(result));
@@ -106,6 +196,22 @@
return readBEInt32(fd); // size of the verity tree
}
+static inline IncFsSize verityTreeSizeForFile(IncFsSize fileSize) {
+ constexpr int SHA256_DIGEST_SIZE = 32;
+ constexpr int digest_size = SHA256_DIGEST_SIZE;
+ constexpr int hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
+
+ IncFsSize total_tree_block_count = 0;
+
+ auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
+ auto hash_block_count = block_count;
+ for (auto i = 0; hash_block_count > 1; i++) {
+ hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
+ total_tree_block_count += hash_block_count;
+ }
+ return total_tree_block_count * INCFS_DATA_FILE_BLOCK_SIZE;
+}
+
static inline unique_fd convertPfdToFdAndDup(JNIEnv* env, const JniIds& jni, jobject pfd) {
if (!pfd) {
ALOGE("Missing In ParcelFileDescriptor.");
@@ -125,8 +231,9 @@
struct InputDesc {
unique_fd fd;
IncFsSize size;
- IncFsBlockKind kind;
- bool waitOnEof;
+ IncFsBlockKind kind = INCFS_BLOCK_KIND_DATA;
+ bool waitOnEof = false;
+ bool streaming = false;
};
using InputDescs = std::vector<InputDesc>;
@@ -135,8 +242,7 @@
InputDescs result;
result.reserve(2);
- const bool fromStdin = (metadata.size == 0 || *metadata.data == '-');
- if (fromStdin) {
+ if (metadata.size == 0 || *metadata.data == '-') {
// stdin
auto fd = convertPfdToFdAndDup(
env, jni,
@@ -146,12 +252,29 @@
result.push_back(InputDesc{
.fd = std::move(fd),
.size = size,
- .kind = INCFS_BLOCK_KIND_DATA,
.waitOnEof = true,
});
}
return result;
}
+ if (*metadata.data == '+') {
+ // verity tree from stdin, rest is streaming
+ auto fd = convertPfdToFdAndDup(
+ env, jni,
+ env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
+ jni.pmscdGetStdInPFD, shellCommand));
+ if (fd.ok()) {
+ auto treeSize = verityTreeSizeForFile(size);
+ result.push_back(InputDesc{
+ .fd = std::move(fd),
+ .size = treeSize,
+ .kind = INCFS_BLOCK_KIND_HASH,
+ .waitOnEof = true,
+ .streaming = true,
+ });
+ }
+ return result;
+ }
// local file and possibly signature
const std::string filePath(metadata.data, metadata.size);
@@ -163,13 +286,17 @@
jni.pmscdGetLocalFile, shellCommand,
env->NewStringUTF(idsigPath.c_str())));
if (idsigFd.ok()) {
- ALOGE("idsig found, skipping to the tree");
- auto treeSize = skipIdSigHeaders(idsigFd);
+ auto treeSize = verityTreeSizeForFile(size);
+ auto actualTreeSize = skipIdSigHeaders(idsigFd);
+ if (treeSize != actualTreeSize) {
+ ALOGE("Verity tree size mismatch: %d vs .idsig: %d.", int(treeSize),
+ int(actualTreeSize));
+ return {};
+ }
result.push_back(InputDesc{
.fd = std::move(idsigFd),
.size = treeSize,
.kind = INCFS_BLOCK_KIND_HASH,
- .waitOnEof = false,
});
}
@@ -182,8 +309,6 @@
result.push_back(InputDesc{
.fd = std::move(fileFd),
.size = size,
- .kind = INCFS_BLOCK_KIND_DATA,
- .waitOnEof = false,
});
}
@@ -226,19 +351,32 @@
android::dataloader::StatusListenerPtr statusListener,
android::dataloader::ServiceConnectorPtr,
android::dataloader::ServiceParamsPtr) final {
+ CHECK(ifs) << "ifs can't be null";
+ CHECK(statusListener) << "statusListener can't be null";
mArgs = params.arguments();
mIfs = ifs;
+ mStatusListener = statusListener;
return true;
}
bool onStart() final { return true; }
- void onStop() final {}
- void onDestroy() final {}
+ void onStop() final {
+ mStopReceiving = true;
+ eventfd_write(mEventFd, 1);
+ if (mReceiverThread.joinable()) {
+ mReceiverThread.join();
+ }
+ }
+ void onDestroy() final {
+ ALOGE("Sending EXIT to server.");
+ sendRequest(mOutFd, EXIT);
+ // Make sure the receiver thread stopped.
+ CHECK(!mReceiverThread.joinable());
- // IFS callbacks.
- void onPendingReads(const dataloader::PendingReads& pendingReads) final {}
- void onPageReads(const dataloader::PageReads& pageReads) final {}
+ mInFd.reset();
+ mOutFd.reset();
+ }
- // FS callbacks.
+ // Installation.
bool onPrepareImage(const dataloader::DataLoaderInstallationFiles& addedFiles) final {
JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
const auto& jni = jniIds(env);
@@ -257,6 +395,7 @@
std::vector<IncFsDataBlock> blocks;
blocks.reserve(BLOCKS_COUNT);
+ unique_fd streamingFd;
for (auto&& file : addedFiles) {
auto inputs = openInputs(env, jni, shellCommand, file.size, file.metadata);
if (inputs.empty()) {
@@ -267,7 +406,6 @@
}
const auto fileId = IncFs_FileIdFromMetadata(file.metadata);
-
const auto incfsFd(mIfs->openWrite(fileId));
if (incfsFd < 0) {
ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. "
@@ -277,6 +415,9 @@
}
for (auto&& input : inputs) {
+ if (input.streaming && !streamingFd.ok()) {
+ streamingFd.reset(dup(input.fd));
+ }
if (!copyToIncFs(incfsFd, input.size, input.kind, input.fd, input.waitOnEof,
&buffer, &blocks)) {
ALOGE("Failed to copy data to IncFS file for metadata: %.*s, final file name "
@@ -288,7 +429,12 @@
}
}
- ALOGE("All done.");
+ if (streamingFd.ok()) {
+ ALOGE("onPrepareImage: done, proceeding to streaming.");
+ return initStreaming(std::move(streamingFd));
+ }
+
+ ALOGE("onPrepareImage: done.");
return true;
}
@@ -378,11 +524,253 @@
return true;
}
+ // Read tracing.
+ struct TracedRead {
+ uint64_t timestampUs;
+ android::dataloader::FileId fileId;
+ uint32_t firstBlockIdx;
+ uint32_t count;
+ };
+
+ void onPageReads(const android::dataloader::PageReads& pageReads) final {
+ auto trace = atrace_is_tag_enabled(ATRACE_TAG);
+ if (CC_LIKELY(!trace)) {
+ return;
+ }
+
+ TracedRead last = {};
+ for (auto&& read : pageReads) {
+ if (read.id != last.fileId || read.block != last.firstBlockIdx + last.count) {
+ traceRead(last);
+ last = TracedRead{
+ .timestampUs = read.bootClockTsUs,
+ .fileId = read.id,
+ .firstBlockIdx = (uint32_t)read.block,
+ .count = 1,
+ };
+ } else {
+ ++last.count;
+ }
+ }
+ traceRead(last);
+ }
+
+ void traceRead(const TracedRead& read) {
+ if (!read.count) {
+ return;
+ }
+
+ FileIdx fileIdx = convertFileIdToFileIndex(read.fileId);
+ auto str = android::base::StringPrintf("page_read: index=%lld count=%lld file=%d",
+ static_cast<long long>(read.firstBlockIdx),
+ static_cast<long long>(read.count),
+ static_cast<int>(fileIdx));
+ ATRACE_BEGIN(str.c_str());
+ ATRACE_END();
+ }
+
+ // Streaming.
+ bool initStreaming(unique_fd inout) {
+ mInFd.reset(dup(inout));
+ mOutFd.reset(dup(inout));
+ if (mInFd < 0 || mOutFd < 0) {
+ ALOGE("Failed to dup FDs.");
+ return false;
+ }
+
+ mEventFd.reset(eventfd(0, EFD_CLOEXEC));
+ if (mEventFd < 0) {
+ ALOGE("Failed to create eventfd.");
+ return false;
+ }
+
+ // Awaiting adb handshake.
+ char okay_buf[OKAY.size()];
+ if (!android::base::ReadFully(mInFd, okay_buf, OKAY.size())) {
+ ALOGE("Failed to receive OKAY. Abort.");
+ return false;
+ }
+ if (std::string_view(okay_buf, OKAY.size()) != OKAY) {
+ ALOGE("Received '%.*s', expecting '%.*s'", (int)OKAY.size(), okay_buf, (int)OKAY.size(),
+ OKAY.data());
+ return false;
+ }
+
+ mReceiverThread = std::thread([this]() { receiver(); });
+ ALOGI("Started streaming...");
+ return true;
+ }
+
+ // IFS callbacks.
+ void onPendingReads(const dataloader::PendingReads& pendingReads) final {
+ CHECK(mIfs);
+ for (auto&& pendingRead : pendingReads) {
+ const android::dataloader::FileId& fileId = pendingRead.id;
+ const auto blockIdx = static_cast<BlockIdx>(pendingRead.block);
+ /*
+ ALOGI("Missing: %d", (int) blockIdx);
+ */
+ FileIdx fileIdx = convertFileIdToFileIndex(fileId);
+ if (fileIdx < 0) {
+ ALOGE("Failed to handle event for fileid=%s. Ignore.",
+ android::incfs::toString(fileId).c_str());
+ continue;
+ }
+ if (mRequestedFiles.insert(fileIdx).second) {
+ if (!sendRequest(mOutFd, PREFETCH, fileIdx, blockIdx)) {
+ ALOGE("Failed to request prefetch for fileid=%s. Ignore.",
+ android::incfs::toString(fileId).c_str());
+ mRequestedFiles.erase(fileIdx);
+ mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
+ }
+ }
+ sendRequest(mOutFd, BLOCK_MISSING, fileIdx, blockIdx);
+ }
+ }
+
+ void receiver() {
+ std::vector<uint8_t> data;
+ std::vector<IncFsDataBlock> instructions;
+ std::unordered_map<FileIdx, unique_fd> writeFds;
+ while (!mStopReceiving) {
+ const int res = waitForDataOrSignal(mInFd, mEventFd);
+ if (res == 0) {
+ continue;
+ }
+ if (res < 0) {
+ ALOGE("Failed to poll. Abort.");
+ mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
+ break;
+ }
+ if (res == mEventFd) {
+ ALOGE("Received stop signal. Exit.");
+ break;
+ }
+ if (!readChunk(mInFd, data)) {
+ ALOGE("Failed to read a message. Abort.");
+ mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
+ break;
+ }
+ auto remainingData = std::span(data);
+ while (!remainingData.empty()) {
+ auto header = readHeader(remainingData);
+ if (header.fileIdx == -1 && header.compressionType == 0 && header.blockIdx == 0 &&
+ header.blockSize == 0) {
+ ALOGI("Stop signal received. Sending exit command (remaining bytes: %d).",
+ int(remainingData.size()));
+
+ sendRequest(mOutFd, EXIT);
+ mStopReceiving = true;
+ break;
+ }
+ if (header.fileIdx < 0 || header.blockSize <= 0 || header.compressionType < 0 ||
+ header.blockIdx < 0) {
+ ALOGE("invalid header received. Abort.");
+ mStopReceiving = true;
+ break;
+ }
+ const FileIdx fileIdx = header.fileIdx;
+ const android::dataloader::FileId fileId = convertFileIndexToFileId(fileIdx);
+ if (!android::incfs::isValidFileId(fileId)) {
+ ALOGE("Unknown data destination for file ID %d. "
+ "Ignore.",
+ header.fileIdx);
+ continue;
+ }
+
+ auto& writeFd = writeFds[fileIdx];
+ if (writeFd < 0) {
+ writeFd = this->mIfs->openWrite(fileId);
+ if (writeFd < 0) {
+ ALOGE("Failed to open file %d for writing (%d). Aboring.", header.fileIdx,
+ -writeFd);
+ break;
+ }
+ }
+
+ const auto inst = IncFsDataBlock{
+ .fileFd = writeFd,
+ .pageIndex = static_cast<IncFsBlockIndex>(header.blockIdx),
+ .compression = static_cast<IncFsCompressionKind>(header.compressionType),
+ .kind = INCFS_BLOCK_KIND_DATA,
+ .dataSize = static_cast<uint16_t>(header.blockSize),
+ .data = (const char*)remainingData.data(),
+ };
+ instructions.push_back(inst);
+ remainingData = remainingData.subspan(header.blockSize);
+ }
+ writeInstructions(instructions);
+ }
+ writeInstructions(instructions);
+ }
+
+ void writeInstructions(std::vector<IncFsDataBlock>& instructions) {
+ auto res = this->mIfs->writeBlocks(instructions);
+ if (res != instructions.size()) {
+ ALOGE("Dailed to write data to Incfs (res=%d when expecting %d)", res,
+ int(instructions.size()));
+ }
+ instructions.clear();
+ }
+
+ FileIdx convertFileIdToFileIndex(android::dataloader::FileId fileId) {
+ // FileId is a string in format '+FileIdx\0'.
+ const char* meta = (const char*)&fileId;
+ if (*meta != '+') {
+ return -1;
+ }
+
+ int fileIdx;
+ auto res = std::from_chars(meta + 1, meta + sizeof(fileId), fileIdx);
+ if (res.ec != std::errc{} || fileIdx < std::numeric_limits<FileIdx>::min() ||
+ fileIdx > std::numeric_limits<FileIdx>::max()) {
+ return -1;
+ }
+
+ return FileIdx(fileIdx);
+ }
+
+ android::dataloader::FileId convertFileIndexToFileId(FileIdx fileIdx) {
+ IncFsFileId fileId = {};
+ char* meta = (char*)&fileId;
+ *meta = '+';
+ if (auto [p, ec] = std::to_chars(meta + 1, meta + sizeof(fileId), fileIdx);
+ ec != std::errc()) {
+ return {};
+ }
+ return fileId;
+ }
+
JavaVM* const mJvm;
std::string mArgs;
- android::dataloader::FilesystemConnectorPtr mIfs;
+ android::dataloader::FilesystemConnectorPtr mIfs = nullptr;
+ android::dataloader::StatusListenerPtr mStatusListener = nullptr;
+ android::base::unique_fd mInFd;
+ android::base::unique_fd mOutFd;
+ android::base::unique_fd mEventFd;
+ std::thread mReceiverThread;
+ std::atomic<bool> mStopReceiving = false;
+ /** Tracks which files have been requested */
+ std::unordered_set<FileIdx> mRequestedFiles;
};
+BlockHeader readHeader(std::span<uint8_t>& data) {
+ BlockHeader header;
+ if (data.size() < sizeof(header)) {
+ return header;
+ }
+
+ header.fileIdx = static_cast<FileIdx>(be16toh(*reinterpret_cast<const uint16_t*>(&data[0])));
+ header.compressionType =
+ static_cast<CompressionType>(be16toh(*reinterpret_cast<const uint16_t*>(&data[2])));
+ header.blockIdx = static_cast<BlockIdx>(be32toh(*reinterpret_cast<const uint32_t*>(&data[4])));
+ header.blockSize =
+ static_cast<BlockSize>(be16toh(*reinterpret_cast<const uint16_t*>(&data[8])));
+ data = data.subspan(sizeof(header));
+
+ return header;
+}
+
static void nativeInitialize(JNIEnv* env, jclass klass) {
jniIds(env);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e469067..aa5dafc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -57,6 +57,7 @@
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
+import static android.app.admin.DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
@@ -522,7 +523,8 @@
/** Keyguard features that are allowed to be set on a managed profile */
private static final int PROFILE_KEYGUARD_FEATURES =
- PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER | PROFILE_KEYGUARD_FEATURES_PROFILE_ONLY;
+ NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER
+ | PROFILE_KEYGUARD_FEATURES_PROFILE_ONLY;
private static final int DEVICE_ADMIN_DEACTIVATE_TIMEOUT = 10000;
@@ -8168,16 +8170,20 @@
}
Objects.requireNonNull(who, "ComponentName is null");
final int userHandle = mInjector.userHandleGetCallingUserId();
- if (isManagedProfile(userHandle)) {
- if (parent) {
- which = which & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
- } else {
- which = which & PROFILE_KEYGUARD_FEATURES;
- }
- }
synchronized (getLockObject()) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES, parent);
+ if (isManagedProfile(userHandle)) {
+ if (parent) {
+ if (isProfileOwnerOfOrganizationOwnedDevice(ap)) {
+ which = which & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+ } else {
+ which = which & NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+ }
+ } else {
+ which = which & PROFILE_KEYGUARD_FEATURES;
+ }
+ }
if (ap.disabledKeyguardFeatures != which) {
ap.disabledKeyguardFeatures = which;
saveSettingsLocked(userHandle);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 93662c9..1936f13 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1741,7 +1741,7 @@
mSystemServiceManager.startService(SensorNotificationService.class);
t.traceEnd();
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CONTEXTHUB)) {
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CONTEXT_HUB)) {
t.traceBegin("StartContextHubSystemService");
mSystemServiceManager.startService(ContextHubSystemService.class);
t.traceEnd();
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 2d18a29..663bf4f 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.UserIdInt;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionSessionId;
import android.app.prediction.AppTarget;
@@ -24,6 +25,7 @@
import android.app.prediction.IPredictionCallback;
import android.content.Context;
import android.content.pm.ParceledListSlice;
+import android.os.CancellationSignal;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
@@ -138,6 +140,21 @@
});
}
+ @Override
+ public void pruneDataForUser(@UserIdInt int userId, @NonNull CancellationSignal signal) {
+ mDataManager.pruneDataForUser(userId, signal);
+ }
+
+ @Override
+ public byte[] backupConversationInfos(@UserIdInt int userId) {
+ return new byte[0];
+ }
+
+ @Override
+ public void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
+ @NonNull byte[] payload) {
+ }
+
@VisibleForTesting
SessionInfo getSessionInfo(AppPredictionSessionId sessionId) {
return mSessions.get(sessionId);
@@ -160,14 +177,5 @@
Slog.e(TAG, "Failed to calling callback" + e);
}
}
-
- @Override
- public byte[] backupConversationInfos(int userId) {
- return new byte[0];
- }
-
- @Override
- public void restoreConversationInfos(int userId, String key, byte[] payload) {
- }
}
}
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 ea36d38..3afb209 100644
--- a/services/people/java/com/android/server/people/data/ConversationStore.java
+++ b/services/people/java/com/android/server/people/data/ConversationStore.java
@@ -133,10 +133,11 @@
}
@MainThread
- synchronized void deleteConversation(@NonNull String shortcutId) {
+ @Nullable
+ synchronized ConversationInfo deleteConversation(@NonNull String shortcutId) {
ConversationInfo conversationInfo = mConversationInfoMap.remove(shortcutId);
if (conversationInfo == null) {
- return;
+ return null;
}
LocusId locusId = conversationInfo.getLocusId();
@@ -159,6 +160,7 @@
mNotifChannelIdToShortcutIdMap.remove(notifChannelId);
}
scheduleUpdateConversationsOnDisk();
+ return conversationInfo;
}
synchronized void forAllConversations(@NonNull Consumer<ConversationInfo> consumer) {
diff --git a/services/people/java/com/android/server/people/data/DataMaintenanceService.java b/services/people/java/com/android/server/people/data/DataMaintenanceService.java
new file mode 100644
index 0000000..58f0654
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/DataMaintenanceService.java
@@ -0,0 +1,92 @@
+/*
+ * 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.people.data;
+
+import android.annotation.UserIdInt;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.CancellationSignal;
+
+import com.android.server.LocalServices;
+import com.android.server.people.PeopleServiceInternal;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This service runs periodically to ensure the data consistency and the retention policy for a
+ * specific user's data.
+ */
+public class DataMaintenanceService extends JobService {
+
+ private static final long JOB_RUN_INTERVAL = TimeUnit.HOURS.toMillis(24);
+
+ /** This job ID must be unique within the system server. */
+ private static final int BASE_JOB_ID = 0xC315BD7; // 204561367
+
+ static void scheduleJob(Context context, @UserIdInt int userId) {
+ int jobId = getJobId(userId);
+ JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ if (jobScheduler.getPendingJob(jobId) == null) {
+ ComponentName component = new ComponentName(context, DataMaintenanceService.class);
+ JobInfo newJob = new JobInfo.Builder(jobId, component)
+ .setRequiresDeviceIdle(true)
+ .setPeriodic(JOB_RUN_INTERVAL)
+ .build();
+ jobScheduler.schedule(newJob);
+ }
+ }
+
+ static void cancelJob(Context context, @UserIdInt int userId) {
+ JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ jobScheduler.cancel(getJobId(userId));
+ }
+
+ private CancellationSignal mSignal;
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ int userId = getUserId(params.getJobId());
+ mSignal = new CancellationSignal();
+ new Thread(() -> {
+ PeopleServiceInternal peopleServiceInternal =
+ LocalServices.getService(PeopleServiceInternal.class);
+ peopleServiceInternal.pruneDataForUser(userId, mSignal);
+ jobFinished(params, mSignal.isCanceled());
+ }).start();
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ if (mSignal != null) {
+ mSignal.cancel();
+ }
+ return false;
+ }
+
+ private static int getJobId(@UserIdInt int userId) {
+ return BASE_JOB_ID + userId;
+ }
+
+ private static @UserIdInt int getUserId(int jobId) {
+ return jobId - BASE_JOB_ID;
+ }
+}
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 a904b42..2edfd74 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -32,6 +32,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutManager.ShareShortcutInfo;
@@ -40,6 +41,7 @@
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Process;
import android.os.RemoteException;
@@ -53,16 +55,20 @@
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
+import android.util.ArraySet;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ChooserActivity;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.telephony.SmsApplication;
import com.android.server.LocalServices;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@@ -94,10 +100,12 @@
private final SparseArray<ScheduledFuture<?>> mUsageStatsQueryFutures = new SparseArray<>();
private final SparseArray<NotificationListenerService> mNotificationListeners =
new SparseArray<>();
+ private final SparseArray<PackageMonitor> mPackageMonitors = new SparseArray<>();
private final ContentObserver mCallLogContentObserver;
private final ContentObserver mMmsSmsContentObserver;
private ShortcutServiceInternal mShortcutServiceInternal;
+ private PackageManagerInternal mPackageManagerInternal;
private ShortcutManager mShortcutManager;
private UserManager mUserManager;
@@ -120,6 +128,7 @@
/** Initialization. Called when the system services are up running. */
public void initialize() {
mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mShortcutManager = mContext.getSystemService(ShortcutManager.class);
mUserManager = mContext.getSystemService(UserManager.class);
@@ -172,6 +181,10 @@
// Should never occur for local calls.
}
+ PackageMonitor packageMonitor = new PerUserPackageMonitor();
+ packageMonitor.register(mContext, null, UserHandle.of(userId), true);
+ mPackageMonitors.put(userId, packageMonitor);
+
if (userId == UserHandle.USER_SYSTEM) {
// The call log and MMS/SMS messages are shared across user profiles. So only need to
// register the content observers once for the primary user.
@@ -183,6 +196,8 @@
MmsSms.CONTENT_URI, /* notifyForDescendants= */ false,
mMmsSmsContentObserver, UserHandle.USER_SYSTEM);
}
+
+ DataMaintenanceService.scheduleJob(mContext, userId);
}
/** This method is called when a user is stopped. */
@@ -207,10 +222,15 @@
// Should never occur for local calls.
}
}
+ if (mPackageMonitors.indexOfKey(userId) >= 0) {
+ mPackageMonitors.get(userId).unregister();
+ }
if (userId == UserHandle.USER_SYSTEM) {
mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver);
mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver);
}
+
+ DataMaintenanceService.cancelJob(mContext, userId);
}
/**
@@ -274,9 +294,8 @@
|| TextUtils.isEmpty(mimeType)) {
return;
}
- EventHistoryImpl eventHistory =
- packageData.getEventStore().getOrCreateShortcutEventHistory(
- shortcutInfo.getId());
+ EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_SHORTCUT_BASED, shortcutInfo.getId());
@Event.EventType int eventType;
if (mimeType.startsWith("text/")) {
eventType = Event.TYPE_SHARE_TEXT;
@@ -291,6 +310,45 @@
}
}
+ /** Prunes the data for the specified user. */
+ public void pruneDataForUser(@UserIdInt int userId, @NonNull CancellationSignal signal) {
+ UserData userData = getUnlockedUserData(userId);
+ if (userData == null || signal.isCanceled()) {
+ return;
+ }
+ pruneUninstalledPackageData(userData);
+
+ long currentTimeMillis = System.currentTimeMillis();
+ userData.forAllPackages(packageData -> {
+ if (signal.isCanceled()) {
+ return;
+ }
+ packageData.getEventStore().pruneOldEvents(currentTimeMillis);
+ if (!packageData.isDefaultDialer()) {
+ packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_CALL);
+ }
+ if (!packageData.isDefaultSmsApp()) {
+ packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_SMS);
+ }
+ packageData.pruneOrphanEvents();
+ });
+ }
+
+ private void pruneUninstalledPackageData(@NonNull UserData userData) {
+ Set<String> installApps = new ArraySet<>();
+ mPackageManagerInternal.forEachInstalledPackage(
+ pkg -> installApps.add(pkg.getPackageName()), userData.getUserId());
+ List<String> packagesToDelete = new ArrayList<>();
+ userData.forAllPackages(packageData -> {
+ if (!installApps.contains(packageData.getPackageName())) {
+ packagesToDelete.add(packageData.getPackageName());
+ }
+ });
+ for (String packageName : packagesToDelete) {
+ userData.deletePackageData(packageName);
+ }
+ }
+
/** Gets a list of {@link ShortcutInfo}s with the given shortcut IDs. */
private List<ShortcutInfo> getShortcuts(
@NonNull String packageName, @UserIdInt int userId,
@@ -347,7 +405,8 @@
|| packageData.getConversationStore().getConversation(shortcutId) == null) {
return null;
}
- return packageData.getEventStore().getOrCreateShortcutEventHistory(shortcutId);
+ return packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
}
@VisibleForTesting
@@ -413,6 +472,11 @@
}
@VisibleForTesting
+ PackageMonitor getPackageMonitorForTesting(@UserIdInt int userId) {
+ return mPackageMonitors.get(userId);
+ }
+
+ @VisibleForTesting
UserData getUserDataForTesting(@UserIdInt int userId) {
return mUserDataArray.get(userId);
}
@@ -499,7 +563,8 @@
return;
}
EventStore eventStore = defaultDialer.getEventStore();
- eventStore.getOrCreateCallEventHistory(phoneNumber).addEvent(event);
+ eventStore.getOrCreateEventHistory(
+ EventStore.CATEGORY_CALL, phoneNumber).addEvent(event);
});
}
}
@@ -544,7 +609,8 @@
return;
}
EventStore eventStore = defaultSmsApp.getEventStore();
- eventStore.getOrCreateSmsEventHistory(phoneNumber).addEvent(event);
+ eventStore.getOrCreateEventHistory(
+ EventStore.CATEGORY_SMS, phoneNumber).addEvent(event);
});
}
}
@@ -670,6 +736,20 @@
}
}
+ private class PerUserPackageMonitor extends PackageMonitor {
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ super.onPackageRemoved(packageName, uid);
+
+ int userId = getChangingUserId();
+ UserData userData = getUnlockedUserData(userId);
+ if (userData != null) {
+ userData.deletePackageData(packageName);
+ }
+ }
+ }
+
private class ShutdownBroadcastReceiver extends BroadcastReceiver {
@Override
diff --git a/services/people/java/com/android/server/people/data/EventHistoryImpl.java b/services/people/java/com/android/server/people/data/EventHistoryImpl.java
index 6b6bd7e..6bef1db 100644
--- a/services/people/java/com/android/server/people/data/EventHistoryImpl.java
+++ b/services/people/java/com/android/server/people/data/EventHistoryImpl.java
@@ -78,6 +78,17 @@
mRecentEvents.add(event);
}
+ void onDestroy() {
+ mEventIndexArray.clear();
+ mRecentEvents.clear();
+ // TODO: STOPSHIP: Delete the data files.
+ }
+
+ /** Deletes the events data that exceeds the retention period. */
+ void pruneOldEvents(long currentTimeMillis) {
+ // TODO: STOPSHIP: Delete the old events data files.
+ }
+
@VisibleForTesting
static class Injector {
diff --git a/services/people/java/com/android/server/people/data/EventList.java b/services/people/java/com/android/server/people/data/EventList.java
index b267d66..d770f91 100644
--- a/services/people/java/com/android/server/people/data/EventList.java
+++ b/services/people/java/com/android/server/people/data/EventList.java
@@ -69,6 +69,10 @@
return result;
}
+ void clear() {
+ mEvents.clear();
+ }
+
/** Returns the first index whose timestamp is greater or equal to the provided timestamp. */
private int firstIndexOnOrAfter(long timestamp) {
int result = mEvents.size();
diff --git a/services/people/java/com/android/server/people/data/EventStore.java b/services/people/java/com/android/server/people/data/EventStore.java
index d6b7a86..c8d44ac 100644
--- a/services/people/java/com/android/server/people/data/EventStore.java
+++ b/services/people/java/com/android/server/people/data/EventStore.java
@@ -16,99 +16,129 @@
package com.android.server.people.data;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.LocusId;
import android.util.ArrayMap;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
/** The store that stores and accesses the events data for a package. */
class EventStore {
- private final EventHistoryImpl mPackageEventHistory = new EventHistoryImpl();
+ /** The events that are queryable with a shortcut ID. */
+ static final int CATEGORY_SHORTCUT_BASED = 0;
- // Shortcut ID -> Event History
- private final Map<String, EventHistoryImpl> mShortcutEventHistoryMap = new ArrayMap<>();
+ /** The events that are queryable with a {@link android.content.LocusId}. */
+ static final int CATEGORY_LOCUS_ID_BASED = 1;
- // Locus ID -> Event History
- private final Map<LocusId, EventHistoryImpl> mLocusEventHistoryMap = new ArrayMap<>();
+ /** The phone call events that are queryable with a phone number. */
+ static final int CATEGORY_CALL = 2;
- // Phone Number -> Event History
- private final Map<String, EventHistoryImpl> mCallEventHistoryMap = new ArrayMap<>();
+ /** The SMS or MMS events that are queryable with a phone number. */
+ static final int CATEGORY_SMS = 3;
- // Phone Number -> Event History
- private final Map<String, EventHistoryImpl> mSmsEventHistoryMap = new ArrayMap<>();
+ /** The events that are queryable with an {@link android.app.Activity} class name. */
+ static final int CATEGORY_CLASS_BASED = 4;
- /** Gets the package level {@link EventHistory}. */
- @NonNull
- EventHistory getPackageEventHistory() {
- return mPackageEventHistory;
- }
+ @IntDef(prefix = { "CATEGORY_" }, value = {
+ CATEGORY_SHORTCUT_BASED,
+ CATEGORY_LOCUS_ID_BASED,
+ CATEGORY_CALL,
+ CATEGORY_SMS,
+ CATEGORY_CLASS_BASED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface EventCategory {}
- /** Gets the {@link EventHistory} for the specified {@code shortcutId} if exists. */
- @Nullable
- EventHistory getShortcutEventHistory(String shortcutId) {
- return mShortcutEventHistoryMap.get(shortcutId);
- }
+ private final List<Map<String, EventHistoryImpl>> mEventHistoryMaps = new ArrayList<>();
- /** Gets the {@link EventHistory} for the specified {@code locusId} if exists. */
- @Nullable
- EventHistory getLocusEventHistory(LocusId locusId) {
- return mLocusEventHistoryMap.get(locusId);
- }
-
- /** Gets the phone call {@link EventHistory} for the specified {@code phoneNumber} if exists. */
- @Nullable
- EventHistory getCallEventHistory(String phoneNumber) {
- return mCallEventHistoryMap.get(phoneNumber);
- }
-
- /** Gets the SMS {@link EventHistory} for the specified {@code phoneNumber} if exists. */
- @Nullable
- EventHistory getSmsEventHistory(String phoneNumber) {
- return mSmsEventHistoryMap.get(phoneNumber);
+ EventStore() {
+ mEventHistoryMaps.add(CATEGORY_SHORTCUT_BASED, new ArrayMap<>());
+ mEventHistoryMaps.add(CATEGORY_LOCUS_ID_BASED, new ArrayMap<>());
+ mEventHistoryMaps.add(CATEGORY_CALL, new ArrayMap<>());
+ mEventHistoryMaps.add(CATEGORY_SMS, new ArrayMap<>());
+ mEventHistoryMaps.add(CATEGORY_CLASS_BASED, new ArrayMap<>());
}
/**
- * Gets the {@link EventHistoryImpl} for the specified {@code shortcutId} or creates a new
- * instance and put it into the store if not exists. The caller needs to verify if a
- * conversation with this shortcut ID exists before calling this method.
+ * Gets the {@link EventHistory} for the specified key if exists.
+ *
+ * @param key Category-specific key, it can be shortcut ID, locus ID, phone number, or class
+ * name.
*/
- @NonNull
- EventHistoryImpl getOrCreateShortcutEventHistory(String shortcutId) {
- return mShortcutEventHistoryMap.computeIfAbsent(shortcutId, key -> new EventHistoryImpl());
+ @Nullable
+ EventHistory getEventHistory(@EventCategory int category, String key) {
+ return mEventHistoryMaps.get(category).get(key);
}
/**
- * Gets the {@link EventHistoryImpl} for the specified {@code locusId} or creates a new
- * instance and put it into the store if not exists. The caller needs to ensure a conversation
- * with this locus ID exists before calling this method.
+ * Gets the {@link EventHistoryImpl} for the specified ID or creates a new instance and put it
+ * into the store if not exists. The caller needs to verify if the associated conversation
+ * exists before calling this method.
+ *
+ * @param key Category-specific key, it can be shortcut ID, locus ID, phone number, or class
+ * name.
*/
@NonNull
- EventHistoryImpl getOrCreateLocusEventHistory(LocusId locusId) {
- return mLocusEventHistoryMap.computeIfAbsent(locusId, key -> new EventHistoryImpl());
+ EventHistoryImpl getOrCreateEventHistory(@EventCategory int category, String key) {
+ return mEventHistoryMaps.get(category).computeIfAbsent(key, k -> new EventHistoryImpl());
}
/**
- * Gets the {@link EventHistoryImpl} for the specified {@code phoneNumber} for call events
- * or creates a new instance and put it into the store if not exists. The caller needs to ensure
- * a conversation with this phone number exists and this package is the default dialer
- * before calling this method.
+ * Deletes the events and index data for the specified key.
+ *
+ * @param key Category-specific key, it can be shortcut ID, locus ID, phone number, or class
+ * name.
*/
- @NonNull
- EventHistoryImpl getOrCreateCallEventHistory(String phoneNumber) {
- return mCallEventHistoryMap.computeIfAbsent(phoneNumber, key -> new EventHistoryImpl());
+ void deleteEventHistory(@EventCategory int category, String key) {
+ EventHistoryImpl eventHistory = mEventHistoryMaps.get(category).remove(key);
+ if (eventHistory != null) {
+ eventHistory.onDestroy();
+ }
+ }
+
+ /** Deletes all the events and index data for the specified category from disk. */
+ void deleteEventHistories(@EventCategory int category) {
+ mEventHistoryMaps.get(category).clear();
+ // TODO: Implement this method to delete the data from disk.
+ }
+
+ /** Deletes the events data that exceeds the retention period. */
+ void pruneOldEvents(long currentTimeMillis) {
+ for (Map<String, EventHistoryImpl> map : mEventHistoryMaps) {
+ for (EventHistoryImpl eventHistory : map.values()) {
+ eventHistory.pruneOldEvents(currentTimeMillis);
+ }
+ }
}
/**
- * Gets the {@link EventHistoryImpl} for the specified {@code phoneNumber} for SMS events
- * or creates a new instance and put it into the store if not exists. The caller needs to ensure
- * a conversation with this phone number exists and this package is the default SMS app
- * before calling this method.
+ * Prunes the event histories whose key (shortcut ID, locus ID or phone number) does not match
+ * any conversations.
+ *
+ * @param keyChecker Check whether there exists a conversation contains this key.
*/
- @NonNull
- EventHistoryImpl getOrCreateSmsEventHistory(String phoneNumber) {
- return mSmsEventHistoryMap.computeIfAbsent(phoneNumber, key -> new EventHistoryImpl());
+ void pruneOrphanEventHistories(@EventCategory int category, Predicate<String> keyChecker) {
+ Set<String> keys = mEventHistoryMaps.get(category).keySet();
+ List<String> keysToDelete = new ArrayList<>();
+ for (String key : keys) {
+ if (!keyChecker.test(key)) {
+ keysToDelete.add(key);
+ }
+ }
+ Map<String, EventHistoryImpl> eventHistoryMap = mEventHistoryMaps.get(category);
+ for (String key : keysToDelete) {
+ EventHistoryImpl eventHistory = eventHistoryMap.remove(key);
+ if (eventHistory != null) {
+ eventHistory.onDestroy();
+ }
+ }
}
}
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 f67699c..c55f972 100644
--- a/services/people/java/com/android/server/people/data/PackageData.java
+++ b/services/people/java/com/android/server/people/data/PackageData.java
@@ -16,6 +16,12 @@
package com.android.server.people.data;
+import static com.android.server.people.data.EventStore.CATEGORY_CALL;
+import static com.android.server.people.data.EventStore.CATEGORY_CLASS_BASED;
+import static com.android.server.people.data.EventStore.CATEGORY_LOCUS_ID_BASED;
+import static com.android.server.people.data.EventStore.CATEGORY_SHORTCUT_BASED;
+import static com.android.server.people.data.EventStore.CATEGORY_SMS;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -89,11 +95,6 @@
mConversationStore.forAllConversations(consumer);
}
- @NonNull
- public EventHistory getPackageLevelEventHistory() {
- return getEventStore().getPackageEventHistory();
- }
-
/**
* Gets the {@link ConversationInfo} for a given shortcut ID. Returns null if such as {@link
* ConversationInfo} does not exist.
@@ -117,14 +118,16 @@
return result;
}
- EventHistory shortcutEventHistory = getEventStore().getShortcutEventHistory(shortcutId);
+ EventHistory shortcutEventHistory = getEventStore().getEventHistory(
+ CATEGORY_SHORTCUT_BASED, shortcutId);
if (shortcutEventHistory != null) {
result.addEventHistory(shortcutEventHistory);
}
LocusId locusId = conversationInfo.getLocusId();
if (locusId != null) {
- EventHistory locusEventHistory = getEventStore().getLocusEventHistory(locusId);
+ EventHistory locusEventHistory = getEventStore().getEventHistory(
+ CATEGORY_LOCUS_ID_BASED, locusId.getId());
if (locusEventHistory != null) {
result.addEventHistory(locusEventHistory);
}
@@ -135,13 +138,15 @@
return result;
}
if (isDefaultDialer()) {
- EventHistory callEventHistory = getEventStore().getCallEventHistory(phoneNumber);
+ EventHistory callEventHistory = getEventStore().getEventHistory(
+ CATEGORY_CALL, phoneNumber);
if (callEventHistory != null) {
result.addEventHistory(callEventHistory);
}
}
if (isDefaultSmsApp()) {
- EventHistory smsEventHistory = getEventStore().getSmsEventHistory(phoneNumber);
+ EventHistory smsEventHistory = getEventStore().getEventHistory(
+ CATEGORY_SMS, phoneNumber);
if (smsEventHistory != null) {
result.addEventHistory(smsEventHistory);
}
@@ -149,6 +154,14 @@
return result;
}
+ /** Gets the {@link EventHistory} for a given Activity class. */
+ @NonNull
+ public EventHistory getClassLevelEventHistory(String className) {
+ EventHistory eventHistory = getEventStore().getEventHistory(
+ CATEGORY_CLASS_BASED, className);
+ return eventHistory != null ? eventHistory : new AggregateEventHistoryImpl();
+ }
+
public boolean isDefaultDialer() {
return mIsDefaultDialerPredicate.test(mPackageName);
}
@@ -167,6 +180,47 @@
return mEventStore;
}
+ /**
+ * Deletes all the data (including conversation, events and index) for the specified
+ * conversation shortcut ID.
+ */
+ void deleteDataForConversation(String shortcutId) {
+ ConversationInfo conversationInfo = mConversationStore.deleteConversation(shortcutId);
+ if (conversationInfo == null) {
+ return;
+ }
+ mEventStore.deleteEventHistory(CATEGORY_SHORTCUT_BASED, shortcutId);
+ if (conversationInfo.getLocusId() != null) {
+ mEventStore.deleteEventHistory(
+ CATEGORY_LOCUS_ID_BASED, conversationInfo.getLocusId().getId());
+ }
+ String phoneNumber = conversationInfo.getContactPhoneNumber();
+ if (!TextUtils.isEmpty(phoneNumber)) {
+ if (isDefaultDialer()) {
+ mEventStore.deleteEventHistory(CATEGORY_CALL, phoneNumber);
+ }
+ if (isDefaultSmsApp()) {
+ mEventStore.deleteEventHistory(CATEGORY_SMS, phoneNumber);
+ }
+ }
+ }
+
+ /** Prunes the events and index data that don't have a associated conversation. */
+ void pruneOrphanEvents() {
+ mEventStore.pruneOrphanEventHistories(CATEGORY_SHORTCUT_BASED,
+ key -> mConversationStore.getConversation(key) != null);
+ mEventStore.pruneOrphanEventHistories(CATEGORY_LOCUS_ID_BASED,
+ key -> mConversationStore.getConversationByLocusId(new LocusId(key)) != null);
+ if (isDefaultDialer()) {
+ mEventStore.pruneOrphanEventHistories(CATEGORY_CALL,
+ key -> mConversationStore.getConversationByPhoneNumber(key) != null);
+ }
+ if (isDefaultSmsApp()) {
+ mEventStore.pruneOrphanEventHistories(CATEGORY_SMS,
+ key -> mConversationStore.getConversationByPhoneNumber(key) != null);
+ }
+ }
+
void onDestroy() {
// TODO: STOPSHIP: Implements this method for the case of package being uninstalled.
}
diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
index 644c155..6dcfaa0 100644
--- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
@@ -59,7 +59,7 @@
*/
boolean querySince(long sinceTime) {
UsageEvents usageEvents = mUsageStatsManagerInternal.queryEventsForUser(
- mUserId, sinceTime, System.currentTimeMillis(), false, false, false);
+ mUserId, sinceTime, System.currentTimeMillis(), UsageEvents.SHOW_ALL_EVENT_DATA);
if (usageEvents == null) {
return false;
}
@@ -129,8 +129,8 @@
if (packageData.getConversationStore().getConversation(shortcutId) == null) {
return;
}
- EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory(
- shortcutId);
+ EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
eventHistory.addEvent(event);
}
@@ -138,8 +138,8 @@
if (packageData.getConversationStore().getConversationByLocusId(locusId) == null) {
return;
}
- EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateLocusEventHistory(
- locusId);
+ EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_LOCUS_ID_BASED, locusId.getId());
eventHistory.addEvent(event);
}
@@ -151,8 +151,8 @@
if (conversationInfo == null) {
return;
}
- EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory(
- conversationInfo.getShortcutId());
+ EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_SHORTCUT_BASED, conversationInfo.getShortcutId());
eventHistory.addEvent(event);
}
}
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 aaa5db8..7ca4b6c 100644
--- a/services/people/java/com/android/server/people/data/UserData.java
+++ b/services/people/java/com/android/server/people/data/UserData.java
@@ -104,6 +104,14 @@
return mPackageDataMap.get(packageName);
}
+ /** Deletes the specified package data. */
+ void deletePackageData(@NonNull String packageName) {
+ PackageData packageData = mPackageDataMap.remove(packageName);
+ if (packageData != null) {
+ packageData.onDestroy();
+ }
+ }
+
void setDefaultDialer(@Nullable String packageName) {
mDefaultDialer = packageName;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 64da6f6..d7a3cfd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -19,6 +19,12 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
+import static com.android.server.job.JobSchedulerService.RARE_INDEX;
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
+import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
@@ -34,13 +40,16 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
import android.app.job.JobInfo;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
import android.os.SystemClock;
import android.provider.MediaStore;
+import android.util.SparseIntArray;
import androidx.test.runner.AndroidJUnit4;
@@ -52,6 +61,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@@ -61,7 +71,12 @@
@RunWith(AndroidJUnit4.class)
public class JobStatusTest {
private static final double DELTA = 0.00001;
+ private static final String TEST_PACKAGE = "job.test.package";
+ private static final ComponentName TEST_JOB_COMPONENT = new ComponentName(TEST_PACKAGE, "test");
+ private static final Uri TEST_MEDIA_URI = Uri.parse("content://media/path/to/media");
+ @Mock
+ private JobSchedulerInternal mJobSchedulerInternal;
private MockitoSession mMockingSession;
@Before
@@ -71,7 +86,7 @@
.strictness(Strictness.LENIENT)
.mockStatic(LocalServices.class)
.startMocking();
- doReturn(mock(JobSchedulerInternal.class))
+ doReturn(mJobSchedulerInternal)
.when(() -> LocalServices.getService(JobSchedulerInternal.class));
doReturn(mock(PackageManagerInternal.class))
.when(() -> LocalServices.getService(PackageManagerInternal.class));
@@ -94,6 +109,82 @@
}
}
+ private static void assertEffectiveBucketForMediaExemption(JobStatus jobStatus,
+ boolean exemptionGranted) {
+ final SparseIntArray effectiveBucket = new SparseIntArray();
+ effectiveBucket.put(ACTIVE_INDEX, ACTIVE_INDEX);
+ effectiveBucket.put(WORKING_INDEX, WORKING_INDEX);
+ effectiveBucket.put(FREQUENT_INDEX, exemptionGranted ? WORKING_INDEX : FREQUENT_INDEX);
+ effectiveBucket.put(RARE_INDEX, exemptionGranted ? WORKING_INDEX : RARE_INDEX);
+ effectiveBucket.put(NEVER_INDEX, NEVER_INDEX);
+ effectiveBucket.put(RESTRICTED_INDEX, RESTRICTED_INDEX);
+ for (int i = 0; i < effectiveBucket.size(); i++) {
+ jobStatus.setStandbyBucket(effectiveBucket.keyAt(i));
+ assertEquals(effectiveBucket.valueAt(i), jobStatus.getEffectiveStandbyBucket());
+ }
+ }
+
+ @Test
+ public void testMediaBackupExemption_lateConstraint() {
+ final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+ .setOverrideDeadline(12)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .build();
+ when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+ assertEffectiveBucketForMediaExemption(createJobStatus(triggerContentJob), false);
+ }
+
+ @Test
+ public void testMediaBackupExemption_noConnectivityConstraint() {
+ final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+ .build();
+ when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+ assertEffectiveBucketForMediaExemption(createJobStatus(triggerContentJob), false);
+ }
+
+ @Test
+ public void testMediaBackupExemption_noContentTriggerConstraint() {
+ final JobInfo networkJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .build();
+ when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+ assertEffectiveBucketForMediaExemption(createJobStatus(networkJob), false);
+ }
+
+ @Test
+ public void testMediaBackupExemption_wrongSourcePackage() {
+ final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .build();
+ when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn("not.test.package");
+ assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), false);
+ }
+
+ @Test
+ public void testMediaBackupExemption_nonMediaUri() {
+ final Uri nonMediaUri = Uri.parse("content://not-media/any/path");
+ final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(nonMediaUri, 0))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .build();
+ when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+ assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), false);
+ }
+
+ @Test
+ public void testMediaBackupExemptionGranted() {
+ final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .build();
+ when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+ assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), true);
+ }
+
@Test
public void testFraction() throws Exception {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
diff --git a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk
new file mode 100644
index 0000000..9161d86
--- /dev/null
+++ b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index f2f8ad1..7b2b30b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2087,7 +2087,7 @@
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionAccounts(new ArrayList<>())
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
dpm.setFactoryResetProtectionPolicy(admin1, policy);
@@ -2105,7 +2105,7 @@
setupProfileOwner();
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
assertExpectException(SecurityException.class, null,
@@ -2157,7 +2157,7 @@
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionAccounts(new ArrayList<>())
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
dpm.setFactoryResetProtectionPolicy(admin1, policy);
@@ -2177,8 +2177,8 @@
private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy,
FactoryResetProtectionPolicy actualPolicy) {
- assertThat(actualPolicy.isFactoryResetProtectionDisabled()).isEqualTo(
- expectedPolicy.isFactoryResetProtectionDisabled());
+ assertThat(actualPolicy.isFactoryResetProtectionEnabled()).isEqualTo(
+ expectedPolicy.isFactoryResetProtectionEnabled());
assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(),
actualPolicy.getFactoryResetProtectionAccounts());
}
@@ -2188,6 +2188,42 @@
assertThat(actualAccounts).containsExactlyElementsIn(expectedAccounts);
}
+ public void testSetKeyguardDisabledFeaturesWithDO() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+
+ dpm.setKeyguardDisabledFeatures(admin1, DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA);
+
+ assertThat(dpm.getKeyguardDisabledFeatures(admin1)).isEqualTo(
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA);
+ }
+
+ public void testSetKeyguardDisabledFeaturesWithPO() throws Exception {
+ setupProfileOwner();
+
+ dpm.setKeyguardDisabledFeatures(admin1, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
+
+ assertThat(dpm.getKeyguardDisabledFeatures(admin1)).isEqualTo(
+ DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
+ }
+
+ public void testSetKeyguardDisabledFeaturesWithPOOfOrganizationOwnedDevice()
+ throws Exception {
+ final int MANAGED_PROFILE_USER_ID = DpmMockContext.CALLER_USER_HANDLE;
+ final int MANAGED_PROFILE_ADMIN_UID =
+ UserHandle.getUid(MANAGED_PROFILE_USER_ID, DpmMockContext.SYSTEM_UID);
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+
+ addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+ configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+ parentDpm.setKeyguardDisabledFeatures(admin1,
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA);
+
+ assertThat(parentDpm.getKeyguardDisabledFeatures(admin1)).isEqualTo(
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA);
+ }
+
public void testSetApplicationHiddenWithDO() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
index bc853c6..e8818a3 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
@@ -62,7 +62,7 @@
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionAccounts(accounts)
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
testParcelAndUnparcel(policy);
@@ -77,7 +77,7 @@
FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionAccounts(accounts)
- .setFactoryResetProtectionDisabled(true)
+ .setFactoryResetProtectionEnabled(false)
.build();
testParcelAndUnparcel(policy);
@@ -133,8 +133,8 @@
private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy,
FactoryResetProtectionPolicy actualPolicy) {
- assertEquals(expectedPolicy.isFactoryResetProtectionDisabled(),
- actualPolicy.isFactoryResetProtectionDisabled());
+ assertEquals(expectedPolicy.isFactoryResetProtectionEnabled(),
+ actualPolicy.isFactoryResetProtectionEnabled());
assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(),
actualPolicy.getFactoryResetProtectionAccounts());
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceStaticTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceStaticTest.java
new file mode 100644
index 0000000..607cd81
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceStaticTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Locale;
+
+/**
+ * Tests for static methods of {@link HdmiControlService} class.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class HdmiControlServiceStaticTest {
+
+ @Test
+ public void localToMenuLanguage_english() {
+ assertThat(HdmiControlService.localeToMenuLanguage(Locale.ENGLISH)).isEqualTo("eng");
+ }
+
+ @Test
+ public void localToMenuLanguage_german() {
+ assertThat(HdmiControlService.localeToMenuLanguage(Locale.GERMAN)).isEqualTo("ger");
+ }
+
+ @Test
+ public void localToMenuLanguage_taiwan() {
+ assertThat(HdmiControlService.localeToMenuLanguage(Locale.TAIWAN)).isEqualTo("chi");
+ }
+
+ @Test
+ public void localToMenuLanguage_macau() {
+ assertThat(HdmiControlService.localeToMenuLanguage(new Locale("zh", "MO"))).isEqualTo(
+ "chi");
+ }
+
+ @Test
+ public void localToMenuLanguage_hongkong() {
+ assertThat(HdmiControlService.localeToMenuLanguage(new Locale("zh", "HK"))).isEqualTo(
+ "chi");
+ }
+
+ @Test
+ public void localToMenuLanguage_chinese() {
+ assertThat(HdmiControlService.localeToMenuLanguage(Locale.CHINESE)).isEqualTo("zho");
+ }
+
+}
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 f78fbda..0d4c6e8 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -40,6 +40,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -66,6 +68,7 @@
import com.android.server.integrity.model.IntegrityCheckResult;
import com.android.server.testutils.TestUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -77,6 +80,8 @@
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -87,6 +92,9 @@
private static final String TEST_APP_PATH =
"/data/local/tmp/AppIntegrityManagerServiceTestApp.apk";
+ private static final String TEST_APP_TWO_CERT_PATH =
+ "AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk";
+
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
private static final String VERSION = "version";
private static final String TEST_FRAMEWORK_PACKAGE = "com.android.frameworks.servicestests";
@@ -105,6 +113,11 @@
private static final String INSTALLER_SHA256 =
"30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227";
+ private static final String DUMMY_APP_TWO_CERTS_CERT_1 =
+ "C0369C2A1096632429DFA8433068AECEAD00BAC337CA92A175036D39CC9AFE94";
+ private static final String DUMMY_APP_TWO_CERTS_CERT_2 =
+ "94366E0A80F3A3F0D8171A15760B88E228CD6E1101F0414C98878724FBE70147";
+
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";
@@ -128,6 +141,7 @@
private PackageManager mSpyPackageManager;
private File mTestApk;
+ private File mTestApkTwoCerts;
private final Context mRealContext = InstrumentationRegistry.getTargetContext();
// under test
@@ -136,6 +150,10 @@
@Before
public void setup() throws Exception {
mTestApk = new File(TEST_APP_PATH);
+ mTestApkTwoCerts = File.createTempFile("AppIntegrity", ".apk");
+ try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_TWO_CERT_PATH)) {
+ Files.copy(inputStream, mTestApkTwoCerts.toPath(), REPLACE_EXISTING);
+ }
mService =
new AppIntegrityManagerServiceImpl(
@@ -154,6 +172,11 @@
when(mIntegrityFileManager.initialized()).thenReturn(true);
}
+ @After
+ public void tearDown() throws Exception {
+ mTestApkTwoCerts.delete();
+ }
+
@Test
public void updateRuleSet_notAuthorized() throws Exception {
makeUsSystemApp();
@@ -293,6 +316,29 @@
}
@Test
+ public void handleBroadcast_correctArgs_multipleCerts() throws Exception {
+ whitelistUsAsRuleProvider();
+ makeUsSystemApp();
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mMockContext)
+ .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
+ Intent intent = makeVerificationIntent();
+ intent.setDataAndType(Uri.fromFile(mTestApkTwoCerts), PACKAGE_MIME_TYPE);
+ when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
+
+ broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
+ runJobInHandler();
+
+ ArgumentCaptor<AppInstallMetadata> metadataCaptor =
+ ArgumentCaptor.forClass(AppInstallMetadata.class);
+ verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
+ AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
+ assertThat(appInstallMetadata.getAppCertificates()).containsExactly(
+ DUMMY_APP_TWO_CERTS_CERT_1, DUMMY_APP_TWO_CERTS_CERT_2);
+ }
+
+ @Test
public void handleBroadcast_allow() throws Exception {
whitelistUsAsRuleProvider();
makeUsSystemApp();
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 6769faa..0bb984e 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
@@ -24,12 +24,14 @@
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.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -41,6 +43,7 @@
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
+import android.app.job.JobScheduler;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
@@ -49,12 +52,15 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
+import android.content.pm.parsing.AndroidPackage;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.CancellationSignal;
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
@@ -66,6 +72,7 @@
import android.util.Range;
import com.android.internal.app.ChooserActivity;
+import com.android.internal.content.PackageMonitor;
import com.android.server.LocalServices;
import org.junit.After;
@@ -84,6 +91,7 @@
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
@RunWith(JUnit4.class)
public final class DataManagerTest {
@@ -101,12 +109,14 @@
@Mock private Context mContext;
@Mock private ShortcutServiceInternal mShortcutServiceInternal;
@Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
+ @Mock private PackageManagerInternal mPackageManagerInternal;
@Mock private ShortcutManager mShortcutManager;
@Mock private UserManager mUserManager;
@Mock private TelephonyManager mTelephonyManager;
@Mock private TelecomManager mTelecomManager;
@Mock private ContentResolver mContentResolver;
@Mock private ScheduledExecutorService mExecutorService;
+ @Mock private JobScheduler mJobScheduler;
@Mock private ScheduledFuture mScheduledFuture;
@Mock private StatusBarNotification mStatusBarNotification;
@Mock private Notification mNotification;
@@ -114,6 +124,7 @@
private NotificationChannel mNotificationChannel;
private DataManager mDataManager;
private int mCallingUserId;
+ private CancellationSignal mCancellationSignal;
private TestInjector mInjector;
@Before
@@ -124,6 +135,15 @@
addLocalServiceMock(UsageStatsManagerInternal.class, mUsageStatsManagerInternal);
+ addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternal);
+ AndroidPackage androidPackage = mock(AndroidPackage.class);
+ when(androidPackage.getPackageName()).thenReturn(TEST_PKG_NAME);
+ doAnswer(ans -> {
+ Consumer<AndroidPackage> callback = (Consumer<AndroidPackage>) ans.getArguments()[0];
+ callback.accept(androidPackage);
+ return null;
+ }).when(mPackageManagerInternal).forEachInstalledPackage(any(Consumer.class), anyInt());
+
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
Context originalContext = getInstrumentation().getTargetContext();
@@ -145,6 +165,10 @@
when(mTelecomManager.getDefaultDialerPackage(any(UserHandle.class)))
.thenReturn(TEST_PKG_NAME);
+ when(mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE)).thenReturn(mJobScheduler);
+ when(mContext.getSystemServiceName(JobScheduler.class)).thenReturn(
+ Context.JOB_SCHEDULER_SERVICE);
+
when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(
TimeUnit.class))).thenReturn(mScheduledFuture);
@@ -168,6 +192,8 @@
mCallingUserId = USER_ID_PRIMARY;
+ mCancellationSignal = new CancellationSignal();
+
mInjector = new TestInjector();
mDataManager = new DataManager(mContext, mInjector);
mDataManager.initialize();
@@ -177,6 +203,7 @@
public void tearDown() {
LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
}
@Test
@@ -454,6 +481,101 @@
assertEquals(2, activeTimeSlots.size());
}
+ @Test
+ public void testDeleteUninstalledPackageDataOnPackageRemoved() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+ assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
+
+ PackageMonitor packageMonitor = mDataManager.getPackageMonitorForTesting(USER_ID_PRIMARY);
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_PRIMARY);
+ intent.setData(Uri.parse("package:" + TEST_PKG_NAME));
+ packageMonitor.onReceive(mContext, intent);
+ assertNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
+ }
+
+ @Test
+ public void testPruneUninstalledPackageData() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+ assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
+
+ doAnswer(ans -> null).when(mPackageManagerInternal)
+ .forEachInstalledPackage(any(Consumer.class), anyInt());
+ mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+ assertNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
+ }
+
+ @Test
+ public void testPruneCallEventsFromNonDialer() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+ long currentTimestamp = System.currentTimeMillis();
+ mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
+ new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_CALL_OUTGOING));
+
+ List<Range<Long>> activeTimeSlots = new ArrayList<>();
+ mDataManager.forAllPackages(packageData ->
+ activeTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.CALL_EVENT_TYPES)
+ .getActiveTimeSlots()));
+ assertEquals(1, activeTimeSlots.size());
+
+ mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultDialer(null);
+ mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+ activeTimeSlots.clear();
+ mDataManager.forAllPackages(packageData ->
+ activeTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.CALL_EVENT_TYPES)
+ .getActiveTimeSlots()));
+ assertTrue(activeTimeSlots.isEmpty());
+ }
+
+ @Test
+ public void testPruneSmsEventsFromNonDefaultSmsApp() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME);
+
+ long currentTimestamp = System.currentTimeMillis();
+ mInjector.mMmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
+ new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_SMS_OUTGOING));
+
+ List<Range<Long>> activeTimeSlots = new ArrayList<>();
+ mDataManager.forAllPackages(packageData ->
+ activeTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.SMS_EVENT_TYPES)
+ .getActiveTimeSlots()));
+ assertEquals(1, activeTimeSlots.size());
+
+ mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(null);
+ mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+ activeTimeSlots.clear();
+ mDataManager.forAllPackages(packageData ->
+ activeTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.SMS_EVENT_TYPES)
+ .getActiveTimeSlots()));
+ assertTrue(activeTimeSlots.isEmpty());
+ }
+
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/people/data/PackageDataTest.java b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
index 1ddc21e..e52cdf5 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
@@ -81,8 +81,10 @@
@Test
public void testGetEventHistory() {
EventStore eventStore = mPackageData.getEventStore();
- eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
- eventStore.getOrCreateLocusEventHistory(LOCUS_ID).addEvent(mE2);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+ .addEvent(mE1);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_LOCUS_ID_BASED, LOCUS_ID.getId())
+ .addEvent(mE2);
EventHistory eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
List<Event> events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
@@ -96,9 +98,10 @@
mIsDefaultDialer = true;
mIsDefaultSmsApp = true;
EventStore eventStore = mPackageData.getEventStore();
- eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
- eventStore.getOrCreateCallEventHistory(PHONE_NUMBER).addEvent(mE3);
- eventStore.getOrCreateSmsEventHistory(PHONE_NUMBER).addEvent(mE4);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+ .addEvent(mE1);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_CALL, PHONE_NUMBER).addEvent(mE3);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SMS, PHONE_NUMBER).addEvent(mE4);
assertTrue(mPackageData.isDefaultDialer());
assertTrue(mPackageData.isDefaultSmsApp());
@@ -113,9 +116,10 @@
@Test
public void testGetEventHistoryNotDefaultDialerOrSmsApp() {
EventStore eventStore = mPackageData.getEventStore();
- eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
- eventStore.getOrCreateCallEventHistory(PHONE_NUMBER).addEvent(mE3);
- eventStore.getOrCreateSmsEventHistory(PHONE_NUMBER).addEvent(mE4);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+ .addEvent(mE1);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_CALL, PHONE_NUMBER).addEvent(mE3);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SMS, PHONE_NUMBER).addEvent(mE4);
assertFalse(mPackageData.isDefaultDialer());
assertFalse(mPackageData.isDefaultSmsApp());
@@ -125,6 +129,61 @@
assertEventEquals(mE1, events.get(0));
}
+ @Test
+ public void testDeleteConversationData() {
+ mIsDefaultDialer = true;
+ mIsDefaultSmsApp = true;
+ EventStore eventStore = mPackageData.getEventStore();
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+ .addEvent(mE1);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_LOCUS_ID_BASED, LOCUS_ID.getId())
+ .addEvent(mE2);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_CALL, PHONE_NUMBER).addEvent(mE3);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SMS, PHONE_NUMBER).addEvent(mE4);
+
+ EventHistory eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+ List<Event> events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(4, events.size());
+
+ mPackageData.deleteDataForConversation(SHORTCUT_ID);
+
+ eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+ events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertTrue(events.isEmpty());
+ }
+
+ @Test
+ public void testPruneOrphanEvents() {
+ mIsDefaultDialer = true;
+ mIsDefaultSmsApp = true;
+ EventStore eventStore = mPackageData.getEventStore();
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+ .addEvent(mE1);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_LOCUS_ID_BASED, LOCUS_ID.getId())
+ .addEvent(mE2);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_CALL, PHONE_NUMBER).addEvent(mE3);
+ eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SMS, PHONE_NUMBER).addEvent(mE4);
+
+ EventHistory eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+ List<Event> events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(4, events.size());
+
+ ConversationInfo conversationInfo = new ConversationInfo.Builder()
+ .setShortcutId(SHORTCUT_ID)
+ .setLocusId(null)
+ .setContactUri(null)
+ .setContactPhoneNumber(null)
+ .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
+ .build();
+ mPackageData.getConversationStore().addOrUpdate(conversationInfo);
+ mPackageData.pruneOrphanEvents();
+ eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+ events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(1, events.size());
+ // Only the shortcut based event is kept. All the other events are deleted.
+ assertEventEquals(mE1, events.get(0));
+ }
+
private void assertEventEquals(Event expected, Event actual) {
assertEquals(expected.getTimestamp(), actual.getTimestamp());
assertEquals(expected.getType(), actual.getType());
diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
index 01d9dc0..dc4876b 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
@@ -19,9 +19,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
@@ -189,7 +189,7 @@
private void addUsageEvents(UsageEvents.Event... events) {
UsageEvents usageEvents = new UsageEvents(Arrays.asList(events), new String[]{});
when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(),
- anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(usageEvents);
+ eq(UsageEvents.SHOW_ALL_EVENT_DATA))).thenReturn(usageEvents);
}
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
@@ -288,14 +288,13 @@
@Override
@NonNull
- EventHistoryImpl getOrCreateShortcutEventHistory(String shortcutId) {
- return mShortcutEventHistory;
- }
-
- @Override
- @NonNull
- EventHistoryImpl getOrCreateLocusEventHistory(LocusId locusId) {
- return mLocusEventHistory;
+ EventHistoryImpl getOrCreateEventHistory(@EventCategory int category, String key) {
+ if (category == EventStore.CATEGORY_SHORTCUT_BASED) {
+ return mShortcutEventHistory;
+ } else if (category == EventStore.CATEGORY_LOCUS_ID_BASED) {
+ return mLocusEventHistory;
+ }
+ throw new UnsupportedOperationException();
}
}
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 e768205..9e57763 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -145,6 +145,7 @@
static class MyInjector extends AppStandbyController.Injector {
long mElapsedRealtime;
boolean mIsAppIdleEnabled = true;
+ boolean mIsCharging;
List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>();
boolean mDisplayOn;
DisplayManager.DisplayListener mDisplayListener;
@@ -179,6 +180,11 @@
}
@Override
+ boolean isCharging() {
+ return mIsCharging;
+ }
+
+ @Override
boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
return mPowerSaveWhitelistExceptIdle.contains(packageName);
}
@@ -281,6 +287,13 @@
} catch (PackageManager.NameNotFoundException nnfe) {}
}
+ private void setChargingState(AppStandbyController controller, boolean charging) {
+ mInjector.mIsCharging = charging;
+ if (controller != null) {
+ controller.setChargingState(charging);
+ }
+ }
+
private void setAppIdleEnabled(AppStandbyController controller, boolean enabled) {
mInjector.mIsAppIdleEnabled = enabled;
if (controller != null) {
@@ -297,6 +310,7 @@
controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
mInjector.setDisplayOn(false);
mInjector.setDisplayOn(true);
+ setChargingState(controller, false);
controller.checkIdleStates(USER_ID);
assertNotEquals(STANDBY_BUCKET_EXEMPTED,
controller.getAppStandbyBucket(PACKAGE_1, USER_ID,
@@ -324,6 +338,46 @@
mInjector.mElapsedRealtime, false));
}
+ @Test
+ public void testIsAppIdle_Charging() throws Exception {
+ setChargingState(mController, false);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+ assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+
+ setChargingState(mController, true);
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+ assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+
+ setChargingState(mController, false);
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+ assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+ }
+
+ @Test
+ public void testIsAppIdle_Enabled() throws Exception {
+ setChargingState(mController, false);
+ setAppIdleEnabled(mController, true);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+ assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+
+ setAppIdleEnabled(mController, false);
+ assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+ assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+
+ setAppIdleEnabled(mController, true);
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+ assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+ }
+
private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) {
mInjector.mElapsedRealtime = elapsedTime;
controller.checkIdleStates(USER_ID);
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 a0f7f5b..bc33f08 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -51,8 +51,6 @@
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
-import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
@@ -1144,14 +1142,15 @@
}
@Test
- public void testEnqueueNotificationWithTag_WritesExpectedLog() throws Exception {
+ public void testEnqueueNotificationWithTag_WritesExpectedLogs() throws Exception {
final String tag = "testEnqueueNotificationWithTag_WritesExpectedLog";
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
generateNotificationRecord(null).getNotification(), 0);
waitForIdle();
assertEquals(1, mNotificationRecordLogger.getCalls().size());
+
NotificationRecordLoggerFake.CallRecord call = mNotificationRecordLogger.get(0);
- assertTrue(call.shouldLog);
+ assertTrue(call.shouldLogReported);
assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
call.event);
assertNotNull(call.r);
@@ -1161,7 +1160,6 @@
assertEquals(PKG, call.r.getSbn().getPackageName());
assertEquals(0, call.r.getSbn().getId());
assertEquals(tag, call.r.getSbn().getTag());
- assertNotNull(call.r.getSbn().getInstanceId());
assertEquals(0, call.getInstanceId()); // Fake instance IDs are assigned in order
}
@@ -1180,13 +1178,13 @@
waitForIdle();
assertEquals(2, mNotificationRecordLogger.getCalls().size());
- assertTrue(mNotificationRecordLogger.get(0).shouldLog);
+ assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
mNotificationRecordLogger.get(0).event);
assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
- assertTrue(mNotificationRecordLogger.get(1).shouldLog);
+ assertTrue(mNotificationRecordLogger.get(1).shouldLogReported);
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED,
mNotificationRecordLogger.get(1).event);
@@ -1195,16 +1193,37 @@
}
@Test
- public void testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdates() throws Exception {
- final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdates";
+ public void testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdate() throws Exception {
+ final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdate";
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
generateNotificationRecord(null).getNotification(), 0);
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
generateNotificationRecord(null).getNotification(), 0);
waitForIdle();
assertEquals(2, mNotificationRecordLogger.getCalls().size());
- assertTrue(mNotificationRecordLogger.get(0).shouldLog);
- assertFalse(mNotificationRecordLogger.get(1).shouldLog);
+ assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
+ assertEquals(
+ NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
+ mNotificationRecordLogger.get(0).event);
+ assertFalse(mNotificationRecordLogger.get(1).shouldLogReported);
+ assertNull(mNotificationRecordLogger.get(1).event);
+ }
+
+ @Test
+ public void testEnqueueNotificationWithTag_DoesNotLogOnTitleUpdate() throws Exception {
+ final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnTitleUpdate";
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
+ generateNotificationRecord(null).getNotification(),
+ 0);
+ final Notification notif = generateNotificationRecord(null).getNotification();
+ notif.extras.putString(Notification.EXTRA_TITLE, "Changed title");
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, notif, 0);
+ waitForIdle();
+ assertEquals(2, mNotificationRecordLogger.getCalls().size());
+ assertEquals(
+ NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
+ mNotificationRecordLogger.get(0).event);
+ assertNull(mNotificationRecordLogger.get(1).event);
}
@Test
@@ -1224,20 +1243,18 @@
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
mNotificationRecordLogger.get(0).event);
- assertTrue(mNotificationRecordLogger.get(0).shouldLog);
+ assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
- assertEquals(REASON_APP_CANCEL, mNotificationRecordLogger.get(1).reason);
assertEquals(
NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_APP_CANCEL,
mNotificationRecordLogger.get(1).event);
- assertTrue(mNotificationRecordLogger.get(1).shouldLog);
assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId());
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
mNotificationRecordLogger.get(2).event);
- assertTrue(mNotificationRecordLogger.get(2).shouldLog);
+ assertTrue(mNotificationRecordLogger.get(2).shouldLogReported);
// New instance ID because notification was canceled before re-post
assertEquals(1, mNotificationRecordLogger.get(2).getInstanceId());
}
@@ -2605,6 +2622,33 @@
}
@Test
+ public void testSystemNotificationListenerCanUnsnooze() throws Exception {
+ final NotificationRecord nr = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG,
+ "testSystemNotificationListenerCanUnsnooze",
+ nr.getSbn().getId(), nr.getSbn().getNotification(),
+ nr.getSbn().getUserId());
+ waitForIdle();
+ NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
+ mService.new SnoozeNotificationRunnable(
+ nr.getKey(), 100, null);
+ snoozeNotificationRunnable.run();
+
+ ManagedServices.ManagedServiceInfo listener = mListeners.new ManagedServiceInfo(
+ null, new ComponentName(PKG, "test_class"), mUid, true, null, 0);
+ listener.isSystem = true;
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(listener);
+
+ mBinderService.unsnoozeNotificationFromSystemListener(null, nr.getKey());
+ waitForIdle();
+ StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifs.length);
+ assertNotNull(notifs[0].getKey());//mService.getNotificationRecord(nr.getSbn().getKey()));
+ }
+
+ @Test
public void testSetListenerAccessForUser() throws Exception {
UserHandle user = UserHandle.of(10);
ComponentName c = ComponentName.unflattenFromString("package/Component");
@@ -3396,11 +3440,9 @@
// so we only get the cancel notification.
assertEquals(1, mNotificationRecordLogger.getCalls().size());
- assertEquals(REASON_CANCEL, mNotificationRecordLogger.get(0).reason);
assertEquals(
NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_AOD,
mNotificationRecordLogger.get(0).event);
- assertTrue(mNotificationRecordLogger.get(0).shouldLog);
assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
}
@@ -4326,6 +4368,34 @@
}
@Test
+ public void testOnNotificationVisibilityChanged_triggersVisibilityLog() {
+ final NotificationRecord r = generateNotificationRecord(
+ mTestNotificationChannel, 1, null, true);
+ r.setTextChanged(true);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationVisibilityChanged(new NotificationVisibility[]
+ {NotificationVisibility.obtain(r.getKey(), 1, 1, true)},
+ new NotificationVisibility[]{});
+
+ assertEquals(1, mNotificationRecordLogger.getCalls().size());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_OPEN,
+ mNotificationRecordLogger.get(0).event);
+ assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
+
+ mService.mNotificationDelegate.onNotificationVisibilityChanged(
+ new NotificationVisibility[]{},
+ new NotificationVisibility[]
+ {NotificationVisibility.obtain(r.getKey(), 1, 1, true)}
+ );
+
+ assertEquals(2, mNotificationRecordLogger.getCalls().size());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLOSE,
+ mNotificationRecordLogger.get(1).event);
+ assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId());
+ }
+
+ @Test
public void testSetNotificationsShownFromListener_triggersInterruptionUsageStat()
throws RemoteException {
final NotificationRecord r = generateNotificationRecord(
@@ -5373,21 +5443,7 @@
}
@Test
- public void testCancelAllNotifications_cancelsBubble() throws Exception {
- final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
- nr.getSbn().getNotification().flags |= FLAG_BUBBLE;
- mService.addNotification(nr);
-
- mBinderService.cancelAllNotifications(PKG, nr.getSbn().getUserId());
- waitForIdle();
-
- StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
- assertEquals(0, notifs.length);
- assertEquals(0, mService.getNotificationRecordCount());
- }
-
- @Test
- public void testAppCancelNotifications_cancelsBubbles() throws Exception {
+ public void testCancelNotificationsFromApp_cancelsBubbles() throws Exception {
final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
nrBubble.getSbn().getNotification().flags |= FLAG_BUBBLE;
@@ -5413,6 +5469,20 @@
}
@Test
+ public void testCancelAllNotificationsFromApp_cancelsBubble() throws Exception {
+ final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
+ nr.getSbn().getNotification().flags |= FLAG_BUBBLE;
+ mService.addNotification(nr);
+
+ mBinderService.cancelAllNotifications(PKG, nr.getSbn().getUserId());
+ waitForIdle();
+
+ StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
+ assertEquals(0, notifs.length);
+ assertEquals(0, mService.getNotificationRecordCount());
+ }
+
+ @Test
public void testCancelAllNotificationsFromListener_ignoresBubbles() throws Exception {
final NotificationRecord nrNormal = generateNotificationRecord(mTestNotificationChannel);
final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
@@ -5448,6 +5518,25 @@
}
@Test
+ public void testCancelAllNotificationsFromStatusBar_ignoresBubble() throws Exception {
+ // GIVEN a notification bubble
+ final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
+ nr.getSbn().getNotification().flags |= FLAG_BUBBLE;
+ mService.addNotification(nr);
+
+ // WHEN the status bar clears all notifications
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ nr.getSbn().getUserId());
+ waitForIdle();
+
+ // THEN the bubble notification does not get removed
+ StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifs.length);
+ assertEquals(1, mService.getNotificationRecordCount());
+ }
+
+
+ @Test
public void testGetAllowedAssistantAdjustments() throws Exception {
List<String> capabilities = mBinderService.getAllowedAssistantAdjustments(null);
assertNotNull(capabilities);
@@ -6105,6 +6194,26 @@
}
@Test
+ public void testNotificationBubbles_bubbleStays_whenClicked()
+ throws Exception {
+ // GIVEN a notification that has the auto cancels flag (cancel on click) and is a bubble
+ setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+ final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
+ nr.getSbn().getNotification().flags |= FLAG_BUBBLE | FLAG_AUTO_CANCEL;
+ mService.addNotification(nr);
+
+ // WHEN we click the notification
+ final NotificationVisibility nv = NotificationVisibility.obtain(nr.getKey(), 1, 2, true);
+ mService.mNotificationDelegate.onNotificationClick(mUid, Binder.getCallingPid(),
+ nr.getKey(), nv);
+ waitForIdle();
+
+ // THEN the bubble should still exist
+ StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifsAfter.length);
+ }
+
+ @Test
public void testLoadDefaultApprovedServices_emptyResources() {
TestableResources tr = mContext.getOrCreateTestableResources();
tr.addOverride(com.android.internal.R.string.config_defaultListenerAccessPackages, "");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
index b120dbe..2a17bae 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
@@ -26,24 +26,26 @@
*/
class NotificationRecordLoggerFake implements NotificationRecordLogger {
static class CallRecord extends NotificationRecordPair {
- static final int INVALID = -1;
- public int position = INVALID, buzzBeepBlink = INVALID, reason = INVALID;
- public boolean shouldLog;
public UiEventLogger.UiEventEnum event;
+
+ // The following fields are only relevant to maybeLogNotificationPosted() calls.
+ static final int INVALID = -1;
+ public int position = INVALID, buzzBeepBlink = INVALID;
+ public boolean shouldLogReported;
+
CallRecord(NotificationRecord r, NotificationRecord old, int position,
int buzzBeepBlink) {
super(r, old);
-
this.position = position;
this.buzzBeepBlink = buzzBeepBlink;
- shouldLog = shouldLog(buzzBeepBlink);
- event = NotificationReportedEvent.fromRecordPair(this);
+ shouldLogReported = shouldLogReported(buzzBeepBlink);
+ event = shouldLogReported ? NotificationReportedEvent.fromRecordPair(this) : null;
}
- CallRecord(NotificationRecord r, int reason, int dismissalSurface) {
+
+ CallRecord(NotificationRecord r, UiEventLogger.UiEventEnum event) {
super(r, null);
- this.reason = reason;
- shouldLog = true;
- event = NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface);
+ shouldLogReported = false;
+ this.event = event;
}
}
private List<CallRecord> mCalls = new ArrayList<>();
@@ -57,14 +59,19 @@
}
@Override
- public void logNotificationReported(NotificationRecord r, NotificationRecord old,
+ public void maybeLogNotificationPosted(NotificationRecord r, NotificationRecord old,
int position, int buzzBeepBlink) {
mCalls.add(new CallRecord(r, old, position, buzzBeepBlink));
}
@Override
public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) {
- mCalls.add(new CallRecord(r, reason, dismissalSurface));
+ mCalls.add(new CallRecord(r,
+ NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface)));
}
+ @Override
+ public void logNotificationVisibility(NotificationRecord r, boolean visible) {
+ mCalls.add(new CallRecord(r, NotificationEvent.fromVisibility(visible)));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 3186d53..1dd0b1a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -15,15 +15,18 @@
*/
package com.android.server.notification;
+import static com.android.server.notification.SnoozeHelper.EXTRA_KEY;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -322,8 +325,12 @@
mSnoozeHelper.snooze(r, 1000);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "one", UserHandle.ALL);
mSnoozeHelper.snooze(r2, 1000);
+ reset(mAm);
mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
+ ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
+ verify(mAm).cancel(captor.capture());
+ assertEquals(r.getKey(), captor.getValue().getIntent().getStringExtra(EXTRA_KEY));
}
@Test
@@ -332,8 +339,10 @@
mSnoozeHelper.snooze(r, 1000);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "one", UserHandle.ALL);
mSnoozeHelper.snooze(r2, 1000);
+ reset(mAm);
mSnoozeHelper.repost(r.getKey());
verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
+ verify(mAm).cancel(any(PendingIntent.class));
}
@Test
@@ -370,31 +379,7 @@
}
@Test
- public void testGetSnoozedByUser() throws Exception {
- NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
- NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
- NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
- NotificationRecord r4 = getNotificationRecord("pkg2", 3, "three", UserHandle.CURRENT);
- mSnoozeHelper.snooze(r, 1000);
- mSnoozeHelper.snooze(r2, 1000);
- mSnoozeHelper.snooze(r3, 1000);
- mSnoozeHelper.snooze(r4, 1000);
- IntArray profileIds = new IntArray();
- profileIds.add(UserHandle.USER_SYSTEM);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
- assertEquals(3, mSnoozeHelper.getSnoozed().size());
- profileIds = new IntArray();
- profileIds.add(UserHandle.USER_CURRENT);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
- assertEquals(1, mSnoozeHelper.getSnoozed().size());
- }
-
- @Test
- public void testGetSnoozedByUser_managedProfiles() throws Exception {
- IntArray profileIds = new IntArray();
- profileIds.add(UserHandle.USER_CURRENT);
- profileIds.add(UserHandle.USER_SYSTEM);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
+ public void testGetSnoozedBy() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index 9647178..e8c0362 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -144,4 +144,33 @@
anyInt() /* reason */, anyString() /* packageName */);
verify(taskChangeNotifier, never()).notifyActivityDismissingDockedStack();
}
+
+ /**
+ * Ensures that notify focus task changes.
+ */
+ @Test
+ public void testNotifyTaskFocusChanged() {
+ final ActivityRecord fullScreenActivityA = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
+ final Task taskA = fullScreenActivityA.getTask();
+
+ final TaskChangeNotificationController taskChangeNotifier =
+ mService.getTaskChangeNotificationController();
+ spyOn(taskChangeNotifier);
+
+ mService.setResumedActivityUncheckLocked(fullScreenActivityA, "resumeA");
+ verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskA.mTaskId) /* taskId */,
+ eq(true) /* focused */);
+ reset(taskChangeNotifier);
+
+ final ActivityRecord fullScreenActivityB = new ActivityBuilder(mService).setCreateTask(true)
+ .setStack(mFullscreenStack).build();
+ final Task taskB = fullScreenActivityB.getTask();
+
+ mService.setResumedActivityUncheckLocked(fullScreenActivityB, "resumeB");
+ verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskA.mTaskId) /* taskId */,
+ eq(false) /* focused */);
+ verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskB.mTaskId) /* taskId */,
+ eq(true) /* focused */);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
new file mode 100644
index 0000000..8ac1d24
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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 android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+
+import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
+import static com.android.server.wm.DisplayArea.Type.ANY;
+import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
+
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import static java.util.stream.Collectors.toList;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+
+import org.hamcrest.CustomTypeSafeMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Presubmit
+public class DisplayAreaPolicyBuilderTest {
+
+ @Rule
+ public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule();
+
+ private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null);
+
+ @Test
+ public void testBuilder() {
+ WindowManagerService wms = mSystemServices.getWindowManagerService();
+ DisplayArea.Root root = new SurfacelessDisplayAreaRoot(wms);
+ DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime");
+ DisplayArea<ActivityStack> tasks = new DisplayArea<>(wms, ANY, "Tasks");
+
+ final Feature foo;
+ final Feature bar;
+
+ DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+ .addFeature(foo = new Feature.Builder(mPolicy, "Foo")
+ .upTo(TYPE_STATUS_BAR)
+ .and(TYPE_NAVIGATION_BAR)
+ .build())
+ .addFeature(bar = new Feature.Builder(mPolicy, "Bar")
+ .all()
+ .except(TYPE_STATUS_BAR)
+ .build())
+ .build(wms, mock(DisplayContent.class), root, ime, tasks);
+
+ policy.attachDisplayAreas();
+
+ assertThat(policy.getDisplayAreas(foo), is(not(empty())));
+ assertThat(policy.getDisplayAreas(bar), is(not(empty())));
+
+ assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)),
+ is(decendantOfOneOf(policy.getDisplayAreas(foo))));
+ assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)),
+ is(not(decendantOfOneOf(policy.getDisplayAreas(bar)))));
+
+ assertThat(tasks,
+ is(decendantOfOneOf(policy.getDisplayAreas(foo))));
+ assertThat(tasks,
+ is(decendantOfOneOf(policy.getDisplayAreas(bar))));
+
+ assertThat(ime,
+ is(decendantOfOneOf(policy.getDisplayAreas(foo))));
+ assertThat(ime,
+ is(decendantOfOneOf(policy.getDisplayAreas(bar))));
+
+ List<DisplayArea<?>> actualOrder = collectLeafAreas(root);
+ Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime, tasks);
+ actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList());
+
+ Map<DisplayArea<?>, Integer> expectedByMinLayer = mapValues(zSets,
+ v -> v.stream().min(Integer::compareTo).get());
+ Map<DisplayArea<?>, Integer> expectedByMaxLayer = mapValues(zSets,
+ v -> v.stream().max(Integer::compareTo).get());
+
+ assertThat(expectedByMinLayer, is(equalTo(expectedByMaxLayer)));
+ assertThat(actualOrder, is(equalTo(expectedByMaxLayer)));
+ }
+
+ private <K, V, R> Map<K, R> mapValues(Map<K, V> zSets, Function<V, R> f) {
+ return zSets.entrySet().stream().collect(Collectors.toMap(
+ Map.Entry::getKey,
+ e -> f.apply(e.getValue())));
+ }
+
+ private List<DisplayArea<?>> collectLeafAreas(DisplayArea<?> root) {
+ ArrayList<DisplayArea<?>> leafs = new ArrayList<>();
+ traverseLeafAreas(root, leafs::add);
+ return leafs;
+ }
+
+ private Map<DisplayArea<?>, Set<Integer>> calculateZSets(
+ DisplayAreaPolicyBuilder.Result policy, DisplayArea.Root root,
+ DisplayArea<WindowContainer> ime,
+ DisplayArea<ActivityStack> tasks) {
+ Map<DisplayArea<?>, Set<Integer>> zSets = new HashMap<>();
+ int[] types = {TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_PRESENTATION,
+ TYPE_APPLICATION_OVERLAY};
+ for (int type : types) {
+ WindowToken token = tokenOfType(type);
+ recordLayer(policy.findAreaForToken(token), token.getWindowLayerFromType(), zSets);
+ }
+ recordLayer(tasks, APPLICATION_LAYER, zSets);
+ recordLayer(ime, mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD), zSets);
+ return zSets;
+ }
+
+ private void recordLayer(DisplayArea<?> area, int layer,
+ Map<DisplayArea<?>, Set<Integer>> zSets) {
+ zSets.computeIfAbsent(area, k -> new HashSet<>()).add(layer);
+ }
+
+ private Matcher<WindowContainer> decendantOfOneOf(List<? extends WindowContainer> expected) {
+ return new CustomTypeSafeMatcher<WindowContainer>("descendant of one of " + expected) {
+ @Override
+ protected boolean matchesSafely(WindowContainer actual) {
+ for (WindowContainer expected : expected) {
+ WindowContainer candidate = actual;
+ while (candidate != null && candidate.getParent() != candidate) {
+ if (candidate.getParent() == expected) {
+ return true;
+ }
+ candidate = candidate.getParent();
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void describeMismatchSafely(WindowContainer item,
+ Description description) {
+ description.appendText("was ").appendValue(item);
+ while (item != null && item.getParent() != item) {
+ item = item.getParent();
+ description.appendText(", child of ").appendValue(item);
+ }
+ }
+ };
+ }
+
+ private WindowToken tokenOfType(int type) {
+ WindowToken m = mock(WindowToken.class);
+ when(m.getWindowLayerFromType()).thenReturn(mPolicy.getWindowLayerFromTypeLw(type));
+ return m;
+ }
+
+ private static void traverseLeafAreas(DisplayArea<?> root, Consumer<DisplayArea<?>> consumer) {
+ boolean leaf = true;
+ for (int i = 0; i < root.getChildCount(); i++) {
+ WindowContainer child = root.getChildAt(i);
+ if (child instanceof DisplayArea<?>) {
+ traverseLeafAreas((DisplayArea<?>) child, consumer);
+ leaf = false;
+ }
+ }
+ if (leaf) {
+ consumer.accept(root);
+ }
+ }
+
+ private static class SurfacelessDisplayAreaRoot extends DisplayArea.Root {
+
+ SurfacelessDisplayAreaRoot(WindowManagerService wms) {
+ super(wms);
+ }
+
+ @Override
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ return new MockSurfaceControlBuilder();
+ }
+ }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
index c1a1d5e..3120631 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
@@ -34,13 +34,13 @@
@Test
public void testFromResources_emptyProvider() {
Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider("")),
- Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class));
+ Matchers.instanceOf(DisplayAreaPolicy.DefaultProvider.class));
}
@Test
public void testFromResources_nullProvider() {
Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(null)),
- Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class));
+ Matchers.instanceOf(DisplayAreaPolicy.DefaultProvider.class));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 2ea00ce..3b6816a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1033,6 +1033,54 @@
assertTrue(continued[0]);
}
+ @Test
+ public void testGetOrCreateRootHomeTask_defaultDisplay() {
+ DisplayContent defaultDisplay = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
+
+ // Remove the current home stack if it exists so a new one can be created below.
+ ActivityStack homeTask = defaultDisplay.getRootHomeTask();
+ if (homeTask != null) {
+ defaultDisplay.removeStack(homeTask);
+ }
+ assertNull(defaultDisplay.getRootHomeTask());
+
+ assertNotNull(defaultDisplay.getOrCreateRootHomeTask());
+ }
+
+ @Test
+ public void testGetOrCreateRootHomeTask_supportedSecondaryDisplay() {
+ DisplayContent display = createNewDisplay();
+ doReturn(true).when(display).supportsSystemDecorations();
+ doReturn(false).when(display).isUntrustedVirtualDisplay();
+
+ // Remove the current home stack if it exists so a new one can be created below.
+ ActivityStack homeTask = display.getRootHomeTask();
+ if (homeTask != null) {
+ display.removeStack(homeTask);
+ }
+ assertNull(display.getRootHomeTask());
+
+ assertNotNull(display.getOrCreateRootHomeTask());
+ }
+
+ @Test
+ public void testGetOrCreateRootHomeTask_unsupportedSystemDecorations() {
+ DisplayContent display = createNewDisplay();
+ doReturn(false).when(display).supportsSystemDecorations();
+
+ assertNull(display.getRootHomeTask());
+ assertNull(display.getOrCreateRootHomeTask());
+ }
+
+ @Test
+ public void testGetOrCreateRootHomeTask_untrustedVirtualDisplay() {
+ DisplayContent display = createNewDisplay();
+ doReturn(true).when(display).isUntrustedVirtualDisplay();
+
+ assertNull(display.getRootHomeTask());
+ assertNull(display.getOrCreateRootHomeTask());
+ }
+
private boolean isOptionsPanelAtRight(int displayId) {
return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
}
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 79db927..dd46673 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -860,6 +860,26 @@
secondaryDisplay.mDisplayId, result.getDisplayId());
}
+ @Test
+ public void testSwitchUser_missingHomeRootTask() {
+ doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedStack();
+
+ DisplayContent defaultDisplay = mRootWindowContainer.getDefaultDisplay();
+ ActivityStack homeStack = defaultDisplay.getRootHomeTask();
+ if (homeStack != null) {
+ homeStack.removeImmediately();
+ }
+ assertNull(defaultDisplay.getRootHomeTask());
+
+ int currentUser = mRootWindowContainer.mCurrentUser;
+ int otherUser = currentUser + 1;
+
+ mRootWindowContainer.switchUser(otherUser, null);
+
+ assertNotNull(defaultDisplay.getRootHomeTask());
+ assertEquals(defaultDisplay.getTopStack(), defaultDisplay.getRootHomeTask());
+ }
+
/**
* Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
* info for test cases (the original implementation will resolve from the real package manager).
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index f517881..8ad7505 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -56,6 +56,7 @@
import android.os.PowerSaveState;
import android.os.StrictMode;
import android.os.UserHandle;
+import android.util.Log;
import android.view.InputChannel;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -120,11 +121,22 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
+ Throwable throwable = null;
try {
runWithDexmakerShareClassLoader(SystemServicesTestRule.this::setUp);
base.evaluate();
+ } catch (Throwable t) {
+ throwable = t;
} finally {
- tearDown();
+ try {
+ tearDown();
+ } catch (Throwable t) {
+ if (throwable != null) {
+ Log.e("SystemServicesTestRule", "Suppressed: ", throwable);
+ t.addSuppressed(throwable);
+ }
+ throw t;
+ }
}
}
};
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 420695d..df5b311 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -500,6 +500,19 @@
== PackageManager.PERMISSION_GRANTED);
}
+ /**
+ * Obfuscate both {@link UsageEvents.Event#NOTIFICATION_SEEN} and
+ * {@link UsageEvents.Event#NOTIFICATION_INTERRUPTION} events if the provided calling uid does
+ * not hold the {@link android.Manifest.permission.MANAGE_NOTIFICATIONS} permission.
+ */
+ private boolean shouldObfuscateNotificationEvents(int callingPid, int callingUid) {
+ if (callingUid == Process.SYSTEM_UID) {
+ return false;
+ }
+ return !(getContext().checkPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS,
+ callingPid, callingUid) == PackageManager.PERMISSION_GRANTED);
+ }
+
private static void deleteRecursively(File f) {
File[] files = f.listFiles();
if (files != null) {
@@ -1038,9 +1051,7 @@
/**
* Called by the Binder stub.
*/
- UsageEvents queryEvents(int userId, long beginTime, long endTime,
- boolean shouldObfuscateInstantApps, boolean shouldHideShortcutInvocationEvents,
- boolean shouldHideLocusIdEvents) {
+ UsageEvents queryEvents(int userId, long beginTime, long endTime, int flags) {
synchronized (mLock) {
if (!mUserUnlockedStates.get(userId)) {
Slog.w(TAG, "Failed to query events for locked user " + userId);
@@ -1051,8 +1062,7 @@
if (service == null) {
return null; // user was stopped or removed
}
- return service.queryEvents(beginTime, endTime, shouldObfuscateInstantApps,
- shouldHideShortcutInvocationEvents, shouldHideLocusIdEvents);
+ return service.queryEvents(beginTime, endTime, flags);
}
}
@@ -1475,10 +1485,15 @@
try {
final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents(
userId, callingPackage, callingPid, callingUid);
- boolean shouldHideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
- return UsageStatsService.this.queryEvents(userId, beginTime, endTime,
- obfuscateInstantApps, hideShortcutInvocationEvents,
- shouldHideLocusIdEvents);
+ final boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
+ final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents(
+ callingPid, callingUid);
+ int flags = UsageEvents.SHOW_ALL_EVENT_DATA;
+ if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS;
+ if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS;
+ if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS;
+ if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
+ return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1525,10 +1540,15 @@
try {
final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents(
userId, callingPackage, callingPid, callingUid);
- boolean shouldHideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
- return UsageStatsService.this.queryEvents(userId, beginTime, endTime,
- obfuscateInstantApps, hideShortcutInvocationEvents,
- shouldHideLocusIdEvents);
+ final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents(
+ callingPid, callingUid);
+ boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
+ int flags = UsageEvents.SHOW_ALL_EVENT_DATA;
+ if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS;
+ if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS;
+ if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS;
+ if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
+ return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -2144,12 +2164,8 @@
}
@Override
- public UsageEvents queryEventsForUser(int userId, long beginTime, long endTime,
- boolean obfuscateInstantApps, boolean shouldHideShortcutInvocationEvents,
- boolean shouldHideLocusIdEvents) {
- return UsageStatsService.this.queryEvents(
- userId, beginTime, endTime, obfuscateInstantApps,
- shouldHideShortcutInvocationEvents, shouldHideLocusIdEvents);
+ public UsageEvents queryEventsForUser(int userId, long beginTime, long endTime, int flags) {
+ return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags);
}
@Override
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index d9317ac..db26d88 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -18,6 +18,10 @@
import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
import static android.app.usage.UsageEvents.Event.DEVICE_STARTUP;
+import static android.app.usage.UsageEvents.HIDE_LOCUS_EVENTS;
+import static android.app.usage.UsageEvents.HIDE_SHORTCUT_EVENTS;
+import static android.app.usage.UsageEvents.OBFUSCATE_INSTANT_APPS;
+import static android.app.usage.UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
import static android.app.usage.UsageStatsManager.INTERVAL_BEST;
import static android.app.usage.UsageStatsManager.INTERVAL_COUNT;
import static android.app.usage.UsageStatsManager.INTERVAL_DAILY;
@@ -481,8 +485,7 @@
return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner);
}
- UsageEvents queryEvents(final long beginTime, final long endTime, boolean obfuscateInstantApps,
- boolean hideShortcutInvocationEvents, boolean hideLocusIdEvents) {
+ UsageEvents queryEvents(final long beginTime, final long endTime, int flags) {
if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
return null;
}
@@ -500,15 +503,22 @@
}
Event event = stats.events.get(i);
- if (hideShortcutInvocationEvents
- && event.mEventType == Event.SHORTCUT_INVOCATION) {
+ final int eventType = event.mEventType;
+ if (eventType == Event.SHORTCUT_INVOCATION
+ && (flags & HIDE_SHORTCUT_EVENTS) == HIDE_SHORTCUT_EVENTS) {
continue;
}
- if (hideLocusIdEvents
- && event.mEventType == Event.LOCUS_ID_SET) {
+ if (eventType == Event.LOCUS_ID_SET
+ && (flags & HIDE_LOCUS_EVENTS) == HIDE_LOCUS_EVENTS) {
continue;
}
- if (obfuscateInstantApps) {
+ if ((eventType == Event.NOTIFICATION_SEEN
+ || eventType == Event.NOTIFICATION_INTERRUPTION)
+ && (flags & OBFUSCATE_NOTIFICATION_EVENTS)
+ == OBFUSCATE_NOTIFICATION_EVENTS) {
+ event = event.getObfuscatedNotificationEvent();
+ }
+ if ((flags & OBFUSCATE_INSTANT_APPS) == OBFUSCATE_INSTANT_APPS) {
event = event.getObfuscatedIfInstantApp();
}
if (event.mPackage != null) {
diff --git a/startop/iorap/functional_tests/AndroidTest.xml b/startop/iorap/functional_tests/AndroidTest.xml
index 41109b4..ef56fc8 100644
--- a/startop/iorap/functional_tests/AndroidTest.xml
+++ b/startop/iorap/functional_tests/AndroidTest.xml
@@ -34,6 +34,11 @@
<option name="run-command" value="rm -r /data/misc/iorapd/*" />
<option name="run-command" value="sleep 1" />
+ <!-- Set system properties to enable perfetto tracing, readahead and detailed logging. -->
+ <option name="run-command" value="setprop iorapd.perfetto.enable true" />
+ <option name="run-command" value="setprop iorapd.readahead.enable true" />
+ <option name="run-command" value="setprop iorapd.log.verbose true" />
+
<option name="run-command" value="start iorapd" />
<!-- give it some time to restart the service; otherwise the first unit test might fail -->
@@ -45,9 +50,5 @@
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
</test>
- <!-- using DeviceSetup again does not work. we simply leave the device in a semi-bad
- state. there is no way to clean this up as far as I know.
- -->
-
</configuration>
diff --git a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
index bd8a45c..4002387 100644
--- a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
+++ b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
@@ -67,7 +67,7 @@
private static final String TEST_ACTIVITY_NAME = "com.android.settings.Settings";
private static final String DB_PATH = "/data/misc/iorapd/sqlite.db";
- private static final Duration TIMEOUT = Duration.ofSeconds(20L);
+ private static final Duration TIMEOUT = Duration.ofSeconds(300L);
private static final String READAHEAD_INDICATOR =
"Description = /data/misc/iorapd/com.android.settings/none/com.android.settings.Settings/compiled_traces/compiled_trace.pb";
@@ -88,7 +88,7 @@
mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), TIMEOUT.getSeconds());
}
- @Test
+ @Test (timeout = 300000)
public void testApp() throws Exception {
assertThat(mDevice, notNullValue());
@@ -247,7 +247,7 @@
if (supplier.getAsBoolean()) {
return true;
}
- TimeUnit.SECONDS.sleep(totalSleepTimeSeconds);
+ TimeUnit.SECONDS.sleep(sleepIntervalSeconds);
totalSleepTimeSeconds += sleepIntervalSeconds;
if (totalSleepTimeSeconds > timeout.getSeconds()) {
return false;
@@ -367,7 +367,7 @@
*
* <p> This should be run as root.</p>
*/
- private String executeShellCommand(String cmd) throws Exception {
+ private static String executeShellCommand(String cmd) throws Exception {
Log.i(TAG, "Execute: " + cmd);
return UiDevice.getInstance(
InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 0d5e688..52213d8 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -17,6 +17,7 @@
package android.telecom;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -458,8 +459,14 @@
/** Call supports the deflect feature. */
public static final int CAPABILITY_SUPPORT_DEFLECT = 0x01000000;
+ /**
+ * Call supports adding participants to the call via
+ * {@link #addConferenceParticipants(List)}.
+ * @hide
+ */
+ public static final int CAPABILITY_ADD_PARTICIPANT = 0x02000000;
//******************************************************************************************
- // Next CAPABILITY value: 0x02000000
+ // Next CAPABILITY value: 0x04000000
//******************************************************************************************
/**
@@ -689,6 +696,9 @@
if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
builder.append(" CAPABILITY_SUPPORT_DEFLECT");
}
+ if (can(capabilities, CAPABILITY_ADD_PARTICIPANT)) {
+ builder.append(" CAPABILITY_ADD_PARTICIPANT");
+ }
builder.append("]");
return builder.toString();
}
@@ -1703,6 +1713,17 @@
}
/**
+ * Pulls participants to existing call by forming a conference call.
+ * See {@link Details#CAPABILITY_ADD_PARTICIPANT}.
+ *
+ * @param participants participants to be pulled to existing call.
+ * @hide
+ */
+ public void addConferenceParticipants(@NonNull List<Uri> participants) {
+ mInCallAdapter.addConferenceParticipants(mTelecomCallId, participants);
+ }
+
+ /**
* Initiates a request to the {@link ConnectionService} to pull an external call to the local
* device.
* <p>
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 9c80c79..f019a9d 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -324,6 +324,13 @@
public void onConnectionAdded(Connection connection) {}
/**
+ * Notifies the {@link Conference} of a request to add a new participants to the conference call
+ * @param participants that will be added to this conference call
+ * @hide
+ */
+ public void onAddConferenceParticipants(@NonNull List<Uri> participants) {}
+
+ /**
* Notifies this Conference, which is in {@code STATE_RINGING}, of
* a request to accept.
* For managed {@link ConnectionService}s, this will be called when the user answers a call via
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 5f860a1..3b0ba25 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -381,8 +381,14 @@
/** Call supports the deflect feature. */
public static final int CAPABILITY_SUPPORT_DEFLECT = 0x02000000;
+ /**
+ * When set, indicates that this {@link Connection} supports initiation of a conference call
+ * by directly adding participants using {@link #onAddConferenceParticipants(List)}.
+ * @hide
+ */
+ public static final int CAPABILITY_ADD_PARTICIPANT = 0x04000000;
//**********************************************************************************************
- // Next CAPABILITY value: 0x04000000
+ // Next CAPABILITY value: 0x08000000
//**********************************************************************************************
/**
@@ -958,7 +964,9 @@
if ((capabilities & CAPABILITY_SUPPORT_DEFLECT) == CAPABILITY_SUPPORT_DEFLECT) {
builder.append(isLong ? " CAPABILITY_SUPPORT_DEFLECT" : " sup_def");
}
-
+ if ((capabilities & CAPABILITY_ADD_PARTICIPANT) == CAPABILITY_ADD_PARTICIPANT) {
+ builder.append(isLong ? " CAPABILITY_ADD_PARTICIPANT" : " add_participant");
+ }
builder.append("]");
return builder.toString();
}
@@ -2975,6 +2983,14 @@
public void onSeparate() {}
/**
+ * Supports initiation of a conference call by directly adding participants to an ongoing call.
+ *
+ * @param participants with which conference call will be formed.
+ * @hide
+ */
+ public void onAddConferenceParticipants(@NonNull List<Uri> participants) {}
+
+ /**
* Notifies this Connection of a request to abort.
*/
public void onAbort() {}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index f77e529..2aea723 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -141,6 +141,7 @@
private static final String SESSION_SPLIT_CONFERENCE = "CS.sFC";
private static final String SESSION_MERGE_CONFERENCE = "CS.mC";
private static final String SESSION_SWAP_CONFERENCE = "CS.sC";
+ private static final String SESSION_ADD_PARTICIPANT = "CS.aP";
private static final String SESSION_POST_DIAL_CONT = "CS.oPDC";
private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
@@ -194,6 +195,7 @@
private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36;
private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
private static final int MSG_REJECT_WITH_REASON = 38;
+ private static final int MSG_ADD_PARTICIPANT = 39;
private static Connection sNullConnection;
@@ -626,6 +628,21 @@
}
@Override
+ public void addConferenceParticipants(String callId, List<Uri> participants,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_ADD_PARTICIPANT);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = participants;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ADD_PARTICIPANT, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void onPostDialContinue(String callId, boolean proceed, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_POST_DIAL_CONT);
try {
@@ -1223,6 +1240,19 @@
}
break;
}
+ case MSG_ADD_PARTICIPANT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_ADD_PARTICIPANT);
+ addConferenceParticipants((String) args.arg1, (List<Uri>)args.arg2);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+
case MSG_ON_POST_DIAL_CONTINUE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
@@ -2151,6 +2181,17 @@
}
}
+ private void addConferenceParticipants(String callId, List<Uri> participants) {
+ Log.d(this, "addConferenceParticipants(%s)", callId);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "addConferenceParticipants")
+ .onAddConferenceParticipants(participants);
+ } else {
+ findConferenceForAction(callId, "addConferenceParticipants")
+ .onAddConferenceParticipants(participants);
+ }
+ }
+
/**
* Notifies a {@link Connection} of a request to pull an external call.
*
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 594c1eb..9d291740 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -283,6 +283,20 @@
}
/**
+ * Instructs Telecom to pull participants to existing call
+ *
+ * @param callId The unique ID of the call.
+ * @param participants participants to be pulled to existing call.
+ */
+ public void addConferenceParticipants(String callId, List<Uri> participants) {
+ try {
+ mAdapter.addConferenceParticipants(callId, participants);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+
+ /**
* Instructs Telecom to split the specified call from any conference call with which it may be
* connected.
*
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index ebfa3a1..982e5f3 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -20,6 +20,7 @@
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.Service;
+import android.app.UiModeManager;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.hardware.camera2.CameraManager;
@@ -43,12 +44,32 @@
* phone calls.
* <h2>Becoming the Default Phone App</h2>
* The default dialer/phone app is one which provides the in-call user interface while the device is
- * in a call. A device is bundled with a system provided default dialer/phone app. The user may
- * choose a single app to take over this role from the system app. An app which wishes to fulfill
- * one this role uses the {@code android.app.role.RoleManager} to request that they fill the role.
+ * in a call. It also provides the user with a means to initiate calls and see a history of calls
+ * on their device. A device is bundled with a system provided default dialer/phone app. The user
+ * may choose a single app to take over this role from the system app. An app which wishes to
+ * fulfill one this role uses the {@link android.app.role.RoleManager} to request that they fill the
+ * {@link android.app.role.RoleManager#ROLE_DIALER} role.
* <p>
- * An app filling the role of the default phone app provides a user interface while the device is in
- * a call, and the device is not in car mode.
+ * The default phone app provides a user interface while the device is in a call, and the device is
+ * not in car mode (i.e. {@link UiModeManager#getCurrentModeType()} is not
+ * {@link android.content.res.Configuration#UI_MODE_TYPE_CAR}).
+ * <p>
+ * In order to fill the {@link android.app.role.RoleManager#ROLE_DIALER} role, an app must meet a
+ * number of requirements:
+ * <ul>
+ * <li>It must handle the {@link Intent#ACTION_DIAL} intent. This means the app must provide
+ * a dial pad UI for the user to initiate outgoing calls.</li>
+ * <li>It must fully implement the {@link InCallService} API and provide both an incoming call
+ * UI, as well as an ongoing call UI.</li>
+ * </ul>
+ * <p>
+ * Note: If the app filling the {@link android.app.role.RoleManager#ROLE_DIALER} crashes during
+ * {@link InCallService} binding, the Telecom framework will automatically fall back to using the
+ * dialer app pre-loaded on the device. The system will display a notification to the user to let
+ * them know that the app has crashed and that their call was continued using the pre-loaded dialer
+ * app.
+ * <p>
+ * Further, the pre-loaded dialer will ALWAYS be used when the user places an emergency call.
* <p>
* Below is an example manifest registration for an {@code InCallService}. The meta-data
* {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular
@@ -82,6 +103,11 @@
* <action android:name="android.intent.action.DIAL" />
* <category android:name="android.intent.category.DEFAULT" />
* </intent-filter>
+ * <intent-filter>
+ * <action android:name="android.intent.action.DIAL" />
+ * <category android:name="android.intent.category.DEFAULT" />
+ * <data android:scheme="tel" />
+ * </intent-filter>
* </activity>
* }
* </pre>
@@ -111,6 +137,7 @@
* }
* }
* }
+ * }
* </pre>
* <p id="incomingCallNotification">
* <h3>Showing the Incoming Call Notification</h3>
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 4249dff..a397d77 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -104,6 +104,9 @@
void swapConference(String conferenceCallId, in Session.Info sessionInfo);
+ void addConferenceParticipants(String CallId, in List<Uri> participants,
+ in Session.Info sessionInfo);
+
void onPostDialContinue(String callId, boolean proceed, in Session.Info sessionInfo);
void pullExternalCall(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index eb2d714..9beff22 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -67,6 +67,8 @@
void swapConference(String callId);
+ void addConferenceParticipants(String callId, in List<Uri> participants);
+
void turnOnProximitySensor();
void turnOffProximitySensor(boolean screenOnImmediately);
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index d2a5905..a27c480 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -661,4 +661,16 @@
})
@Retention(RetentionPolicy.SOURCE)
public @interface Skip464XlatStatus {}
+
+ /**
+ * Override network type
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "OVERRIDE_NETWORK_TYPE_", value = {
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO,
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
+ DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE})
+ public @interface OverrideNetworkType {}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ebb53c5..c51a852 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1090,6 +1090,14 @@
"support_adhoc_conference_calls_bool";
/**
+ * Determines whether conference participants can be added to existing call. When {@code true},
+ * adding conference participants to existing call is supported, {@code false otherwise}.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL =
+ "support_add_conference_participants_bool";
+
+ /**
* Determines whether conference calls are supported by a carrier. When {@code true},
* conference calling is supported, {@code false otherwise}.
*/
@@ -2369,7 +2377,7 @@
* {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}.
*
* For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
- * If the key is invalid or not configured, a default value (RSRP | RSSNR = 1 << 0 | 1 << 2)
+ * If the key is invalid or not configured, a default value (RSRP = 1 << 0)
* will apply.
*
* @hide
@@ -4004,6 +4012,7 @@
sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
@@ -4325,7 +4334,7 @@
sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000);
sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
- CellSignalStrengthLte.USE_RSRP | CellSignalStrengthLte.USE_RSSNR);
+ CellSignalStrengthLte.USE_RSRP);
// Default wifi configurations.
sDefaults.putAll(Wifi.getDefaults());
sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 1cd45e9..2529387 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -181,7 +181,7 @@
mCqi = CellInfo.UNAVAILABLE;
mTimingAdvance = CellInfo.UNAVAILABLE;
mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- mParametersUseForLevel = USE_RSRP | USE_RSSNR;
+ mParametersUseForLevel = USE_RSRP;
}
/** {@inheritDoc} */
@@ -236,7 +236,7 @@
int[] rsrpThresholds, rsrqThresholds, rssnrThresholds;
boolean rsrpOnly;
if (cc == null) {
- mParametersUseForLevel = USE_RSRP | USE_RSSNR;
+ mParametersUseForLevel = USE_RSRP;
rsrpThresholds = sRsrpThresholds;
rsrqThresholds = sRsrqThresholds;
rssnrThresholds = sRssnrThresholds;
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java b/telephony/java/android/telephony/DisplayInfo.aidl
similarity index 74%
rename from core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
rename to telephony/java/android/telephony/DisplayInfo.aidl
index fcacd52..861b0fe 100644
--- a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
+++ b/telephony/java/android/telephony/DisplayInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,9 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.telephony;
-package com.android.internal.view.animation;
-
-public interface NativeInterpolatorFactory {
- long createNativeInterpolator();
-}
+parcelable DisplayInfo;
diff --git a/telephony/java/android/telephony/DisplayInfo.java b/telephony/java/android/telephony/DisplayInfo.java
new file mode 100644
index 0000000..d54bcf9
--- /dev/null
+++ b/telephony/java/android/telephony/DisplayInfo.java
@@ -0,0 +1,172 @@
+/*
+ * 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 android.telephony;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.Annotation.OverrideNetworkType;
+
+import java.util.Objects;
+
+/**
+ * DisplayInfo contains telephony-related information used for display purposes only. This
+ * information is provided in accordance with carrier policy and branding preferences; it is not
+ * necessarily a precise or accurate representation of the current state and should be treated
+ * accordingly.
+ */
+public final class DisplayInfo implements Parcelable {
+ /**
+ * No override. {@link #getNetworkType()} should be used for display network
+ * type.
+ */
+ public static final int OVERRIDE_NETWORK_TYPE_NONE = 0;
+
+ /**
+ * Override network type when the device is connected to
+ * {@link TelephonyManager#NETWORK_TYPE_LTE} cellular network and is using carrier aggregation.
+ */
+ public static final int OVERRIDE_NETWORK_TYPE_LTE_CA = 1;
+
+ /**
+ * Override network type when the device is connected to advanced pro
+ * {@link TelephonyManager#NETWORK_TYPE_LTE} cellular network.
+ */
+ public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2;
+
+ /**
+ * Override network type when the device is connected to
+ * {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC)
+ * capability or is currently connected to the secondary
+ * {@link TelephonyManager#NETWORK_TYPE_NR} cellular network.
+ */
+ public static final int OVERRIDE_NETWORK_TYPE_NR_NSA = 3;
+
+ /**
+ * Override network type when the device is connected to
+ * {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC)
+ * capability or is currently connected to the secondary
+ * {@link TelephonyManager#NETWORK_TYPE_NR} cellular network on millimeter wave bands.
+ *
+ * @see AccessNetworkConstants.NgranBands#FREQUENCY_RANGE_GROUP_2
+ */
+ public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4;
+
+ @NetworkType
+ private final int mNetworkType;
+
+ @OverrideNetworkType
+ private final int mOverrideNetworkType;
+
+ /**
+ * Constructor
+ *
+ * @param networkType Current packet-switching cellular network type
+ * @param overrideNetworkType The override network type
+ *
+ * @hide
+ */
+ public DisplayInfo(@NetworkType int networkType, @OverrideNetworkType int overrideNetworkType) {
+ mNetworkType = networkType;
+ mOverrideNetworkType = overrideNetworkType;
+ }
+
+ /** @hide */
+ public DisplayInfo(Parcel p) {
+ mNetworkType = p.readInt();
+ mOverrideNetworkType = p.readInt();
+ }
+
+ /**
+ * Get current packet-switching cellular network type. This is the actual network type the
+ * device is camped on.
+ *
+ * @return The network type.
+ */
+ @NetworkType
+ public int getNetworkType() {
+ return mNetworkType;
+ }
+
+ /**
+ * Get the override network type. Note the override network type is for market branding
+ * or visualization purposes only. It cannot be treated as the actual network type device is
+ * camped on.
+ *
+ * @return The override network type.
+ */
+ @OverrideNetworkType
+ public int getOverrideNetworkType() {
+ return mOverrideNetworkType;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mNetworkType);
+ dest.writeInt(mOverrideNetworkType);
+ }
+
+ public static final @NonNull Parcelable.Creator<DisplayInfo> CREATOR =
+ new Parcelable.Creator<DisplayInfo>() {
+ @Override
+ public DisplayInfo createFromParcel(Parcel source) {
+ return new DisplayInfo(source);
+ }
+
+ @Override
+ public DisplayInfo[] newArray(int size) {
+ return new DisplayInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DisplayInfo that = (DisplayInfo) o;
+ return mNetworkType == that.mNetworkType
+ && mOverrideNetworkType == that.mOverrideNetworkType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mNetworkType, mOverrideNetworkType);
+ }
+
+ private static String overrideNetworkTypeToString(@OverrideNetworkType int type) {
+ switch (type) {
+ case OVERRIDE_NETWORK_TYPE_NONE: return "NONE";
+ case OVERRIDE_NETWORK_TYPE_LTE_CA: return "LTE_CA";
+ case OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO: return "LTE_ADV_PRO";
+ case OVERRIDE_NETWORK_TYPE_NR_NSA: return "NR_NSA";
+ case OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE: return "NR_NSA_MMWAVE";
+ default: return "UNKNOWN";
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DisplayInfo {network=" + TelephonyManager.getNetworkTypeName(mNetworkType)
+ + ", override=" + overrideNetworkTypeToString(mOverrideNetworkType);
+ }
+}
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 54980a2..250d9e8 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -256,337 +256,6 @@
/** Access Blocked by CDMA network. */
public static final int CDMA_ACCESS_BLOCKED = 1009;
- /** Mapped from ImsReasonInfo */
- // TODO: remove ImsReasonInfo from preciseDisconnectCause
- /* The passed argument is an invalid */
- /** @hide */
- public static final int LOCAL_ILLEGAL_ARGUMENT = 1200;
- // The operation is invoked in invalid call state
- /** @hide */
- public static final int LOCAL_ILLEGAL_STATE = 1201;
- // IMS service internal error
- /** @hide */
- public static final int LOCAL_INTERNAL_ERROR = 1202;
- // IMS service goes down (service connection is lost)
- /** @hide */
- public static final int LOCAL_IMS_SERVICE_DOWN = 1203;
- // No pending incoming call exists
- /** @hide */
- public static final int LOCAL_NO_PENDING_CALL = 1204;
- // Service unavailable; by power off
- /** @hide */
- public static final int LOCAL_POWER_OFF = 1205;
- // Service unavailable; by low battery
- /** @hide */
- public static final int LOCAL_LOW_BATTERY = 1206;
- // Service unavailable; by out of service (data service state)
- /** @hide */
- public static final int LOCAL_NETWORK_NO_SERVICE = 1207;
- /* Service unavailable; by no LTE coverage
- * (VoLTE is not supported even though IMS is registered)
- */
- /** @hide */
- public static final int LOCAL_NETWORK_NO_LTE_COVERAGE = 1208;
- /** Service unavailable; by located in roaming area */
- /** @hide */
- public static final int LOCAL_NETWORK_ROAMING = 1209;
- /** Service unavailable; by IP changed */
- /** @hide */
- public static final int LOCAL_NETWORK_IP_CHANGED = 1210;
- /** Service unavailable; other */
- /** @hide */
- public static final int LOCAL_SERVICE_UNAVAILABLE = 1211;
- /* Service unavailable; IMS connection is lost (IMS is not registered) */
- /** @hide */
- public static final int LOCAL_NOT_REGISTERED = 1212;
- /** Max call exceeded */
- /** @hide */
- public static final int LOCAL_MAX_CALL_EXCEEDED = 1213;
- /** Call decline */
- /** @hide */
- public static final int LOCAL_CALL_DECLINE = 1214;
- /** SRVCC is in progress */
- /** @hide */
- public static final int LOCAL_CALL_VCC_ON_PROGRESSING = 1215;
- /** Resource reservation is failed (QoS precondition) */
- /** @hide */
- public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 1216;
- /** Retry CS call; VoLTE service can't be provided by the network or remote end
- * Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
- * @hide
- */
- public static final int LOCAL_CALL_CS_RETRY_REQUIRED = 1217;
- /** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */
- /** @hide */
- public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED = 1218;
- /** IMS call is already terminated (in TERMINATED state) */
- /** @hide */
- public static final int LOCAL_CALL_TERMINATED = 1219;
- /** Handover not feasible */
- /** @hide */
- public static final int LOCAL_HO_NOT_FEASIBLE = 1220;
-
- /** 1xx waiting timer is expired after sending INVITE request (MO only) */
- /** @hide */
- public static final int TIMEOUT_1XX_WAITING = 1221;
- /** User no answer during call setup operation (MO/MT)
- * MO : 200 OK to INVITE request is not received,
- * MT : No action from user after alerting the call
- * @hide
- */
- public static final int TIMEOUT_NO_ANSWER = 1222;
- /** User no answer during call update operation (MO/MT)
- * MO : 200 OK to re-INVITE request is not received,
- * MT : No action from user after alerting the call
- * @hide
- */
- public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE = 1223;
-
- /**
- * STATUSCODE (SIP response code) (IMS -> Telephony)
- */
- /** SIP request is redirected */
- /** @hide */
- public static final int SIP_REDIRECTED = 1300;
- /** 4xx responses */
- /** 400 : Bad Request */
- /** @hide */
- public static final int SIP_BAD_REQUEST = 1310;
- /** 403 : Forbidden */
- /** @hide */
- public static final int SIP_FORBIDDEN = 1311;
- /** 404 : Not Found */
- /** @hide */
- public static final int SIP_NOT_FOUND = 1312;
- /** 415 : Unsupported Media Type
- * 416 : Unsupported URI Scheme
- * 420 : Bad Extension
- */
- /** @hide */
- public static final int SIP_NOT_SUPPORTED = 1313;
- /** 408 : Request Timeout */
- /** @hide */
- public static final int SIP_REQUEST_TIMEOUT = 1314;
- /** 480 : Temporarily Unavailable */
- /** @hide */
- public static final int SIP_TEMPRARILY_UNAVAILABLE = 1315;
- /** 484 : Address Incomplete */
- /** @hide */
- public static final int SIP_BAD_ADDRESS = 1316;
- /** 486 : Busy Here
- * 600 : Busy Everywhere
- */
- /** @hide */
- public static final int SIP_BUSY = 1317;
- /** 487 : Request Terminated */
- /** @hide */
- public static final int SIP_REQUEST_CANCELLED = 1318;
- /** 406 : Not Acceptable
- * 488 : Not Acceptable Here
- * 606 : Not Acceptable
- */
- /** @hide */
- public static final int SIP_NOT_ACCEPTABLE = 1319;
- /** 410 : Gone
- * 604 : Does Not Exist Anywhere
- */
- /** @hide */
- public static final int SIP_NOT_REACHABLE = 1320;
- /** Others */
- /** @hide */
- public static final int SIP_CLIENT_ERROR = 1321;
- /** 481 : Transaction Does Not Exist */
- /** @hide */
- public static final int SIP_TRANSACTION_DOES_NOT_EXIST = 1322;
- /** 5xx responses
- * 501 : Server Internal Error
- */
- /** @hide */
- public static final int SIP_SERVER_INTERNAL_ERROR = 1330;
- /** 503 : Service Unavailable */
- /** @hide */
- public static final int SIP_SERVICE_UNAVAILABLE = 1331;
- /** 504 : Server Time-out */
- /** @hide */
- public static final int SIP_SERVER_TIMEOUT = 1332;
- /** Others */
- /** @hide */
- public static final int SIP_SERVER_ERROR = 1333;
- /** 6xx responses
- * 603 : Decline
- */
- /** @hide */
- public static final int SIP_USER_REJECTED = 1340;
- /** Others */
- /** @hide */
- public static final int SIP_GLOBAL_ERROR = 1341;
- /** Emergency failure */
- /** @hide */
- public static final int EMERGENCY_TEMP_FAILURE = 1342;
- /** @hide */
- public static final int EMERGENCY_PERM_FAILURE = 1343;
- /** Media resource initialization failed */
- /** @hide */
- public static final int MEDIA_INIT_FAILED = 1400;
- /** RTP timeout (no audio / video traffic in the session) */
- /** @hide */
- public static final int MEDIA_NO_DATA = 1401;
- /** Media is not supported; so dropped the call */
- /** @hide */
- public static final int MEDIA_NOT_ACCEPTABLE = 1402;
- /** Unknown media related errors */
- /** @hide */
- public static final int MEDIA_UNSPECIFIED = 1403;
- /** User triggers the call end */
- /** @hide */
- public static final int USER_TERMINATED = 1500;
- /** No action while an incoming call is ringing */
- /** @hide */
- public static final int USER_NOANSWER = 1501;
- /** User ignores an incoming call */
- /** @hide */
- public static final int USER_IGNORE = 1502;
- /** User declines an incoming call */
- /** @hide */
- public static final int USER_DECLINE = 1503;
- /** Device declines/ends a call due to low battery */
- /** @hide */
- public static final int LOW_BATTERY = 1504;
- /** Device declines call due to blacklisted call ID */
- /** @hide */
- public static final int BLACKLISTED_CALL_ID = 1505;
- /** The call is terminated by the network or remote user */
- /** @hide */
- public static final int USER_TERMINATED_BY_REMOTE = 1510;
-
- /**
- * UT
- */
- /** @hide */
- public static final int UT_NOT_SUPPORTED = 1800;
- /** @hide */
- public static final int UT_SERVICE_UNAVAILABLE = 1801;
- /** @hide */
- public static final int UT_OPERATION_NOT_ALLOWED = 1802;
- /** @hide */
- public static final int UT_NETWORK_ERROR = 1803;
- /** @hide */
- public static final int UT_CB_PASSWORD_MISMATCH = 1804;
-
- /**
- * ECBM
- * @hide
- */
- public static final int ECBM_NOT_SUPPORTED = 1900;
-
- /**
- * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
- * @hide
- */
- public static final int MULTIENDPOINT_NOT_SUPPORTED = 1901;
-
- /**
- * CALL DROP error codes (Call could drop because of many reasons like Network not available,
- * handover, failed, etc)
- */
-
- /**
- * CALL DROP error code for the case when a device is ePDG capable and when the user is on an
- * active wifi call and at the edge of coverage and there is no qualified LTE network available
- * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
- * code is received as part of the handover message.
- * @hide
- */
- public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 2000;
-
- /**
- * MT call has ended due to a release from the network
- * because the call was answered elsewhere
- * @hide
- */
- public static final int ANSWERED_ELSEWHERE = 2100;
-
- /**
- * For MultiEndpoint - Call Pull request has failed
- * @hide
- */
- public static final int CALL_PULL_OUT_OF_SYNC = 2101;
-
- /**
- * For MultiEndpoint - Call has been pulled from primary to secondary
- * @hide
- */
- public static final int CALL_PULLED = 2102;
-
- /**
- * Supplementary services (HOLD/RESUME) failure error codes.
- * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
- * @hide
- */
- public static final int SUPP_SVC_FAILED = 2300;
- /** @hide */
- public static final int SUPP_SVC_CANCELLED = 2301;
- /** @hide */
- public static final int SUPP_SVC_REINVITE_COLLISION = 2302;
-
- /**
- * DPD Procedure received no response or send failed
- * @hide
- */
- public static final int IWLAN_DPD_FAILURE = 2400;
-
- /**
- * Establishment of the ePDG Tunnel Failed
- * @hide
- */
- public static final int EPDG_TUNNEL_ESTABLISH_FAILURE = 2500;
-
- /**
- * Re-keying of the ePDG Tunnel Failed; may not always result in teardown
- * @hide
- */
- public static final int EPDG_TUNNEL_REKEY_FAILURE = 2501;
-
- /**
- * Connection to the packet gateway is lost
- * @hide
- */
- public static final int EPDG_TUNNEL_LOST_CONNECTION = 2502;
-
- /**
- * The maximum number of calls allowed has been reached. Used in a multi-endpoint scenario
- * where the number of calls across all connected devices has reached the maximum.
- * @hide
- */
- public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 2503;
-
- /**
- * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
- * declined the call. Used in a multi-endpoint scenario where a remote device declined an
- * incoming call.
- * @hide
- */
- public static final int REMOTE_CALL_DECLINE = 2504;
-
- /**
- * Indicates the call was disconnected due to the user reaching their data limit.
- * @hide
- */
- public static final int DATA_LIMIT_REACHED = 2505;
-
- /**
- * Indicates the call was disconnected due to the user disabling cellular data.
- * @hide
- */
- public static final int DATA_DISABLED = 2506;
-
- /**
- * Indicates a call was disconnected due to loss of wifi signal.
- * @hide
- */
- public static final int WIFI_LOST = 2507;
-
-
/* OEM specific error codes. To be used by OEMs when they don't want to
reveal error code which would be replaced by ERROR_UNSPECIFIED */
public static final int OEM_CAUSE_1 = 0xf001;
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index c40573b..6fdc13e 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -492,6 +492,7 @@
int RIL_REQUEST_ENABLE_UICC_APPLICATIONS = 208;
int RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT = 209;
int RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS = 210;
+ int RIL_REQUEST_GET_BARRING_INFO = 211;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index 20d0e96..ae20cae 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -430,10 +430,9 @@
private void verifyInstalledFiles(String... filenames) throws DeviceNotAvailableException {
String apkPath = getApkPath(TARGET_PACKAGE);
String appDir = apkPath.substring(0, apkPath.lastIndexOf("/"));
+ // Exclude directories since we only care about files.
HashSet<String> actualFiles = new HashSet<>(Arrays.asList(
- expectRemoteCommandToSucceed("ls " + appDir).split("\n")));
- assertTrue(actualFiles.remove("lib"));
- assertTrue(actualFiles.remove("oat"));
+ expectRemoteCommandToSucceed("ls -p " + appDir + " | grep -v '/'").split("\n")));
HashSet<String> expectedFiles = new HashSet<>(Arrays.asList(filenames));
assertEquals(expectedFiles, actualFiles);
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 95b8f67..da45d9a 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -296,6 +296,8 @@
AppLaunchResult launchResults = null;
if (hasFailureOnFirstLaunch(launch)) {
// skip if the app has failures while launched first
+ Log.w(TAG, "Has failures on first launch: " + launch.getApp());
+ forceStopApp(launch.getApp());
continue;
}
AtraceLogger atraceLogger = null;
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 155c61f..eb78529 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -148,6 +148,7 @@
@Mock private AppOpsManager mAppOps;
@Mock private NotificationManager mNotificationManager;
@Mock private Vpn.SystemServices mSystemServices;
+ @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
@Mock private ConnectivityManager mConnectivityManager;
@Mock private KeyStore mKeyStore;
private final VpnProfile mVpnProfile = new VpnProfile("key");
@@ -867,7 +868,8 @@
* Mock some methods of vpn object.
*/
private Vpn createVpn(@UserIdInt int userId) {
- return new Vpn(Looper.myLooper(), mContext, mNetService, userId, mSystemServices);
+ return new Vpn(Looper.myLooper(), mContext, mNetService,
+ userId, mSystemServices, mIkev2SessionCreator);
}
private static void assertBlocked(Vpn vpn, int... uids) {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index dc5ac1e..e1450cb 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -486,7 +486,7 @@
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
- requirePMF = true;
+ requirePmf = true;
break;
case SECURITY_TYPE_EAP_SUITE_B:
allowedProtocols.set(WifiConfiguration.Protocol.RSN);
@@ -496,14 +496,14 @@
allowedGroupManagementCiphers.set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
// Note: allowedSuiteBCiphers bitset will be set by the service once the
// certificates are attached to this profile
- requirePMF = true;
+ requirePmf = true;
break;
case SECURITY_TYPE_OWE:
allowedProtocols.set(WifiConfiguration.Protocol.RSN);
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
- requirePMF = true;
+ requirePmf = true;
break;
case SECURITY_TYPE_WAPI_PSK:
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WAPI_PSK);
@@ -662,7 +662,7 @@
* @hide
*/
@SystemApi
- public boolean requirePMF;
+ public boolean requirePmf;
/**
* Update identifier, for Passpoint network.
@@ -2169,7 +2169,7 @@
append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN)
.append(" PRIO: ").append(this.priority)
.append(" HIDDEN: ").append(this.hiddenSSID)
- .append(" PMF: ").append(this.requirePMF)
+ .append(" PMF: ").append(this.requirePmf)
.append("CarrierId: ").append(this.carrierId)
.append('\n');
@@ -2761,7 +2761,7 @@
mRandomizedMacAddress = source.mRandomizedMacAddress;
macRandomizationSetting = source.macRandomizationSetting;
randomizedMacExpirationTimeMs = source.randomizedMacExpirationTimeMs;
- requirePMF = source.requirePMF;
+ requirePmf = source.requirePmf;
updateIdentifier = source.updateIdentifier;
carrierId = source.carrierId;
mPasspointUniqueId = source.mPasspointUniqueId;
@@ -2793,7 +2793,7 @@
dest.writeInt(wepTxKeyIndex);
dest.writeInt(priority);
dest.writeInt(hiddenSSID ? 1 : 0);
- dest.writeInt(requirePMF ? 1 : 0);
+ dest.writeInt(requirePmf ? 1 : 0);
dest.writeString(updateIdentifier);
writeBitSet(dest, allowedKeyManagement);
@@ -2869,7 +2869,7 @@
config.wepTxKeyIndex = in.readInt();
config.priority = in.readInt();
config.hiddenSSID = in.readInt() != 0;
- config.requirePMF = in.readInt() != 0;
+ config.requirePmf = in.readInt() != 0;
config.updateIdentifier = in.readString();
config.allowedKeyManagement = readBitSet(in);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index b6f4490..f693315 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -28,6 +28,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.ActivityManager;
@@ -1621,11 +1622,13 @@
* @param wifiConfiguration WifiConfiguration object corresponding to the network
* user selected.
*/
+ @SuppressLint("CallbackMethodName")
default void select(@NonNull WifiConfiguration wifiConfiguration) {}
/**
* User rejected the app's request.
*/
+ @SuppressLint("CallbackMethodName")
default void reject() {}
}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 9990180..9c01d36 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -738,7 +738,16 @@
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
- builder.append("IMSI: ").append(mImsi).append("\n");
+ String imsi;
+ if (mImsi != null) {
+ if (mImsi.length() > 6 && mImsi.charAt(6) != '*') {
+ // Truncate the full IMSI from the log
+ imsi = mImsi.substring(0, 6) + "****";
+ } else {
+ imsi = mImsi;
+ }
+ builder.append("IMSI: ").append(imsi).append("\n");
+ }
builder.append("EAPType: ").append(mEapType).append("\n");
return builder.toString();
}
diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiNl80211Manager.java
similarity index 98%
rename from wifi/java/android/net/wifi/wificond/WifiCondManager.java
rename to wifi/java/android/net/wifi/wificond/WifiNl80211Manager.java
index 61f18e0..89f642f 100644
--- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/wificond/WifiNl80211Manager.java
@@ -49,15 +49,16 @@
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework. The
+ * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework - used
+ * to encapsulate the Wi-Fi 80211nl management interface. The
* interface is only for use by the Wi-Fi framework and access is protected by SELinux permissions.
*
* @hide
*/
@SystemApi
-@SystemService(Context.WIFI_COND_SERVICE)
-public class WifiCondManager {
- private static final String TAG = "WifiCondManager";
+@SystemService(Context.WIFI_NL80211_SERVICE)
+public class WifiNl80211Manager {
+ private static final String TAG = "WifiNl80211Manager";
private boolean mVerboseLoggingEnabled = false;
/**
@@ -316,14 +317,14 @@
public static final int SEND_MGMT_FRAME_ERROR_ALREADY_STARTED = 5;
/** @hide */
- public WifiCondManager(Context context) {
+ public WifiNl80211Manager(Context context) {
mAlarmManager = context.getSystemService(AlarmManager.class);
mEventHandler = new Handler(context.getMainLooper());
}
/** @hide */
@VisibleForTesting
- public WifiCondManager(Context context, IWificond wificond) {
+ public WifiNl80211Manager(Context context, IWificond wificond) {
this(context);
mWificond = wificond;
}
@@ -485,7 +486,7 @@
}
/**
- * Enable or disable verbose logging of the WifiCondManager module.
+ * Enable or disable verbose logging of the WifiNl80211Manager module.
* @param enable True to enable verbose logging. False to disable verbose logging.
*/
public void enableVerboseLogging(boolean enable) {
@@ -493,7 +494,7 @@
}
/**
- * Register a death notification for the WifiCondManager which acts as a proxy for the
+ * Register a death notification for the WifiNl80211Manager which acts as a proxy for the
* wificond daemon (i.e. the death listener will be called when and if the wificond daemon
* dies).
*
@@ -518,7 +519,7 @@
// We already have a wificond handle.
return true;
}
- IBinder binder = ServiceManager.getService(Context.WIFI_COND_SERVICE);
+ IBinder binder = ServiceManager.getService(Context.WIFI_NL80211_SERVICE);
mWificond = IWificond.Stub.asInterface(binder);
if (mWificond == null) {
Log.e(TAG, "Failed to get reference to wificond");
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 05a3dce..00790d5 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -363,7 +363,7 @@
assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE));
assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
- assertTrue(config.requirePMF);
+ assertTrue(config.requirePmf);
}
/**
@@ -380,7 +380,7 @@
assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE));
assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
- assertTrue(config.requirePMF);
+ assertTrue(config.requirePmf);
}
/**
@@ -399,7 +399,7 @@
assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256));
assertTrue(config.allowedGroupManagementCiphers
.get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
- assertTrue(config.requirePMF);
+ assertTrue(config.requirePmf);
}
/**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index af6fb5c..51bf738 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -140,7 +140,7 @@
assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
.get(WifiConfiguration.KeyMgmt.OWE));
assertNull(suggestion.wifiConfiguration.preSharedKey);
- assertTrue(suggestion.wifiConfiguration.requirePMF);
+ assertTrue(suggestion.wifiConfiguration.requirePmf);
assertFalse(suggestion.isUserAllowedToManuallyConnect);
assertTrue(suggestion.isInitialAutoJoinEnabled);
}
@@ -163,7 +163,7 @@
.get(WifiConfiguration.KeyMgmt.SAE));
assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
suggestion.wifiConfiguration.preSharedKey);
- assertTrue(suggestion.wifiConfiguration.requirePMF);
+ assertTrue(suggestion.wifiConfiguration.requirePmf);
assertTrue(suggestion.isUserAllowedToManuallyConnect);
assertFalse(suggestion.isInitialAutoJoinEnabled);
}
@@ -191,7 +191,7 @@
.get(WifiConfiguration.GroupCipher.GCMP_256));
assertTrue(suggestion.wifiConfiguration.allowedGroupManagementCiphers
.get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
- assertTrue(suggestion.wifiConfiguration.requirePMF);
+ assertTrue(suggestion.wifiConfiguration.requirePmf);
assertNull(suggestion.wifiConfiguration.preSharedKey);
// allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
// here.
diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiNl80211ManagerTest.java
similarity index 95%
rename from wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
rename to wifi/tests/src/android/net/wifi/wificond/WifiNl80211ManagerTest.java
index b745a34..a818406 100644
--- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/WifiNl80211ManagerTest.java
@@ -71,10 +71,10 @@
import java.util.Set;
/**
- * Unit tests for {@link android.net.wifi.WifiCondManager}.
+ * Unit tests for {@link android.net.wifi.wificond.WifiNl80211Manager}.
*/
@SmallTest
-public class WifiCondManagerTest {
+public class WifiNl80211ManagerTest {
@Mock
private IWificond mWificond;
@Mock
@@ -86,21 +86,21 @@
@Mock
private IApInterface mApInterface;
@Mock
- private WifiCondManager.SoftApCallback mSoftApListener;
+ private WifiNl80211Manager.SoftApCallback mSoftApListener;
@Mock
- private WifiCondManager.SendMgmtFrameCallback mSendMgmtFrameCallback;
+ private WifiNl80211Manager.SendMgmtFrameCallback mSendMgmtFrameCallback;
@Mock
- private WifiCondManager.ScanEventCallback mNormalScanCallback;
+ private WifiNl80211Manager.ScanEventCallback mNormalScanCallback;
@Mock
- private WifiCondManager.ScanEventCallback mPnoScanCallback;
+ private WifiNl80211Manager.ScanEventCallback mPnoScanCallback;
@Mock
- private WifiCondManager.PnoScanRequestCallback mPnoScanRequestCallback;
+ private WifiNl80211Manager.PnoScanRequestCallback mPnoScanRequestCallback;
@Mock
private Context mContext;
private TestLooper mLooper;
private TestAlarmManager mTestAlarmManager;
private AlarmManager mAlarmManager;
- private WifiCondManager mWificondControl;
+ private WifiNl80211Manager mWificondControl;
private static final String TEST_INTERFACE_NAME = "test_wlan_if";
private static final String TEST_INTERFACE_NAME1 = "test_wlan_if1";
private static final String TEST_INVALID_INTERFACE_NAME = "asdf";
@@ -180,7 +180,7 @@
when(mWificond.tearDownApInterface(any())).thenReturn(true);
when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl);
when(mClientInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
- mWificondControl = new WifiCondManager(mContext, mWificond);
+ mWificondControl = new WifiNl80211Manager(mContext, mWificond);
assertEquals(true,
mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run,
mNormalScanCallback, mPnoScanCallback));
@@ -492,7 +492,7 @@
// getScanResults should fail.
assertEquals(0,
mWificondControl.getScanResults(TEST_INTERFACE_NAME,
- WifiCondManager.SCAN_TYPE_SINGLE_SCAN).size());
+ WifiNl80211Manager.SCAN_TYPE_SINGLE_SCAN).size());
}
/**
@@ -786,10 +786,10 @@
*/
@Test
public void testSendMgmtFrameCalledTwiceBeforeFinished() throws Exception {
- WifiCondManager.SendMgmtFrameCallback cb1 = mock(
- WifiCondManager.SendMgmtFrameCallback.class);
- WifiCondManager.SendMgmtFrameCallback cb2 = mock(
- WifiCondManager.SendMgmtFrameCallback.class);
+ WifiNl80211Manager.SendMgmtFrameCallback cb1 = mock(
+ WifiNl80211Manager.SendMgmtFrameCallback.class);
+ WifiNl80211Manager.SendMgmtFrameCallback cb2 = mock(
+ WifiNl80211Manager.SendMgmtFrameCallback.class);
mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
Runnable::run, cb1);
@@ -800,7 +800,7 @@
mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE,
Runnable::run, cb2);
- verify(cb2).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_ALREADY_STARTED);
+ verify(cb2).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_ALREADY_STARTED);
// verify SendMgmtFrame() still was only called once i.e. not called again
verify(mClientInterface, times(1))
.SendMgmtFrame(any(), any(), anyInt());
@@ -811,8 +811,8 @@
*/
@Test
public void testSendMgmtFrameThrowsException() throws Exception {
- WifiCondManager.SendMgmtFrameCallback cb = mock(
- WifiCondManager.SendMgmtFrameCallback.class);
+ WifiNl80211Manager.SendMgmtFrameCallback cb = mock(
+ WifiNl80211Manager.SendMgmtFrameCallback.class);
final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor =
ArgumentCaptor.forClass(ISendMgmtFrameEvent.class);
@@ -834,7 +834,7 @@
verify(mAlarmManager).cancel(eq(alarmListenerCaptor.getValue()));
sendMgmtFrameEventCaptor.getValue().OnFailure(
- WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
+ WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
mLooper.dispatchAll();
handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm());
@@ -848,8 +848,8 @@
*/
@Test
public void testSendMgmtFrameSuccess() throws Exception {
- WifiCondManager.SendMgmtFrameCallback cb = mock(
- WifiCondManager.SendMgmtFrameCallback.class);
+ WifiNl80211Manager.SendMgmtFrameCallback cb = mock(
+ WifiNl80211Manager.SendMgmtFrameCallback.class);
final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor =
ArgumentCaptor.forClass(ISendMgmtFrameEvent.class);
@@ -882,8 +882,8 @@
*/
@Test
public void testSendMgmtFrameFailure() throws Exception {
- WifiCondManager.SendMgmtFrameCallback cb = mock(
- WifiCondManager.SendMgmtFrameCallback.class);
+ WifiNl80211Manager.SendMgmtFrameCallback cb = mock(
+ WifiNl80211Manager.SendMgmtFrameCallback.class);
final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor =
ArgumentCaptor.forClass(ISendMgmtFrameEvent.class);
@@ -898,10 +898,10 @@
Runnable::run, cb);
sendMgmtFrameEventCaptor.getValue().OnFailure(
- WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
+ WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
mLooper.dispatchAll();
verify(cb, never()).onAck(anyInt());
- verify(cb).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
+ verify(cb).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
verify(mAlarmManager).cancel(eq(alarmListenerCaptor.getValue()));
// verify that even if timeout is triggered afterwards, SendMgmtFrameCallback is not
@@ -917,8 +917,8 @@
*/
@Test
public void testSendMgmtFrameTimeout() throws Exception {
- WifiCondManager.SendMgmtFrameCallback cb = mock(
- WifiCondManager.SendMgmtFrameCallback.class);
+ WifiNl80211Manager.SendMgmtFrameCallback cb = mock(
+ WifiNl80211Manager.SendMgmtFrameCallback.class);
final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor =
ArgumentCaptor.forClass(ISendMgmtFrameEvent.class);
@@ -935,7 +935,7 @@
handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm());
mLooper.dispatchAll();
verify(cb, never()).onAck(anyInt());
- verify(cb).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
+ verify(cb).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
// verify that even if onAck() callback is triggered after timeout,
// SendMgmtFrameCallback is not triggered again
@@ -1006,7 +1006,8 @@
sendMgmtFrameEventCaptor.getValue().OnAck(TEST_SEND_MGMT_FRAME_ELAPSED_TIME_MS);
mLooper.dispatchAll();
verify(mSendMgmtFrameCallback, never()).onAck(anyInt());
- verify(mSendMgmtFrameCallback).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
+ verify(mSendMgmtFrameCallback).onFailure(
+ WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
}
/**
@@ -1032,9 +1033,10 @@
handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm());
// OnFailure posts to the handler
sendMgmtFrameEventCaptor.getValue().OnFailure(
- WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
+ WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN);
mLooper.dispatchAll();
- verify(mSendMgmtFrameCallback).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
+ verify(mSendMgmtFrameCallback).onFailure(
+ WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
}
/**