Merge "Implement ComplicationTypesUpdater."
diff --git a/INPUT_OWNERS b/INPUT_OWNERS
new file mode 100644
index 0000000..6041f637f
--- /dev/null
+++ b/INPUT_OWNERS
@@ -0,0 +1,3 @@
+michaelwr@google.com
+prabirmsp@google.com
+svv@google.com
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 6d2bd47..2682dd7 100644
--- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
@@ -16,7 +16,7 @@
package com.android.server.job;
-import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.util.proto.ProtoOutputStream;
@@ -47,9 +47,9 @@
void removeBackingUpUid(int uid);
void clearAllBackingUpUids();
- /** Returns the package responsible for backing up media on the device. */
- @NonNull
- String getMediaBackupPackage();
+ /** Returns the package responsible for providing media from the cloud to the device. */
+ @Nullable
+ String getCloudMediaProviderPackage(int userId);
/**
* The user has started interacting with the app. Take any appropriate action.
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 e23e067..bdfdd55 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -52,6 +52,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
+import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.BatteryManager;
@@ -70,6 +71,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.os.storage.StorageManagerInternal;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.format.DateUtils;
@@ -86,10 +88,10 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -237,6 +239,7 @@
static final int MSG_UID_ACTIVE = 6;
static final int MSG_UID_IDLE = 7;
static final int MSG_CHECK_CHANGED_JOB_LIST = 8;
+ static final int MSG_CHECK_MEDIA_EXEMPTION = 9;
/**
* Track Services that have currently active or pending jobs. The index is provided by
@@ -271,8 +274,8 @@
@GuardedBy("mLock")
private final BatteryStateTracker mBatteryStateTracker;
- @NonNull
- private final String mSystemGalleryPackage;
+ @GuardedBy("mLock")
+ private final SparseArray<String> mCloudMediaProviderPackages = new SparseArray<>();
private final CountQuotaTracker mQuotaTracker;
private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()";
@@ -1783,9 +1786,6 @@
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()) {
@@ -1855,6 +1855,9 @@
mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull(
LocalServices.getService(AppStateTracker.class));
+ LocalServices.getService(StorageManagerInternal.class)
+ .registerCloudProviderChangeListener(new CloudProviderChangeListener());
+
// Register br for package removals and user removals.
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
@@ -2353,6 +2356,15 @@
break;
}
+ case MSG_CHECK_MEDIA_EXEMPTION: {
+ final SomeArgs args = (SomeArgs) message.obj;
+ synchronized (mLock) {
+ updateMediaBackupExemptionLocked(
+ args.argi1, (String) args.arg1, (String) args.arg2);
+ }
+ args.recycle();
+ break;
+ }
}
maybeRunPendingJobsLocked();
}
@@ -2649,12 +2661,31 @@
if (DEBUG) {
Slog.d(TAG, "Check changed jobs...");
}
+ if (mChangedJobList.size() == 0) {
+ return;
+ }
mChangedJobList.forEach(mMaybeQueueFunctor);
mMaybeQueueFunctor.postProcessLocked();
mChangedJobList.clear();
}
+ @GuardedBy("mLock")
+ private void updateMediaBackupExemptionLocked(int userId, @Nullable String oldPkg,
+ @Nullable String newPkg) {
+ final Predicate<JobStatus> shouldProcessJob =
+ (job) -> job.getSourceUserId() == userId
+ && (job.getSourcePackageName().equals(oldPkg)
+ || job.getSourcePackageName().equals(newPkg));
+ mJobs.forEachJob(shouldProcessJob,
+ (job) -> {
+ if (job.updateMediaBackupExemptionStatus()) {
+ mChangedJobList.add(job);
+ }
+ });
+ mHandler.sendEmptyMessage(MSG_CHECK_CHANGED_JOB_LIST);
+ }
+
/** Returns true if both the calling and source users for the job are started. */
@GuardedBy("mLock")
public boolean areUsersStartedLocked(final JobStatus job) {
@@ -3050,8 +3081,8 @@
}
@Override
- public String getMediaBackupPackage() {
- return mSystemGalleryPackage;
+ public String getCloudMediaProviderPackage(int userId) {
+ return mCloudMediaProviderPackages.get(userId);
}
@Override
@@ -3159,6 +3190,35 @@
return bucket;
}
+ private class CloudProviderChangeListener implements
+ StorageManagerInternal.CloudProviderChangeListener {
+
+ @Override
+ public void onCloudProviderChanged(int userId, @Nullable String authority) {
+ final PackageManager pm = getContext()
+ .createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager();
+ final ProviderInfo pi = pm.resolveContentProvider(
+ authority, PackageManager.ComponentInfoFlags.of(0));
+ final String newPkg = (pi == null) ? null : pi.packageName;
+ synchronized (mLock) {
+ final String oldPkg = mCloudMediaProviderPackages.get(userId);
+ if (!Objects.equals(oldPkg, newPkg)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Cloud provider of user " + userId + " changed from " + oldPkg
+ + " to " + newPkg);
+ }
+ mCloudMediaProviderPackages.put(userId, newPkg);
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = userId;
+ args.arg1 = oldPkg;
+ args.arg2 = newPkg;
+ mHandler.obtainMessage(MSG_CHECK_MEDIA_EXEMPTION, args).sendToTarget();
+ }
+ }
+ }
+ }
+
/**
* Binder stub trampoline implementation
*/
@@ -3799,6 +3859,12 @@
pw.println();
pw.println("Started users: " + Arrays.toString(mStartedUsers));
+ pw.println();
+
+ pw.print("Media Cloud Providers: ");
+ pw.println(mCloudMediaProviderPackages);
+ pw.println();
+
pw.print("Registered ");
pw.print(mJobs.size());
pw.println(" jobs:");
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 acbb1c1..649aa39 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
@@ -261,11 +261,9 @@
*
* 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;
+ private boolean mHasMediaBackupExemption;
+ private final boolean mHasExemptedMediaUrisOnly;
// Set to true if doze constraint was satisfied due to app being whitelisted.
boolean appHasDozeExemption;
@@ -508,11 +506,9 @@
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;
@@ -531,6 +527,7 @@
}
}
}
+ mHasExemptedMediaUrisOnly = exemptedMediaUrisOnly;
this.requiredConstraints = requiredConstraints;
mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
addDynamicConstraints(dynamicConstraints);
@@ -563,9 +560,7 @@
job = builder.build(false);
}
- final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
- mHasMediaBackupExemption = !job.hasLateConstraint() && exemptedMediaUrisOnly
- && requiresNetwork && this.sourcePackageName.equals(jsi.getMediaBackupPackage());
+ updateMediaBackupExemptionStatus();
}
/** Copy constructor: used specifically when cloning JobStatus objects for persistence,
@@ -914,6 +909,25 @@
mFirstForceBatchedTimeElapsed = now;
}
+ /**
+ * Re-evaluates the media backup exemption status.
+ *
+ * @return true if the exemption status changed
+ */
+ public boolean updateMediaBackupExemptionStatus() {
+ final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
+ boolean hasMediaExemption = mHasExemptedMediaUrisOnly
+ && !job.hasLateConstraint()
+ && job.getRequiredNetwork() != null
+ && getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT
+ && sourcePackageName.equals(jsi.getCloudMediaProviderPackage(sourceUserId));
+ if (mHasMediaBackupExemption == hasMediaExemption) {
+ return false;
+ }
+ mHasMediaBackupExemption = hasMediaExemption;
+ return true;
+ }
+
public String getSourceTag() {
return sourceTag;
}
@@ -2027,6 +2041,7 @@
TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw);
pw.println();
}
+ pw.print("Has media backup exemption", mHasMediaBackupExemption).println();
}
if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
pw.print("Extras: ");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 19f2537..b96055f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -1099,7 +1099,7 @@
final long maxExecutionTimeRemainingMs =
mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs;
- if (maxExecutionTimeRemainingMs <= 0) {
+ if (maxExecutionTimeRemainingMs < 0) {
return 0;
}
@@ -1110,7 +1110,7 @@
sessions, startMaxElapsed, maxExecutionTimeRemainingMs);
}
- if (allowedTimeRemainingMs <= 0) {
+ if (allowedTimeRemainingMs < 0) {
return 0;
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 77f869a..ea3830f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -866,6 +866,7 @@
field public static final int installLocation = 16843447; // 0x10102b7
field public static final int interactiveUiTimeout = 16844181; // 0x1010595
field public static final int interpolator = 16843073; // 0x1010141
+ field public static final int intro;
field public static final int isAccessibilityTool = 16844353; // 0x1010641
field public static final int isAlwaysSyncable = 16843571; // 0x1010333
field public static final int isAsciiCapable = 16843753; // 0x10103e9
@@ -3247,6 +3248,7 @@
method @Nullable public String getTileServiceClassName();
method public boolean isAccessibilityTool();
method public String loadDescription(android.content.pm.PackageManager);
+ method @Nullable public CharSequence loadIntro(@NonNull android.content.pm.PackageManager);
method public CharSequence loadSummary(android.content.pm.PackageManager);
method public void setInteractiveUiTimeoutMillis(@IntRange(from=0) int);
method public void setNonInteractiveUiTimeoutMillis(@IntRange(from=0) int);
@@ -6634,6 +6636,7 @@
method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>);
method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational);
method @NonNull public android.app.PictureInPictureParams.Builder setAutoEnterEnabled(boolean);
+ method @NonNull public android.app.PictureInPictureParams.Builder setExpandedAspectRatio(@Nullable android.util.Rational);
method @NonNull public android.app.PictureInPictureParams.Builder setSeamlessResizeEnabled(boolean);
method public android.app.PictureInPictureParams.Builder setSourceRectHint(android.graphics.Rect);
}
@@ -7046,6 +7049,7 @@
public final class VoiceInteractor {
method public android.app.VoiceInteractor.Request getActiveRequest(String);
method public android.app.VoiceInteractor.Request[] getActiveRequests();
+ method @NonNull public String getPackageName();
method public boolean isDestroyed();
method public void notifyDirectActionsChanged();
method public boolean registerOnDestroyedCallback(@NonNull java.util.concurrent.Executor, @NonNull Runnable);
@@ -7887,6 +7891,8 @@
field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
+ field public static final int TAG_BLUETOOTH_CONNECTION = 210039; // 0x33477
+ field public static final int TAG_BLUETOOTH_DISCONNECTION = 210040; // 0x33478
field public static final int TAG_CAMERA_POLICY_SET = 210034; // 0x33472
field public static final int TAG_CERT_AUTHORITY_INSTALLED = 210029; // 0x3346d
field public static final int TAG_CERT_AUTHORITY_REMOVED = 210030; // 0x3346e
@@ -7909,6 +7915,7 @@
field public static final int TAG_MEDIA_UNMOUNT = 210014; // 0x3345e
field public static final int TAG_OS_SHUTDOWN = 210010; // 0x3345a
field public static final int TAG_OS_STARTUP = 210009; // 0x33459
+ field public static final int TAG_PASSWORD_CHANGED = 210036; // 0x33474
field public static final int TAG_PASSWORD_COMPLEXITY_REQUIRED = 210035; // 0x33473
field public static final int TAG_PASSWORD_COMPLEXITY_SET = 210017; // 0x33461
field public static final int TAG_PASSWORD_EXPIRATION_SET = 210016; // 0x33460
@@ -7918,6 +7925,8 @@
field public static final int TAG_SYNC_SEND_FILE = 210004; // 0x33454
field public static final int TAG_USER_RESTRICTION_ADDED = 210027; // 0x3346b
field public static final int TAG_USER_RESTRICTION_REMOVED = 210028; // 0x3346c
+ field public static final int TAG_WIFI_CONNECTION = 210037; // 0x33475
+ field public static final int TAG_WIFI_DISCONNECTION = 210038; // 0x33476
field public static final int TAG_WIPE_FAILURE = 210023; // 0x33467
}
@@ -11859,6 +11868,7 @@
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";
+ field public static final String FEATURE_EXPANDED_PICTURE_IN_PICTURE = "android.software.expanded_picture_in_picture";
field public static final String FEATURE_FACE = "android.hardware.biometrics.face";
field public static final String FEATURE_FAKETOUCH = "android.hardware.faketouch";
field public static final String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
@@ -17370,6 +17380,7 @@
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_PREVIEW_STABILIZATION_OUTPUT_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS;
@@ -18350,8 +18361,8 @@
}
public final class DisplayManager {
- method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, int, int, int, @Nullable android.view.Surface, int);
- method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, int, int, int, @Nullable android.view.Surface, int, @Nullable android.hardware.display.VirtualDisplay.Callback, @Nullable android.os.Handler);
+ method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int);
+ method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable android.hardware.display.VirtualDisplay.Callback, @Nullable android.os.Handler);
method public android.view.Display getDisplay(int);
method public android.view.Display[] getDisplays();
method public android.view.Display[] getDisplays(String);
@@ -24368,6 +24379,17 @@
package android.media.metrics {
+ public final class BundleSession implements java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public android.media.metrics.LogSessionId getSessionId();
+ method public void reportBundleMetrics(@NonNull android.os.PersistableBundle);
+ }
+
+ public final class EditingSession implements java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public android.media.metrics.LogSessionId getSessionId();
+ }
+
public abstract class Event {
method @NonNull public android.os.Bundle getMetricsBundle();
method @IntRange(from=0xffffffff) public long getTimeSinceCreatedMillis();
@@ -24379,8 +24401,11 @@
}
public final class MediaMetricsManager {
+ method @NonNull public android.media.metrics.BundleSession createBundleSession();
+ method @NonNull public android.media.metrics.EditingSession createEditingSession();
method @NonNull public android.media.metrics.PlaybackSession createPlaybackSession();
method @NonNull public android.media.metrics.RecordingSession createRecordingSession();
+ method @NonNull public android.media.metrics.TranscodingSession createTranscodingSession();
field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL
}
@@ -24628,6 +24653,11 @@
method @NonNull public android.media.metrics.TrackChangeEvent.Builder setWidth(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
}
+ public final class TranscodingSession implements java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public android.media.metrics.LogSessionId getSessionId();
+ }
+
}
package android.media.midi {
@@ -39500,9 +39530,9 @@
public static final class RecognitionSupport.Builder {
ctor public RecognitionSupport.Builder();
- method @NonNull public android.speech.RecognitionSupport.Builder addInstalledLanguages(@NonNull String);
- method @NonNull public android.speech.RecognitionSupport.Builder addPendingLanguages(@NonNull String);
- method @NonNull public android.speech.RecognitionSupport.Builder addSupportedLanguages(@NonNull String);
+ method @NonNull public android.speech.RecognitionSupport.Builder addInstalledLanguage(@NonNull String);
+ method @NonNull public android.speech.RecognitionSupport.Builder addPendingLanguage(@NonNull String);
+ method @NonNull public android.speech.RecognitionSupport.Builder addSupportedLanguage(@NonNull String);
method @NonNull public android.speech.RecognitionSupport build();
method @NonNull public android.speech.RecognitionSupport.Builder setInstalledLanguages(@NonNull java.util.List<java.lang.String>);
method @NonNull public android.speech.RecognitionSupport.Builder setPendingLanguages(@NonNull java.util.List<java.lang.String>);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 24b4f89..594f46b 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -415,6 +415,7 @@
method public long computeStorageCacheBytes(@NonNull java.io.File);
method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int);
method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int);
+ method public void setCloudMediaProvider(@Nullable String);
field public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 1; // 0x1
field public static final int APP_IO_BLOCKED_REASON_UNKNOWN = 0; // 0x0
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5e5f387..584abc9 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -355,6 +355,7 @@
field public static final String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS";
field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
+ field public static final String WRITE_SECURITY_LOG = "android.permission.WRITE_SECURITY_LOG";
field public static final String WRITE_SMS = "android.permission.WRITE_SMS";
}
@@ -1307,6 +1308,10 @@
field public static final int ERROR_UNKNOWN = 0; // 0x0
}
+ public class SecurityLog {
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURITY_LOG) public static int writeEvent(int, @NonNull java.lang.Object...);
+ }
+
public final class SystemUpdatePolicy implements android.os.Parcelable {
method public android.app.admin.SystemUpdatePolicy.InstallationOption getInstallationOptionAt(long);
field public static final int TYPE_PAUSE = 4; // 0x4
@@ -1647,6 +1652,7 @@
method public int getResultNumber();
method public int getResultOffset();
method @NonNull public android.os.Bundle getSearchConstraints();
+ method @NonNull public String getSource();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final String CONSTRAINT_IS_PRESUBMIT_SUGGESTION = "IS_PRESUBMIT_SUGGESTION";
field public static final String CONSTRAINT_SEARCH_PROVIDER_FILTER = "SEARCH_PROVIDER_FILTER";
@@ -2755,7 +2761,7 @@
method public void addActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener, @NonNull java.util.concurrent.Executor);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback);
- method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(int, int, int, @Nullable android.view.Surface, int, @Nullable android.os.Handler, @Nullable android.hardware.display.VirtualDisplay.Callback);
+ method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @NonNull java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
@@ -6279,6 +6285,9 @@
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getHeadTrackingMode();
method @IntRange(from=0) @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getOutput();
method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<java.lang.Integer> getSupportedHeadTrackingModes();
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public boolean hasHeadTracker(@NonNull android.media.AudioDeviceAttributes);
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public boolean isAvailableForDevice(@NonNull android.media.AudioDeviceAttributes);
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public boolean isHeadTrackerEnabled(@NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void recenterHeadTracker();
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeOnHeadTrackingModeChangedListener(@NonNull android.media.Spatializer.OnHeadTrackingModeChangedListener);
@@ -6286,6 +6295,7 @@
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setEffectParameter(int, @NonNull byte[]);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setEnabled(boolean);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setGlobalTransform(@NonNull float[]);
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setHeadTrackerEnabled(boolean, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setOnHeadToSoundstagePoseUpdatedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadToSoundstagePoseUpdatedListener);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setOnSpatializerOutputChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerOutputChangedListener);
field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_DISABLED = -1; // 0xffffffff
@@ -9700,7 +9710,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean hasRestrictedProfiles();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isAdminUser();
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isCloneProfile();
+ method public boolean isCloneProfile();
method public boolean isCredentialSharedWithParent();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isGuestUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bcba21b0..84b393a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -352,6 +352,7 @@
public final class PictureInPictureParams implements android.os.Parcelable {
method public java.util.List<android.app.RemoteAction> getActions();
method public float getAspectRatio();
+ method public float getExpandedAspectRatio();
method public android.graphics.Rect getSourceRectHint();
method public boolean isSeamlessResizeEnabled();
}
@@ -605,6 +606,14 @@
}
+package android.app.cloudsearch {
+
+ public static final class SearchRequest.Builder {
+ method @NonNull public android.app.cloudsearch.SearchRequest.Builder setSource(@NonNull String);
+ }
+
+}
+
package android.app.contentsuggestions {
public final class ContentSuggestionsManager {
@@ -1959,6 +1968,7 @@
method public long computeStorageCacheBytes(@NonNull java.io.File);
method @NonNull public static java.util.UUID convert(@NonNull String);
method @NonNull public static String convert(@NonNull java.util.UUID);
+ method @Nullable public String getCloudMediaProvider();
method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int);
method public static boolean isUserKeyUnlocked(int);
field public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high";
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 85e7854..8af68d7 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -569,6 +569,11 @@
private String mNonLocalizedSummary;
/**
+ * Resource id of the intro of the accessibility service.
+ */
+ private int mIntroResId;
+
+ /**
* Resource id of the description of the accessibility service.
*/
private int mDescriptionResId;
@@ -737,6 +742,11 @@
R.styleable.AccessibilityService_isAccessibilityTool, false);
mTileServiceClassName = asAttributes.getString(
com.android.internal.R.styleable.AccessibilityService_tileService);
+ peekedValue = asAttributes.peekValue(
+ com.android.internal.R.styleable.AccessibilityService_intro);
+ if (peekedValue != null) {
+ mIntroResId = peekedValue.resourceId;
+ }
asAttributes.recycle();
} catch (NameNotFoundException e) {
throw new XmlPullParserException( "Unable to create context for: "
@@ -957,6 +967,29 @@
}
/**
+ * The localized intro of the accessibility service.
+ * <p>
+ * <strong>Statically set from
+ * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
+ * </p>
+ * @return The localized intro if available, and {@code null} if a intro
+ * has not been provided.
+ */
+ @Nullable
+ public CharSequence loadIntro(@NonNull PackageManager packageManager) {
+ if (mIntroResId == /* invalid */ 0) {
+ return null;
+ }
+ ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
+ CharSequence intro = packageManager.getText(serviceInfo.packageName,
+ mIntroResId, serviceInfo.applicationInfo);
+ if (intro != null) {
+ return intro.toString().trim();
+ }
+ return null;
+ }
+
+ /**
* Gets the non-localized description of the accessibility service.
* <p>
* <strong>Statically set from
@@ -1114,6 +1147,7 @@
parcel.writeString(mNonLocalizedDescription);
parcel.writeBoolean(mIsAccessibilityTool);
parcel.writeString(mTileServiceClassName);
+ parcel.writeInt(mIntroResId);
}
private void initFromParcel(Parcel parcel) {
@@ -1137,6 +1171,7 @@
mNonLocalizedDescription = parcel.readString();
mIsAccessibilityTool = parcel.readBoolean();
mTileServiceClassName = parcel.readString();
+ mIntroResId = parcel.readInt();
}
@Override
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index 52a1cad..9a73219 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -70,6 +70,11 @@
private final ActivityInfo mActivityInfo;
/**
+ * Resource id of the intro of the accessibility shortcut target.
+ */
+ private final int mIntroResId;
+
+ /**
* Resource id of the summary of the accessibility shortcut target.
*/
private final int mSummaryResId;
@@ -160,6 +165,9 @@
// Get tile service class name
mTileServiceClassName = asAttributes.getString(
com.android.internal.R.styleable.AccessibilityShortcutTarget_tileService);
+ // Gets intro
+ mIntroResId = asAttributes.getResourceId(
+ com.android.internal.R.styleable.AccessibilityShortcutTarget_intro, 0);
asAttributes.recycle();
if ((mDescriptionResId == 0 && mHtmlDescriptionRes == 0) || mSummaryResId == 0) {
@@ -203,6 +211,16 @@
}
/**
+ * The localized intro of the accessibility shortcut target.
+ *
+ * @return The localized intro.
+ */
+ @Nullable
+ public String loadIntro(@NonNull PackageManager packageManager) {
+ return loadResourceString(packageManager, mActivityInfo, mIntroResId);
+ }
+
+ /**
* The localized description of the accessibility shortcut target.
*
* @return The localized description.
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index a58ceaa..294621e 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -779,6 +779,16 @@
* @param started {@code true} if the process transits from non-FGS state to FGS state.
*/
void onForegroundServiceStateChanged(String packageName, int uid, int pid, boolean started);
+
+ /**
+ * Call when the notification of the foreground service is updated.
+ *
+ * @param packageName The package name of the process.
+ * @param uid The UID of the process.
+ * @param foregroundId The current foreground service notification ID, a negative value
+ * means this notification is being removed.
+ */
+ void onForegroundServiceNotificationUpdated(String packageName, int uid, int foregroundId);
}
/**
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index acbcb3e..5012121 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1121,7 +1121,7 @@
mPackageName = opts.getString(KEY_PACKAGE_NAME);
try {
- mUsageTimeReport = opts.getParcelable(KEY_USAGE_TIME_REPORT);
+ mUsageTimeReport = opts.getParcelable(KEY_USAGE_TIME_REPORT, PendingIntent.class);
} catch (RuntimeException e) {
Slog.w(TAG, e);
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ec2115c..a3dd705a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2900,6 +2900,14 @@
}
}
+ /**
+ * @hide
+ */
+ @Override
+ public int getAssociatedDisplayId() {
+ return isAssociatedWithDisplay() ? getDisplayId() : Display.INVALID_DISPLAY;
+ }
+
@Override
public Display getDisplayNoVerify() {
if (mDisplay == null) {
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index cc8b182..490afc1 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -128,6 +128,7 @@
int callingUid, in Intent intent, in String resolvedType,
in IVoiceInteractionSession session, in IVoiceInteractor interactor, int flags,
in ProfilerInfo profilerInfo, in Bundle options, int userId);
+ String getVoiceInteractorPackageName(in IBinder callingVoiceInteractor);
int startAssistantActivity(in String callingPackage, in String callingFeatureId, int callingPid,
int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY)")
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 5d1f4df..9910000 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -591,7 +591,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean isDeviceLocked(int userId) {
try {
- return mTrustManager.isDeviceLocked(userId);
+ return mTrustManager.isDeviceLocked(userId, mContext.getAssociatedDisplayId());
} catch (RemoteException e) {
return false;
}
@@ -617,7 +617,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isDeviceSecure(int userId) {
try {
- return mTrustManager.isDeviceSecure(userId);
+ return mTrustManager.isDeviceSecure(userId, mContext.getAssociatedDisplayId());
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index 358ce6a..18343fd 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -18,7 +18,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.TestApi;
+import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -43,6 +45,9 @@
private Rational mAspectRatio;
@Nullable
+ private Rational mExpandedAspectRatio;
+
+ @Nullable
private List<RemoteAction> mUserActions;
@Nullable
@@ -67,6 +72,24 @@
}
/**
+ * Sets the aspect ratio for the expanded picture-in-picture mode. The aspect ratio is
+ * defined as the desired width / height. <br/>
+ * The aspect ratio cannot be changed from horizontal to vertical or vertical to horizontal
+ * while the PIP is shown. Any such changes will be ignored. <br/>
+ *
+ * Setting the expanded ratio shows the activity's support for expanded mode.
+ *
+ * @param expandedAspectRatio must not be between 2.39:1 and 1:2.39 (inclusive). If {@code
+ * null}, expanded picture-in-picture mode is not supported.
+ * @return this builder instance.
+ */
+ @RequiresFeature(PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE)
+ public @NonNull Builder setExpandedAspectRatio(@Nullable Rational expandedAspectRatio) {
+ mExpandedAspectRatio = expandedAspectRatio;
+ return this;
+ }
+
+ /**
* Sets the user actions. If there are more than
* {@link Activity#getMaxNumPictureInPictureActions()} actions, then the input list
* will be truncated to that number.
@@ -152,7 +175,8 @@
* @see Activity#setPictureInPictureParams(PictureInPictureParams)
*/
public PictureInPictureParams build() {
- PictureInPictureParams params = new PictureInPictureParams(mAspectRatio, mUserActions,
+ PictureInPictureParams params = new PictureInPictureParams(mAspectRatio,
+ mExpandedAspectRatio, mUserActions,
mSourceRectHint, mAutoEnterEnabled, mSeamlessResizeEnabled);
return params;
}
@@ -165,6 +189,12 @@
private Rational mAspectRatio;
/**
+ * The expected aspect ratio of the vertically expanded picture-in-picture window.
+ */
+ @Nullable
+ private Rational mExpandedAspectRatio;
+
+ /**
* The set of actions that are associated with this activity when in picture-in-picture.
*/
@Nullable
@@ -197,9 +227,8 @@
/** {@hide} */
PictureInPictureParams(Parcel in) {
- if (in.readInt() != 0) {
- mAspectRatio = new Rational(in.readInt(), in.readInt());
- }
+ mAspectRatio = readRationalFromParcel(in);
+ mExpandedAspectRatio = readRationalFromParcel(in);
if (in.readInt() != 0) {
mUserActions = new ArrayList<>();
in.readTypedList(mUserActions, RemoteAction.CREATOR);
@@ -216,9 +245,11 @@
}
/** {@hide} */
- PictureInPictureParams(Rational aspectRatio, List<RemoteAction> actions,
- Rect sourceRectHint, Boolean autoEnterEnabled, Boolean seamlessResizeEnabled) {
+ PictureInPictureParams(Rational aspectRatio, Rational expandedAspectRatio,
+ List<RemoteAction> actions, Rect sourceRectHint, Boolean autoEnterEnabled,
+ Boolean seamlessResizeEnabled) {
mAspectRatio = aspectRatio;
+ mExpandedAspectRatio = expandedAspectRatio;
mUserActions = actions;
mSourceRectHint = sourceRectHint;
mAutoEnterEnabled = autoEnterEnabled;
@@ -230,7 +261,7 @@
* @hide
*/
public PictureInPictureParams(PictureInPictureParams other) {
- this(other.mAspectRatio, other.mUserActions,
+ this(other.mAspectRatio, other.mExpandedAspectRatio, other.mUserActions,
other.hasSourceBoundsHint() ? new Rect(other.getSourceRectHint()) : null,
other.mAutoEnterEnabled, other.mSeamlessResizeEnabled);
}
@@ -243,6 +274,10 @@
if (otherArgs.hasSetAspectRatio()) {
mAspectRatio = otherArgs.mAspectRatio;
}
+
+ // Copy either way because null can be used to explicitly unset the value
+ mExpandedAspectRatio = otherArgs.mExpandedAspectRatio;
+
if (otherArgs.hasSetActions()) {
mUserActions = otherArgs.mUserActions;
}
@@ -283,6 +318,26 @@
}
/**
+ * @return the expanded aspect ratio. If none is set, return 0.
+ * @hide
+ */
+ @TestApi
+ public float getExpandedAspectRatio() {
+ if (mExpandedAspectRatio != null) {
+ return mExpandedAspectRatio.floatValue();
+ }
+ return 0f;
+ }
+
+ /**
+ * @return whether the expanded aspect ratio is set
+ * @hide
+ */
+ public boolean hasSetExpandedAspectRatio() {
+ return mExpandedAspectRatio != null;
+ }
+
+ /**
* @return the set of user actions.
* @hide
*/
@@ -349,7 +404,8 @@
*/
public boolean empty() {
return !hasSourceBoundsHint() && !hasSetActions() && !hasSetAspectRatio()
- && mAutoEnterEnabled != null && mSeamlessResizeEnabled != null;
+ && !hasSetExpandedAspectRatio() && mAutoEnterEnabled != null
+ && mSeamlessResizeEnabled != null;
}
@Override
@@ -360,13 +416,14 @@
return Objects.equals(mAutoEnterEnabled, that.mAutoEnterEnabled)
&& Objects.equals(mSeamlessResizeEnabled, that.mSeamlessResizeEnabled)
&& Objects.equals(mAspectRatio, that.mAspectRatio)
+ && Objects.equals(mExpandedAspectRatio, that.mExpandedAspectRatio)
&& Objects.equals(mUserActions, that.mUserActions)
&& Objects.equals(mSourceRectHint, that.mSourceRectHint);
}
@Override
public int hashCode() {
- return Objects.hash(mAspectRatio, mUserActions, mSourceRectHint,
+ return Objects.hash(mAspectRatio, mExpandedAspectRatio, mUserActions, mSourceRectHint,
mAutoEnterEnabled, mSeamlessResizeEnabled);
}
@@ -377,13 +434,8 @@
@Override
public void writeToParcel(Parcel out, int flags) {
- if (mAspectRatio != null) {
- out.writeInt(1);
- out.writeInt(mAspectRatio.getNumerator());
- out.writeInt(mAspectRatio.getDenominator());
- } else {
- out.writeInt(0);
- }
+ writeRationalToParcel(mAspectRatio, out);
+ writeRationalToParcel(mExpandedAspectRatio, out);
if (mUserActions != null) {
out.writeInt(1);
out.writeTypedList(mUserActions, 0);
@@ -410,10 +462,28 @@
}
}
+ private void writeRationalToParcel(Rational rational, Parcel out) {
+ if (rational != null) {
+ out.writeInt(1);
+ out.writeInt(rational.getNumerator());
+ out.writeInt(rational.getDenominator());
+ } else {
+ out.writeInt(0);
+ }
+ }
+
+ private Rational readRationalFromParcel(Parcel in) {
+ if (in.readInt() != 0) {
+ return new Rational(in.readInt(), in.readInt());
+ }
+ return null;
+ }
+
@Override
public String toString() {
return "PictureInPictureParams("
+ " aspectRatio=" + getAspectRatioRational()
+ + " expandedAspectRatio=" + mExpandedAspectRatio
+ " sourceRectHint=" + getSourceRectHint()
+ " hasSetActions=" + hasSetActions()
+ " isAutoPipEnabled=" + isAutoEnterEnabled()
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index 433b275..7014d69 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -1168,6 +1168,23 @@
}
}
+ /**
+ * @return the package name of the service providing the VoiceInteractionService.
+ */
+ @NonNull
+ public String getPackageName() {
+ String packageName = null;
+ if (mActivity != null && mInteractor != null) {
+ try {
+ packageName = ActivityTaskManager.getService()
+ .getVoiceInteractorPackageName(mInteractor.asBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return packageName == null ? "" : packageName;
+ }
+
void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
String innerPrefix = prefix + " ";
if (mActiveRequests.size() > 0) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6614be7..9a7093e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8307,10 +8307,10 @@
* @hide
*/
@RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
- public void reportPasswordChanged(@UserIdInt int userId) {
+ public void reportPasswordChanged(PasswordMetrics metrics, @UserIdInt int userId) {
if (mService != null) {
try {
- mService.reportPasswordChanged(userId);
+ mService.reportPasswordChanged(metrics, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index eedc042..0b9d51f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -158,7 +158,7 @@
void forceRemoveActiveAdmin(in ComponentName policyReceiver, int userHandle);
boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy, int userHandle);
- void reportPasswordChanged(int userId);
+ void reportPasswordChanged(in PasswordMetrics metrics, int userId);
void reportFailedPasswordAttempt(int userHandle);
void reportSuccessfulPasswordAttempt(int userHandle);
void reportFailedBiometricAttempt(int userHandle);
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 8c59982..b170aa2 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -16,8 +16,12 @@
package android.app.admin;
+import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -86,7 +90,12 @@
TAG_KEY_INTEGRITY_VIOLATION,
TAG_CERT_VALIDATION_FAILURE,
TAG_CAMERA_POLICY_SET,
- TAG_PASSWORD_COMPLEXITY_REQUIRED
+ TAG_PASSWORD_COMPLEXITY_REQUIRED,
+ TAG_PASSWORD_CHANGED,
+ TAG_WIFI_CONNECTION,
+ TAG_WIFI_DISCONNECTION,
+ TAG_BLUETOOTH_CONNECTION,
+ TAG_BLUETOOTH_DISCONNECTION,
})
public @interface SecurityLogTag {}
@@ -495,6 +504,65 @@
SecurityLogTags.SECURITY_PASSWORD_COMPLEXITY_REQUIRED;
/**
+ * Indicates that a user has just changed their lockscreen password.
+ * The log entry contains the following information about the
+ * event, encapsulated in an {@link Object} array and accessible via
+ * {@link SecurityEvent#getData()}:
+ * <li> [0] complexity for the new password ({@code Integer})
+ * <li> [1] target user ID ({@code Integer})
+ *
+ * <p>Password complexity levels are defined as in
+ * {@link DevicePolicyManager#getPasswordComplexity()}
+ */
+ public static final int TAG_PASSWORD_CHANGED = SecurityLogTags.SECURITY_PASSWORD_CHANGED;
+
+ /**
+ * Indicates that the device attempts to connect to a WiFi network.
+ * The log entry contains the following information about the
+ * event, encapsulated in an {@link Object} array and accessible via
+ * {@link SecurityEvent#getData()}:
+ * <li> [0] The SSID of the network ({@code String})
+ * <li> [1] The BSSID of the network ({@code String})
+ * <li> [2] Whether the connection is successful ({@code Integer}, 1 if successful, 0 otherwise)
+ * <li> [3] Optional human-readable failure reason, empty string if none ({@code String})
+ */
+ public static final int TAG_WIFI_CONNECTION = SecurityLogTags.SECURITY_WIFI_CONNECTION;
+
+ /**
+ * Indicates that the device disconnects from a connected WiFi network.
+ * The log entry contains the following information about the
+ * event, encapsulated in an {@link Object} array and accessible via
+ * {@link SecurityEvent#getData()}:
+ * <li> [0] The SSID of the connected network ({@code String})
+ * <li> [1] The BSSID of the connected network ({@code String})
+ * <li> [2] Optional human-readable disconnection reason, empty string if none ({@code String})
+ */
+ public static final int TAG_WIFI_DISCONNECTION = SecurityLogTags.SECURITY_WIFI_DISCONNECTION;
+
+ /**
+ * Indicates that the device attempts to connect to a Bluetooth device.
+ * The log entry contains the following information about the
+ * event, encapsulated in an {@link Object} array and accessible via
+ * {@link SecurityEvent#getData()}:
+ * <li> [0] The MAC address of the Bluetooth device ({@code String})
+ * <li> [1] Whether the connection is successful ({@code Integer}, 1 if successful, 0 otherwise)
+ * <li> [2] Optional human-readable failure reason, empty string if none ({@code String})
+ */
+ public static final int TAG_BLUETOOTH_CONNECTION =
+ SecurityLogTags.SECURITY_BLUETOOTH_CONNECTION;
+
+ /**
+ * Indicates that the device disconnects from a connected Bluetooth device.
+ * The log entry contains the following information about the
+ * event, encapsulated in an {@link Object} array and accessible via
+ * {@link SecurityEvent#getData()}:
+ * <li> [0] The MAC address of the connected Bluetooth device ({@code String})
+ * <li> [1] Optional human-readable disconnection reason, empty string if none ({@code String})
+ */
+ public static final int TAG_BLUETOOTH_DISCONNECTION =
+ SecurityLogTags.SECURITY_BLUETOOTH_DISCONNECTION;
+
+ /**
* Event severity level indicating that the event corresponds to normal workflow.
*/
public static final int LEVEL_INFO = 1;
@@ -635,6 +703,7 @@
case TAG_USER_RESTRICTION_REMOVED:
case TAG_CAMERA_POLICY_SET:
case TAG_PASSWORD_COMPLEXITY_REQUIRED:
+ case TAG_PASSWORD_CHANGED:
return LEVEL_INFO;
case TAG_CERT_AUTHORITY_REMOVED:
case TAG_CRYPTO_SELF_TEST_COMPLETED:
@@ -725,6 +794,13 @@
return null;
}
break;
+ case SecurityLog.TAG_PASSWORD_CHANGED:
+ try {
+ userId = getIntegerData(1);
+ } catch (Exception e) {
+ return null;
+ }
+ break;
default:
userId = UserHandle.USER_NULL;
}
@@ -840,15 +916,21 @@
throws IOException;
/**
- * Write a log entry to the underlying storage, with a string payload.
- * @hide
- */
- public static native int writeEvent(int tag, String str);
-
- /**
* Write a log entry to the underlying storage, with several payloads.
* Supported types of payload are: integer, long, float, string plus array of supported types.
+ *
+ * <p>Security log is part of Android's device management capability that tracks
+ * security-sensitive events for auditing purposes.
+ *
+ * @param tag the tag ID of the security event
+ * @param payloads a list of payload values. Each tag dictates the expected payload types
+ * and their meanings
+ * @see DevicePolicyManager#setSecurityLoggingEnabled(ComponentName, boolean)
+ *
* @hide
*/
- public static native int writeEvent(int tag, Object... payloads);
+ // TODO(b/218658622): enforce WRITE_SECURITY_LOG in logd.
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_SECURITY_LOG)
+ public static native int writeEvent(@SecurityLogTag int tag, @NonNull Object... payloads);
}
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index db5245c..5f41109 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -40,3 +40,8 @@
210033 security_cert_validation_failure (reason|3)
210034 security_camera_policy_set (package|3),(admin_user|1),(target_user|1),(disabled|1)
210035 security_password_complexity_required (package|3),(admin_user|1),(target_user|1),(complexity|1)
+210036 security_password_changed (password_complexity|1),(target_user|1)
+210037 security_wifi_connection (ssid|3),(bssid|3),(success|1),(reason|3)
+210038 security_wifi_disconnection (ssid|3),(bssid|3),(reason|3)
+210039 security_bluetooth_connection (addr|3),(success|1),(reason|3)
+210040 security_bluetooth_disconnection (addr|3),(reason|3)
\ No newline at end of file
diff --git a/core/java/android/app/cloudsearch/SearchRequest.java b/core/java/android/app/cloudsearch/SearchRequest.java
index 0c5c30c..ef66c0c 100644
--- a/core/java/android/app/cloudsearch/SearchRequest.java
+++ b/core/java/android/app/cloudsearch/SearchRequest.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -91,6 +92,14 @@
@NonNull
private Bundle mSearchConstraints;
+ /** Auto set by system servier, and the caller cannot set it.
+ *
+ * The caller's package name.
+ *
+ */
+ @NonNull
+ private String mSource;
+
private SearchRequest(Parcel in) {
this.mQuery = in.readString();
this.mResultOffset = in.readInt();
@@ -98,15 +107,17 @@
this.mMaxLatencyMillis = in.readFloat();
this.mSearchConstraints = in.readBundle();
this.mId = in.readString();
+ this.mSource = in.readString();
}
private SearchRequest(String query, int resultOffset, int resultNumber, float maxLatencyMillis,
- Bundle searchConstraints) {
+ Bundle searchConstraints, String source) {
mQuery = query;
mResultOffset = resultOffset;
mResultNumber = resultNumber;
mMaxLatencyMillis = maxLatencyMillis;
mSearchConstraints = searchConstraints;
+ mSource = source;
}
/** Returns the original query. */
@@ -136,35 +147,37 @@
return mSearchConstraints;
}
+ /** Gets the caller's package name. */
+ @NonNull
+ public String getSource() {
+ return mSource;
+ }
+
/** Returns the search request id, which is used to identify the request. */
@NonNull
public String getRequestId() {
if (mId == null || mId.length() == 0) {
- boolean isPresubmit =
- mSearchConstraints.containsKey(CONSTRAINT_IS_PRESUBMIT_SUGGESTION)
- && mSearchConstraints.getBoolean(CONSTRAINT_IS_PRESUBMIT_SUGGESTION);
-
- String searchProvider = "EMPTY";
- if (mSearchConstraints.containsKey(CONSTRAINT_SEARCH_PROVIDER_FILTER)) {
- searchProvider = mSearchConstraints.getString(CONSTRAINT_SEARCH_PROVIDER_FILTER);
- }
-
- String rawContent = String.format("%s\t%d\t%d\t%f\t%b\t%s",
- mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis,
- isPresubmit, searchProvider);
-
- mId = String.valueOf(rawContent.hashCode());
+ mId = String.valueOf(toString().hashCode());
}
return mId;
}
+ /** Sets the caller, and this will be set by the system server.
+ *
+ * @hide
+ */
+ public void setSource(@NonNull String source) {
+ this.mSource = source;
+ }
+
private SearchRequest(Builder b) {
mQuery = requireNonNull(b.mQuery);
mResultOffset = b.mResultOffset;
mResultNumber = b.mResultNumber;
mMaxLatencyMillis = b.mMaxLatencyMillis;
mSearchConstraints = requireNonNull(b.mSearchConstraints);
+ mSource = requireNonNull(b.mSource);
}
/**
@@ -192,6 +205,7 @@
dest.writeFloat(this.mMaxLatencyMillis);
dest.writeBundle(this.mSearchConstraints);
dest.writeString(getRequestId());
+ dest.writeString(this.mSource);
}
@Override
@@ -214,13 +228,30 @@
&& mResultOffset == that.mResultOffset
&& mResultNumber == that.mResultNumber
&& mMaxLatencyMillis == that.mMaxLatencyMillis
- && Objects.equals(mSearchConstraints, that.mSearchConstraints);
+ && Objects.equals(mSearchConstraints, that.mSearchConstraints)
+ && Objects.equals(mSource, that.mSource);
+ }
+
+ @Override
+ public String toString() {
+ boolean isPresubmit =
+ mSearchConstraints.containsKey(CONSTRAINT_IS_PRESUBMIT_SUGGESTION)
+ && mSearchConstraints.getBoolean(CONSTRAINT_IS_PRESUBMIT_SUGGESTION);
+
+ String searchProvider = "EMPTY";
+ if (mSearchConstraints.containsKey(CONSTRAINT_SEARCH_PROVIDER_FILTER)) {
+ searchProvider = mSearchConstraints.getString(CONSTRAINT_SEARCH_PROVIDER_FILTER);
+ }
+
+ return String.format("SearchRequest: {query:%s,offset:%d;number:%d;max_latency:%f;"
+ + "is_presubmit:%b;search_provider:%s;source:%s}", mQuery, mResultOffset,
+ mResultNumber, mMaxLatencyMillis, isPresubmit, searchProvider, mSource);
}
@Override
public int hashCode() {
return Objects.hash(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis,
- mSearchConstraints);
+ mSearchConstraints, mSource);
}
/**
@@ -235,6 +266,7 @@
private int mResultNumber;
private float mMaxLatencyMillis;
private Bundle mSearchConstraints;
+ private String mSource;
/**
*
@@ -250,6 +282,7 @@
mResultNumber = 10;
mMaxLatencyMillis = 200;
mSearchConstraints = Bundle.EMPTY;
+ mSource = "DEFAULT_CALLER";
}
/** Sets the input query. */
@@ -288,6 +321,17 @@
return this;
}
+ /** Sets the caller, and this will be set by the system server.
+ *
+ * @hide
+ */
+ @NonNull
+ @TestApi
+ public Builder setSource(@NonNull String source) {
+ this.mSource = source;
+ return this;
+ }
+
/** Builds a SearchRequest based-on the given params. */
@NonNull
public SearchRequest build() {
@@ -297,7 +341,7 @@
}
return new SearchRequest(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis,
- mSearchConstraints);
+ mSearchConstraints, mSource);
}
}
}
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index edabccf..b786444 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -33,8 +33,8 @@
void unregisterTrustListener(in ITrustListener trustListener);
void reportKeyguardShowingChanged();
void setDeviceLockedForUser(int userId, boolean locked);
- boolean isDeviceLocked(int userId);
- boolean isDeviceSecure(int userId);
+ boolean isDeviceLocked(int userId, int displayId);
+ boolean isDeviceSecure(int userId, int displayId);
boolean isTrustUsuallyManaged(int userId);
void unlockedByBiometricForUser(int userId, in BiometricSourceType source);
void clearAllBiometricRecognized(in BiometricSourceType target, int unlockedUser);
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 0f5cd4e..3c256ad 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -1037,6 +1037,7 @@
// Data can be too large for a transact. Write the data as a Blob, which will be written to
// ashmem if too large.
dest.writeBlob(data.marshall());
+ data.recycle();
}
public static final @android.annotation.NonNull Creator<UsageEvents> CREATOR = new Creator<UsageEvents>() {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index fdff27f..f1abb05 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -16,10 +16,11 @@
package android.companion.virtual;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.Activity;
@@ -31,6 +32,7 @@
import android.content.Context;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.VirtualDisplayFlag;
import android.hardware.display.VirtualDisplay;
import android.hardware.display.VirtualDisplayConfig;
import android.hardware.input.VirtualKeyboard;
@@ -46,6 +48,7 @@
import android.util.ArrayMap;
import android.view.Surface;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -210,25 +213,22 @@
* {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC VIRTUAL_DISPLAY_FLAG_PUBLIC} and
* {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
* VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
+ * @param executor The executor on which {@code callback} will be invoked. This is ignored
+ * if {@code callback} is {@code null}.
* @param callback Callback to call when the state of the {@link VirtualDisplay} changes
- * @param handler The handler on which the listener should be invoked, or null
- * if the listener should be invoked on the calling thread's looper.
* @return The newly created virtual display, or {@code null} if the application could
* not create the virtual display.
*
* @see DisplayManager#createVirtualDisplay
*/
- // Suppress "ExecutorRegistration" because DisplayManager.createVirtualDisplay takes a
- // handler
- @SuppressLint("ExecutorRegistration")
@Nullable
public VirtualDisplay createVirtualDisplay(
- int width,
- int height,
- int densityDpi,
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @IntRange(from = 1) int densityDpi,
@Nullable Surface surface,
- int flags,
- @Nullable Handler handler,
+ @VirtualDisplayFlag int flags,
+ @NonNull @CallbackExecutor Executor executor,
@Nullable VirtualDisplay.Callback callback) {
// TODO(b/205343547): Handle display groups properly instead of creating a new display
// group for every new virtual display created using this API.
@@ -244,7 +244,7 @@
.setFlags(getVirtualDisplayFlags(flags))
.build(),
callback,
- handler);
+ Objects.requireNonNull(executor));
}
/**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 957cb24c..0fed733 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -7075,6 +7075,18 @@
public abstract int getDisplayId();
/**
+ * @return Returns the id of the Display object associated with this Context or
+ * {@link Display#INVALID_DISPLAY} if no Display has been associated.
+ * @see #getDisplay()
+ * @see #getDisplayId()
+ *
+ * @hide
+ */
+ public int getAssociatedDisplayId() {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* @hide
*/
@SuppressWarnings("HiddenAbstractMethod")
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4279d07..dc79ee6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3797,6 +3797,16 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports expanded picture-in-picture multi-window mode.
+ *
+ * @see android.app.PictureInPictureParams.Builder#setExpandedAspectRatio
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_EXPANDED_PICTURE_IN_PICTURE
+ = "android.software.expanded_picture_in_picture";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
* The device supports running activities on secondary displays.
*/
@SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 8e8aaf1..70b90e6 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -767,7 +767,7 @@
* order to make sure shortcuts exist and are up-to-date, without the need to explicitly handle
* the shortcut count limit.
* @see android.app.NotificationManager#notify(int, Notification)
- * @see Notification.Builder#setShortcutId(String)
+ * @see android.app.Notification.Builder#setShortcutId(String)
*
* <p>If {@link #getMaxShortcutCountPerActivity()} is already reached, an existing shortcut with
* the lowest rank will be removed to add space for the new shortcut.
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index a06566c..524fe79 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -3455,6 +3455,30 @@
new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryTenBitOutputStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
/**
+ * <p>An array of mandatory stream combinations which are applicable when device lists
+ * {@code PREVIEW_STABILIZATION} in {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES android.control.availableVideoStabilizationModes}.
+ * This is an app-readable conversion of the maximum resolution mandatory stream combination
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p>
+ * <p>The array of
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
+ * generated according to the documented
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each
+ * device which supports {@code PREVIEW_STABILIZATION}
+ * Clients can use the array as a quick reference to find an appropriate camera stream
+ * combination.
+ * The mandatory stream combination array will be {@code null} in case the device does not
+ * list {@code PREVIEW_STABILIZATION} in {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES android.control.availableVideoStabilizationModes}.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES
+ */
+ @PublicKey
+ @NonNull
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_PREVIEW_STABILIZATION_OUTPUT_STREAM_COMBINATIONS =
+ new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryPreviewStabilizationOutputStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
+
+ /**
* <p>Whether the camera device supports multi-resolution input or output streams</p>
* <p>A logical multi-camera or an ultra high resolution camera may support multi-resolution
* input or output streams. With multi-resolution output streams, the camera device is able
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 1a42eaf..8f42b1f 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -883,6 +883,27 @@
* </table><br>
* </p>
*
+ *<p> For devices where {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES}
+ * includes {@link CameraMetadata#CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION},
+ * the following stream combinations are guaranteed,
+ * for CaptureRequests where {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE} is set to
+ * {@link CameraMetadata#CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION} <p>
+ * <table>
+ * <tr><th colspan="7">Preview stabilization guaranteed stream configurations</th></tr>
+ * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+ * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code RECORD}</td><td colspan="4" id="rb"></td> <td>Stabilized preview, GPU video processing, or no-preview stabilized video recording.</td> </tr>
+ * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG / YUV}</td><td id="rb">{@code MAXIMUM }</td><td>Standard still imaging with stabilized preview.</td> </tr>
+ * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV / YUV}</td><td id="rb">{@code RECORD }</td><td>High-resolution recording with stabilized preview and recording stream.</td> </tr>
+ * </table><br>
+ * <p>
+ * For the maximum size column, PREVIEW refers to the best size match to the device's screen
+ * resolution, or to 1080p (1920x1080), whichever is smaller. RECORD refers to the camera
+ * device's maximum supported recording resolution, as determined by
+ * {@link android.media.CamcorderProfile}. MAXIMUM refers to the camera device's maximum output
+ * resolution for that format or target from {@link StreamConfigurationMap#getOutputSizes(int)}.
+ * </p>
+ *
* <p>Since the capabilities of camera devices vary greatly, a given camera device may support
* target combinations with sizes outside of these guarantees, but this can only be tested for
* by calling {@link #isSessionConfigurationSupported} or attempting to create a session with
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 9b67633..4fb496d 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -334,6 +334,7 @@
private static final int MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT = 2;
private static final int MANDATORY_STREAM_CONFIGURATIONS_10BIT = 3;
private static final int MANDATORY_STREAM_CONFIGURATIONS_USE_CASE = 4;
+ private static final int MANDATORY_STREAM_CONFIGURATIONS_PREVIEW_STABILIZATION = 5;
private static String translateLocationProviderToProcess(final String provider) {
if (provider == null) {
@@ -709,6 +710,15 @@
return (T) metadata.getMandatoryUseCaseStreamCombinations();
}
});
+ sGetCommandMap.put(
+ CameraCharacteristics.SCALER_MANDATORY_PREVIEW_STABILIZATION_OUTPUT_STREAM_COMBINATIONS.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getMandatoryPreviewStabilizationStreamCombinations();
+ }
+ });
sGetCommandMap.put(
CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
@@ -1400,6 +1410,24 @@
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
}
+ private boolean isPreviewStabilizationSupported() {
+ boolean ret = false;
+
+ int[] videoStabilizationModes =
+ getBase(CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES);
+ if (videoStabilizationModes == null) {
+ return false;
+ }
+ for (int mode : videoStabilizationModes) {
+ if (mode == CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION) {
+ ret = true;
+ break;
+ }
+ }
+
+ return ret;
+ }
+
private MandatoryStreamCombination[] getMandatoryStreamCombinationsHelper(
int mandatoryStreamsType) {
int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
@@ -1411,7 +1439,7 @@
int hwLevel = getBase(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
MandatoryStreamCombination.Builder build = new MandatoryStreamCombination.Builder(
mCameraId, hwLevel, mDisplaySize, caps, getStreamConfigurationMap(),
- getStreamConfigurationMapMaximumResolution());
+ getStreamConfigurationMapMaximumResolution(), isPreviewStabilizationSupported());
List<MandatoryStreamCombination> combs = null;
switch (mandatoryStreamsType) {
@@ -1427,6 +1455,9 @@
case MANDATORY_STREAM_CONFIGURATIONS_USE_CASE:
combs = build.getAvailableMandatoryStreamUseCaseCombinations();
break;
+ case MANDATORY_STREAM_CONFIGURATIONS_PREVIEW_STABILIZATION:
+ combs = build.getAvailableMandatoryPreviewStabilizedStreamCombinations();
+ break;
default:
combs = build.getAvailableMandatoryStreamCombinations();
}
@@ -1464,6 +1495,11 @@
return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_USE_CASE);
}
+ private MandatoryStreamCombination[] getMandatoryPreviewStabilizationStreamCombinations() {
+ return getMandatoryStreamCombinationsHelper(
+ MANDATORY_STREAM_CONFIGURATIONS_PREVIEW_STABILIZATION);
+ }
+
private StreamConfigurationMap getStreamConfigurationMap() {
StreamConfiguration[] configurations = getBase(
CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 0d93c98..8c0dcfc 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -1262,6 +1262,50 @@
"Preview, in-application image processing, and YUV still image capture"),
};
+ private static StreamCombinationTemplate sPreviewStabilizedStreamCombinations[] = {
+ // 1 stream combinations
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD)},
+ "Stabilized preview, GPU video processing, or no-preview stabilized recording"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD)},
+ "Stabilized preview, GPU video processing, or no-preview stabilized recording"),
+ //2 stream combinations
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+ "Standard JPEG still imaging with stabilized preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+ "Standard YUV still imaging with stabilized preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
+ "Standard YUV still imaging with stabilized in-app image processing stream"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
+ "Standard JPEG still imaging with stabilized in-app image processing stream"),
+
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+ "High-resolution video recording with preview both streams stabilized"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
+ "High-resolution video recording with preview both streams stabilized"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW)},
+ "High-resolution video recording with preview both streams stabilized"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+ "High-resolution video recording with preview both streams stabilized"),
+ };
+
/**
* Helper builder class to generate a list of available mandatory stream combinations.
* @hide
@@ -1273,6 +1317,7 @@
private StreamConfigurationMap mStreamConfigMap;
private StreamConfigurationMap mStreamConfigMapMaximumResolution;
private boolean mIsHiddenPhysicalCamera;
+ private boolean mIsPreviewStabilizationSupported;
private final Size kPreviewSizeBound = new Size(1920, 1088);
@@ -1289,7 +1334,7 @@
*/
public Builder(int cameraId, int hwLevel, @NonNull Size displaySize,
@NonNull List<Integer> capabilities, @NonNull StreamConfigurationMap sm,
- StreamConfigurationMap smMaxResolution) {
+ StreamConfigurationMap smMaxResolution, boolean previewStabilization) {
mCameraId = cameraId;
mDisplaySize = displaySize;
mCapabilities = capabilities;
@@ -1298,24 +1343,12 @@
mHwLevel = hwLevel;
mIsHiddenPhysicalCamera =
CameraManager.isHiddenPhysicalCamera(Integer.toString(mCameraId));
+ mIsPreviewStabilizationSupported = previewStabilization;
}
- /**
- * Retrieve a list of all available mandatory 10-bit output capable stream combinations.
- *
- * @return a non-modifiable list of supported mandatory 10-bit capable stream combinations,
- * null in case device is not 10-bit output capable.
- */
- public @NonNull List<MandatoryStreamCombination>
- getAvailableMandatory10BitStreamCombinations() {
- // Since 10-bit streaming support is optional, we mandate these stream
- // combinations regardless of camera device capabilities.
-
- StreamCombinationTemplate []chosenStreamCombinations = s10BitOutputStreamCombinations;
- if (!is10BitOutputSupported()) {
- Log.v(TAG, "Device is not able to output 10-bit!");
- return null;
- }
+ private @Nullable List<MandatoryStreamCombination>
+ getAvailableMandatoryStreamCombinationsInternal(
+ StreamCombinationTemplate []chosenStreamCombinations, boolean s10Bit) {
HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
enumerateAvailableSizes();
@@ -1334,7 +1367,7 @@
Pair<SizeThreshold, Integer> pair;
pair = new Pair<>(template.mSizeThreshold, new Integer(template.mFormat));
sizes = availableSizes.get(pair);
- if (template.mFormat == ImageFormat.YCBCR_P010) {
+ if (s10Bit && template.mFormat == ImageFormat.YCBCR_P010) {
// Make sure that exactly the same 10 and 8-bit YUV streams sizes are
// supported
pair = new Pair<>(template.mSizeThreshold,
@@ -1354,7 +1387,8 @@
streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
isMaximumSize, /*isInput*/ false,
/*isUltraHighResolution*/ false,
- /*is10BitCapable*/ template.mFormat != ImageFormat.JPEG);
+ /*is10BitCapable*/ s10Bit ? template.mFormat != ImageFormat.JPEG :
+ false);
} catch (IllegalArgumentException e) {
Log.e(TAG, "No available sizes found for format: " + template.mFormat +
" size threshold: " + template.mSizeThreshold + " combination: " +
@@ -1381,6 +1415,52 @@
}
/**
+ * Retrieve a list of all available mandatory stream combinations for devices supporting
+ * preview stabilization.
+ *
+ * @return a non-modifiable list of supported mandatory stream combinations on which
+ * preview stabilization is supported.,
+ * null in case device is not 10-bit output capable.
+ */
+ public @Nullable List<MandatoryStreamCombination>
+ getAvailableMandatoryPreviewStabilizedStreamCombinations() {
+ // Since preview stabilization support is optional, we mandate these stream
+ // combinations regardless of camera device capabilities.
+
+ StreamCombinationTemplate []chosenStreamCombinations =
+ sPreviewStabilizedStreamCombinations;
+
+ if (mIsPreviewStabilizationSupported) {
+ Log.v(TAG, "Device does not support preview stabilization");
+ return null;
+ }
+
+ return getAvailableMandatoryStreamCombinationsInternal(chosenStreamCombinations,
+ /*10bit*/false);
+ }
+
+
+ /**
+ * Retrieve a list of all available mandatory 10-bit output capable stream combinations.
+ *
+ * @return a non-modifiable list of supported mandatory 10-bit capable stream combinations,
+ * null in case device is not 10-bit output capable.
+ */
+ public @Nullable List<MandatoryStreamCombination>
+ getAvailableMandatory10BitStreamCombinations() {
+ // Since 10-bit streaming support is optional, we mandate these stream
+ // combinations regardless of camera device capabilities.
+
+ StreamCombinationTemplate []chosenStreamCombinations = s10BitOutputStreamCombinations;
+ if (!is10BitOutputSupported()) {
+ Log.v(TAG, "Device is not able to output 10-bit!");
+ return null;
+ }
+ return getAvailableMandatoryStreamCombinationsInternal(chosenStreamCombinations,
+ /*10bit*/true);
+ }
+
+ /**
* Retrieve a list of all available mandatory stream combinations with stream use cases.
* when the camera device has {@link
* CameraMetdata.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE} capability.
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 623f38e..971b61b 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -22,6 +22,7 @@
import android.Manifest;
import android.annotation.FloatRange;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -38,6 +39,8 @@
import android.media.projection.MediaProjection;
import android.os.Build;
import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -48,6 +51,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
/**
@@ -104,6 +108,25 @@
public static final String DISPLAY_CATEGORY_PRESENTATION =
"android.hardware.display.category.PRESENTATION";
+ /** @hide **/
+ @IntDef(prefix = "VIRTUAL_DISPLAY_FLAG_", flag = true, value = {
+ VIRTUAL_DISPLAY_FLAG_PUBLIC,
+ VIRTUAL_DISPLAY_FLAG_PRESENTATION,
+ VIRTUAL_DISPLAY_FLAG_SECURE,
+ VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY,
+ VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD,
+ VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH,
+ VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT,
+ VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL,
+ VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS,
+ VIRTUAL_DISPLAY_FLAG_TRUSTED,
+ VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP,
+ VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VirtualDisplayFlag {}
+
/**
* Virtual display flag: Create a public display.
*
@@ -820,7 +843,11 @@
* VirtualDisplay.Callback, Handler)
*/
public VirtualDisplay createVirtualDisplay(@NonNull String name,
- int width, int height, int densityDpi, @Nullable Surface surface, int flags) {
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @IntRange(from = 1) int densityDpi,
+ @Nullable Surface surface,
+ @VirtualDisplayFlag int flags) {
return createVirtualDisplay(name, width, height, densityDpi, surface, flags, null, null);
}
@@ -868,8 +895,13 @@
* a virtual display with the specified flags.
*/
public VirtualDisplay createVirtualDisplay(@NonNull String name,
- int width, int height, int densityDpi, @Nullable Surface surface, int flags,
- @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @IntRange(from = 1) int densityDpi,
+ @Nullable Surface surface,
+ @VirtualDisplayFlag int flags,
+ @Nullable VirtualDisplay.Callback callback,
+ @Nullable Handler handler) {
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
height, densityDpi);
builder.setFlags(flags);
@@ -882,9 +914,16 @@
// TODO : Remove this hidden API after remove all callers. (Refer to MultiDisplayService)
/** @hide */
- public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection,
- @NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface,
- int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler,
+ public VirtualDisplay createVirtualDisplay(
+ @Nullable MediaProjection projection,
+ @NonNull String name,
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @IntRange(from = 1) int densityDpi,
+ @Nullable Surface surface,
+ @VirtualDisplayFlag int flags,
+ @Nullable VirtualDisplay.Callback callback,
+ @Nullable Handler handler,
@Nullable String uniqueId) {
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
height, densityDpi);
@@ -904,16 +943,24 @@
@NonNull VirtualDisplayConfig virtualDisplayConfig,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler,
@Nullable Context windowContext) {
+ Executor executor = null;
+ // If callback is null, the executor will not be used. Avoid creating the handler and the
+ // handler executor.
+ if (callback != null) {
+ executor = new HandlerExecutor(
+ Handler.createAsync(handler != null ? handler.getLooper() : Looper.myLooper()));
+ }
return mGlobal.createVirtualDisplay(mContext, projection, null /* virtualDevice */,
- virtualDisplayConfig, callback, handler, windowContext);
+ virtualDisplayConfig, callback, executor, windowContext);
}
/** @hide */
public VirtualDisplay createVirtualDisplay(@Nullable IVirtualDevice virtualDevice,
@NonNull VirtualDisplayConfig virtualDisplayConfig,
- @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
+ @Nullable VirtualDisplay.Callback callback,
+ @NonNull Executor executor) {
return mGlobal.createVirtualDisplay(mContext, null /* projection */, virtualDevice,
- virtualDisplayConfig, callback, handler, null);
+ virtualDisplayConfig, callback, executor, null);
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 7e7a648..889100d 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -56,6 +56,8 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* Manager communication with the display manager service on behalf of
@@ -585,8 +587,9 @@
public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection,
IVirtualDevice virtualDevice, @NonNull VirtualDisplayConfig virtualDisplayConfig,
- VirtualDisplay.Callback callback, Handler handler, @Nullable Context windowContext) {
- VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
+ VirtualDisplay.Callback callback, @Nullable Executor executor,
+ @Nullable Context windowContext) {
+ VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, executor);
IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
int displayId;
try {
@@ -1048,61 +1051,36 @@
}
private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
- private VirtualDisplayCallbackDelegate mDelegate;
+ @Nullable private final VirtualDisplay.Callback mCallback;
+ @Nullable private final Executor mExecutor;
- public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) {
- if (callback != null) {
- mDelegate = new VirtualDisplayCallbackDelegate(callback, handler);
- }
+ VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor) {
+ mCallback = callback;
+ mExecutor = mCallback != null ? Objects.requireNonNull(executor) : null;
}
+ // These methods are called from the binder thread, but the AIDL is oneway, so it should be
+ // safe to call the callback on arbitrary executors directly without risking blocking
+ // the system.
+
@Override // Binder call
public void onPaused() {
- if (mDelegate != null) {
- mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED);
+ if (mCallback != null) {
+ mExecutor.execute(mCallback::onPaused);
}
}
@Override // Binder call
public void onResumed() {
- if (mDelegate != null) {
- mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED);
+ if (mCallback != null) {
+ mExecutor.execute(mCallback::onResumed);
}
}
@Override // Binder call
public void onStopped() {
- if (mDelegate != null) {
- mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED);
- }
- }
- }
-
- private final static class VirtualDisplayCallbackDelegate extends Handler {
- public static final int MSG_DISPLAY_PAUSED = 0;
- public static final int MSG_DISPLAY_RESUMED = 1;
- public static final int MSG_DISPLAY_STOPPED = 2;
-
- private final VirtualDisplay.Callback mCallback;
-
- public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback,
- Handler handler) {
- super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
- mCallback = callback;
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_DISPLAY_PAUSED:
- mCallback.onPaused();
- break;
- case MSG_DISPLAY_RESUMED:
- mCallback.onResumed();
- break;
- case MSG_DISPLAY_STOPPED:
- mCallback.onStopped();
- break;
+ if (mCallback != null) {
+ mExecutor.execute(mCallback::onStopped);
}
}
}
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 0e86f43..e292394 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -21,6 +21,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.display.DisplayManager.VirtualDisplayFlag;
import android.media.projection.MediaProjection;
import android.os.Handler;
import android.os.IBinder;
@@ -70,6 +71,7 @@
* {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
* or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
*/
+ @VirtualDisplayFlag
private int mFlags = 0;
/**
@@ -120,7 +122,7 @@
@IntRange(from = 1) int width,
@IntRange(from = 1) int height,
@IntRange(from = 1) int densityDpi,
- int flags,
+ @VirtualDisplayFlag int flags,
@Nullable Surface surface,
@Nullable String uniqueId,
int displayIdToMirror,
@@ -141,6 +143,8 @@
IntRange.class, null, mDensityDpi,
"from", 1);
this.mFlags = flags;
+ com.android.internal.util.AnnotationValidations.validate(
+ VirtualDisplayFlag.class, null, mFlags);
this.mSurface = surface;
this.mUniqueId = uniqueId;
this.mDisplayIdToMirror = displayIdToMirror;
@@ -190,7 +194,7 @@
* or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
*/
@DataClass.Generated.Member
- public int getFlags() {
+ public @VirtualDisplayFlag int getFlags() {
return mFlags;
}
@@ -291,6 +295,8 @@
IntRange.class, null, mDensityDpi,
"from", 1);
this.mFlags = flags;
+ com.android.internal.util.AnnotationValidations.validate(
+ VirtualDisplayFlag.class, null, mFlags);
this.mSurface = surface;
this.mUniqueId = uniqueId;
this.mDisplayIdToMirror = displayIdToMirror;
@@ -324,7 +330,7 @@
private @IntRange(from = 1) int mWidth;
private @IntRange(from = 1) int mHeight;
private @IntRange(from = 1) int mDensityDpi;
- private int mFlags;
+ private @VirtualDisplayFlag int mFlags;
private @Nullable Surface mSurface;
private @Nullable String mUniqueId;
private int mDisplayIdToMirror;
@@ -419,7 +425,7 @@
* or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
*/
@DataClass.Generated.Member
- public @NonNull Builder setFlags(int value) {
+ public @NonNull Builder setFlags(@VirtualDisplayFlag int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x10;
mFlags = value;
@@ -517,10 +523,10 @@
}
@DataClass.Generated(
- time = 1620657851981L,
+ time = 1643938791506L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate int mDisplayIdToMirror\nprivate @android.annotation.Nullable android.os.IBinder mWindowTokenClientToMirror\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
+ inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate @android.hardware.display.DisplayManager.VirtualDisplayFlag int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate int mDisplayIdToMirror\nprivate @android.annotation.Nullable android.os.IBinder mWindowTokenClientToMirror\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index ad3de25..244335d 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -31,7 +31,7 @@
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Set;
-import java.util.function.Supplier;
+import java.util.function.Function;
/**
* A mapping from String keys to values of various types. In most cases, you
@@ -252,11 +252,10 @@
if (size == 0) {
return null;
}
- Object o = getValueAt(0);
try {
- return (String) o;
- } catch (ClassCastException e) {
- typeWarning("getPairValue()", o, "String", e);
+ return getValueAt(0, String.class);
+ } catch (ClassCastException | BadParcelableException e) {
+ typeWarning("getPairValue()", /* value */ null, "String", e);
return null;
}
}
@@ -309,7 +308,7 @@
}
for (int i = 0, n = mMap.size(); i < n; i++) {
// Triggers deserialization of i-th item, if needed
- getValueAt(i);
+ getValueAt(i, /* clazz */ null);
}
}
}
@@ -324,8 +323,21 @@
* @hide
*/
final Object getValue(String key) {
+ return getValue(key, /* clazz */ null);
+ }
+
+ /**
+ * Returns the value for key {@code key} for expected return type {@param clazz} (or {@code
+ * null} for no type check).
+ *
+ * This call should always be made after {@link #unparcel()} or inside a lock after making sure
+ * {@code mMap} is not null.
+ *
+ * @hide
+ */
+ final <T> T getValue(String key, @Nullable Class<T> clazz) {
int i = mMap.indexOfKey(key);
- return (i >= 0) ? getValueAt(i) : null;
+ return (i >= 0) ? getValueAt(i, clazz) : null;
}
/**
@@ -336,11 +348,12 @@
*
* @hide
*/
- final Object getValueAt(int i) {
+ @SuppressWarnings("unchecked")
+ final <T> T getValueAt(int i, @Nullable Class<T> clazz) {
Object object = mMap.valueAt(i);
- if (object instanceof Supplier<?>) {
+ if (object instanceof Function<?, ?>) {
try {
- object = ((Supplier<?>) object).get();
+ object = ((Function<Class<?>, ?>) object).apply(clazz);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse item " + mMap.keyAt(i) + ", returning null.", e);
@@ -351,7 +364,7 @@
}
mMap.setValueAt(i, object);
}
- return object;
+ return (clazz != null) ? clazz.cast(object) : (T) object;
}
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
@@ -528,7 +541,7 @@
} else {
// Following semantic above of failing in case we get a serialized value vs a
// deserialized one, we'll compare the map. If a certain element hasn't been
- // deserialized yet, it's a Supplier (or more specifically a LazyValue, but let's
+ // deserialized yet, it's a function object (or more specifically a LazyValue, but let's
// pretend we don't know that here :P), we'll use that element's equality comparison as
// map naturally does. That will takes care of comparing the payload if needed (see
// Parcel.readLazyValue() for details).
@@ -982,15 +995,19 @@
}
// Log a message if the value was non-null but not of the expected type
- void typeWarning(String key, Object value, String className,
- Object defaultValue, ClassCastException e) {
+ void typeWarning(String key, @Nullable Object value, String className,
+ Object defaultValue, RuntimeException e) {
StringBuilder sb = new StringBuilder();
sb.append("Key ");
sb.append(key);
sb.append(" expected ");
sb.append(className);
- sb.append(" but value was a ");
- sb.append(value.getClass().getName());
+ if (value != null) {
+ sb.append(" but value was a ");
+ sb.append(value.getClass().getName());
+ } else {
+ sb.append(" but value was of a different type ");
+ }
sb.append(". The default value ");
sb.append(defaultValue);
sb.append(" was returned.");
@@ -998,8 +1015,7 @@
Log.w(TAG, "Attempt to cast generated internal exception:", e);
}
- void typeWarning(String key, Object value, String className,
- ClassCastException e) {
+ void typeWarning(String key, @Nullable Object value, String className, RuntimeException e) {
typeWarning(key, value, className, "<null>", e);
}
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index b2bbfd6..2b13f20 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -16,6 +16,9 @@
package android.os;
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ArrayMap;
@@ -914,6 +917,33 @@
/**
* Returns the value associated with the given key, or {@code null} if
+ * no mapping of the desired type exists for the given key or a {@code null}
+ * value is explicitly associated with the key.
+ *
+ * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+ * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+ * Otherwise, this method might throw an exception or return {@code null}.
+ *
+ * @param key a String, or {@code null}
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
+ * @return a Parcelable value, or {@code null}
+ *
+ * @hide
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable
+ public <T> T getParcelable(@Nullable String key, @NonNull Class<T> clazz) {
+ unparcel();
+ try {
+ return getValue(key, requireNonNull(clazz));
+ } catch (ClassCastException | BadParcelableException e) {
+ typeWarning(key, /* value */ null, "Parcelable", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or {@code null} if
* no mapping of the desired type exists for the given key or a null
* value is explicitly associated with the key.
*
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index fcce266..39ca596 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -107,9 +107,7 @@
void clearSeedAccountData(int userId);
boolean someUserHasSeedAccount(in String accountName, in String accountType);
boolean someUserHasAccount(in String accountName, in String accountType);
- boolean isProfile(int userId);
- boolean isManagedProfile(int userId);
- boolean isCloneProfile(int userId);
+ String getProfileType(int userId);
boolean isMediaSharedWithParent(int userId);
boolean isCredentialSharedWithParent(int userId);
boolean isDemoUser(int userId);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 9998e12..ae92353 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -4319,18 +4319,19 @@
}
/**
- * This will return a {@link Supplier} for length-prefixed types that deserializes the object
- * when {@link Supplier#get()} is called, for other types it will return the object itself.
+ * This will return a {@link Function} for length-prefixed types that deserializes the object
+ * when {@link Function#apply} is called with the expected class of the return object (or {@code
+ * null} for no type check), for other types it will return the object itself.
*
- * <p>After calling {@link Supplier#get()} the parcel cursor will not change. Note that you
- * shouldn't recycle the parcel, not at least until all objects have been retrieved. No
+ * <p>After calling {@link Function#apply(Object)} the parcel cursor will not change. Note that
+ * you shouldn't recycle the parcel, not at least until all objects have been retrieved. No
* synchronization attempts are made.
*
- * </p>The supplier returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
- * suppliers are equal if either of the following is true:
+ * </p>The function returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
+ * function objects are equal if either of the following is true:
* <ul>
- * <li>{@link Supplier#get()} has been called on both and both objects returned are equal.
- * <li>{@link Supplier#get()} hasn't been called on either one and everything below is true:
+ * <li>{@link Function#apply} has been called on both and both objects returned are equal.
+ * <li>{@link Function#apply} hasn't been called on either one and everything below is true:
* <ul>
* <li>The {@code loader} parameters used to retrieve each are equal.
* <li>They both have the same type.
@@ -4357,7 +4358,7 @@
}
- private static final class LazyValue implements Supplier<Object> {
+ private static final class LazyValue implements Function<Class<?>, Object> {
/**
* | 4B | 4B |
* mSource = Parcel{... | type | length | object | ...}
@@ -4389,7 +4390,7 @@
}
@Override
- public Object get() {
+ public Object apply(@Nullable Class<?> clazz) {
Parcel source = mSource;
if (source != null) {
synchronized (source) {
@@ -4398,7 +4399,7 @@
int restore = source.dataPosition();
try {
source.setDataPosition(mPosition);
- mObject = source.readValue(mLoader);
+ mObject = source.readValue(mLoader, clazz);
} finally {
source.setDataPosition(restore);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c597a1a..e56f214 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -98,8 +98,8 @@
/** The userId of the constructor param context. To be used instead of mContext.getUserId(). */
private final @UserIdInt int mUserId;
- private Boolean mIsManagedProfileCached;
- private Boolean mIsProfileCached;
+ /** The userType of UserHandle.myUserId(); empty string if not a profile; null until cached. */
+ private String mProfileTypeOfProcessUser = null;
/**
* User type representing a {@link UserHandle#USER_SYSTEM system} user that is a human user.
@@ -2276,7 +2276,7 @@
* {@link UserManager#USER_TYPE_PROFILE_MANAGED managed profile}.
* @hide
*/
- public static boolean isUserTypeManagedProfile(String userType) {
+ public static boolean isUserTypeManagedProfile(@Nullable String userType) {
return USER_TYPE_PROFILE_MANAGED.equals(userType);
}
@@ -2284,7 +2284,7 @@
* Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_GUEST guest user}.
* @hide
*/
- public static boolean isUserTypeGuest(String userType) {
+ public static boolean isUserTypeGuest(@Nullable String userType) {
return USER_TYPE_FULL_GUEST.equals(userType);
}
@@ -2293,7 +2293,7 @@
* {@link UserManager#USER_TYPE_FULL_RESTRICTED restricted user}.
* @hide
*/
- public static boolean isUserTypeRestricted(String userType) {
+ public static boolean isUserTypeRestricted(@Nullable String userType) {
return USER_TYPE_FULL_RESTRICTED.equals(userType);
}
@@ -2301,7 +2301,7 @@
* Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_DEMO demo user}.
* @hide
*/
- public static boolean isUserTypeDemo(String userType) {
+ public static boolean isUserTypeDemo(@Nullable String userType) {
return USER_TYPE_FULL_DEMO.equals(userType);
}
@@ -2309,7 +2309,7 @@
* Returns whether the user type is a {@link UserManager#USER_TYPE_PROFILE_CLONE clone user}.
* @hide
*/
- public static boolean isUserTypeCloneProfile(String userType) {
+ public static boolean isUserTypeCloneProfile(@Nullable String userType) {
return USER_TYPE_PROFILE_CLONE.equals(userType);
}
@@ -2525,25 +2525,50 @@
}
private boolean isProfile(@UserIdInt int userId) {
- if (userId == mUserId) {
+ final String profileType = getProfileType(userId);
+ return profileType != null && !profileType.equals("");
+ }
+
+ /**
+ * Returns the user type of the context user if it is a profile.
+ *
+ * This is a more specific form of {@link #getUserType()} with relaxed permission requirements.
+ *
+ * @return the user type of the context user if it is a {@link #isProfile() profile},
+ * an empty string if it is not a profile,
+ * or null if the user doesn't exist.
+ */
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
+ private @Nullable String getProfileType() {
+ return getProfileType(mUserId);
+ }
+
+ /** @see #getProfileType() */
+ private @Nullable String getProfileType(@UserIdInt int userId) {
+ // First, the typical case (i.e. the *process* user, not necessarily the context user).
+ // This cache cannot be become invalidated since it's about the calling process itself.
+ if (userId == UserHandle.myUserId()) {
// No need for synchronization. Once it becomes non-null, it'll be non-null forever.
// Worst case we might end up calling the AIDL method multiple times but that's fine.
- if (mIsProfileCached != null) {
- return mIsProfileCached;
+ if (mProfileTypeOfProcessUser != null) {
+ return mProfileTypeOfProcessUser;
}
try {
- mIsProfileCached = mService.isProfile(mUserId);
- return mIsProfileCached;
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- } else {
- try {
- return mService.isProfile(userId);
+ final String profileType = mService.getProfileType(userId);
+ if (profileType != null) {
+ return mProfileTypeOfProcessUser = profileType.intern();
+ }
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
+
+ // The userId is not for the process's user. Use a slower cache that handles invalidation.
+ return mProfileTypeCache.query(userId);
}
/**
@@ -2577,50 +2602,26 @@
android.Manifest.permission.QUERY_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isManagedProfile(@UserIdInt int userId) {
- if (userId == mUserId) {
- // No need for synchronization. Once it becomes non-null, it'll be non-null forever.
- // Worst case we might end up calling the AIDL method multiple times but that's fine.
- if (mIsManagedProfileCached != null) {
- return mIsManagedProfileCached;
- }
- try {
- mIsManagedProfileCached = mService.isManagedProfile(mUserId);
- return mIsManagedProfileCached;
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- } else {
- try {
- return mService.isManagedProfile(userId);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
+ return isUserTypeManagedProfile(getProfileType(userId));
}
/**
* Checks if the context user is a clone profile.
*
- * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
- * must be in the same profile group of the user.
- *
* @return whether the context user is a clone profile.
*
* @see android.os.UserManager#USER_TYPE_PROFILE_CLONE
* @hide
*/
@SystemApi
- @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
- Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
- @UserHandleAware
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.QUERY_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
@SuppressAutoDoc
public boolean isCloneProfile() {
- try {
- return mService.isCloneProfile(mUserId);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ return isUserTypeCloneProfile(getProfileType());
}
/**
@@ -4840,15 +4841,16 @@
* @param overrideDevicePolicy when {@code true}, user is removed even if the caller has
* the {@link #DISALLOW_REMOVE_USER} or {@link #DISALLOW_REMOVE_MANAGED_PROFILE} restriction
*
- * @return the result code {@link #REMOVE_RESULT_REMOVED}, {@link #REMOVE_RESULT_DEFERRED},
- * {@link #REMOVE_RESULT_ALREADY_BEING_REMOVED}, or {@link #REMOVE_RESULT_ERROR}.
+ * @return the {@link RemoveResult} code: {@link #REMOVE_RESULT_REMOVED},
+ * {@link #REMOVE_RESULT_DEFERRED}, {@link #REMOVE_RESULT_ALREADY_BEING_REMOVED}, or
+ * {@link #REMOVE_RESULT_ERROR}.
*
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
- public int removeUserWhenPossible(@NonNull UserHandle user,
+ public @RemoveResult int removeUserWhenPossible(@NonNull UserHandle user,
boolean overrideDevicePolicy) {
try {
return mService.removeUserWhenPossible(user.getIdentifier(), overrideDevicePolicy);
@@ -5247,6 +5249,33 @@
}
}
+ /* Cache key for anything that assumes that userIds cannot be re-used without rebooting. */
+ private static final String CACHE_KEY_STATIC_USER_PROPERTIES = "cache_key.static_user_props";
+
+ private final PropertyInvalidatedCache<Integer, String> mProfileTypeCache =
+ new PropertyInvalidatedCache<Integer, String>(32, CACHE_KEY_STATIC_USER_PROPERTIES) {
+ @Override
+ public String recompute(Integer query) {
+ try {
+ // Will be null (and not cached) if invalid user; otherwise cache the type.
+ String profileType = mService.getProfileType(query);
+ if (profileType != null) profileType = profileType.intern();
+ return profileType;
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ @Override
+ public boolean bypass(Integer query) {
+ return query < 0;
+ }
+ };
+
+ /** {@hide} */
+ public static final void invalidateStaticUserProperties() {
+ PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STATIC_USER_PROPERTIES);
+ }
+
/**
* @hide
* User that enforces a restriction.
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 0eb21e1..722cdbc 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -203,4 +203,6 @@
void notifyAppIoResumed(in String volumeUuid, int uid, int tid, int reason) = 93;
int getExternalStorageMountMode(int uid, in String packageName) = 94;
boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 95;
+ void setCloudMediaProvider(in String authority) = 96;
+ String getCloudMediaProvider() = 97;
}
\ No newline at end of file
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4d65f39..4e1337f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -3002,6 +3002,35 @@
}
}
+ /**
+ * Notify the system of the current cloud media provider.
+ *
+ * This can only be called by the {@link android.service.storage.ExternalStorageService}
+ * holding the {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission.
+ *
+ * @param authority the authority of the content provider
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void setCloudMediaProvider(@Nullable String authority) {
+ try {
+ mStorageManager.setCloudMediaProvider(authority);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** @hide */
+ @TestApi
+ @Nullable
+ public String getCloudMediaProvider() {
+ try {
+ return mStorageManager.getCloudMediaProvider();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private final Object mFuseAppLoopLock = new Object();
@GuardedBy("mFuseAppLoopLock")
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 8928a42..059bd84 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -114,7 +114,6 @@
*/
public abstract void prepareAppDataAfterInstall(@NonNull String packageName, int uid);
-
/**
* Return true if uid is external storage service.
*/
@@ -151,4 +150,23 @@
* it's ok to access and modify CE directories on volumes for this user.
*/
public abstract boolean isCeStoragePrepared(@UserIdInt int userId);
+
+ /**
+ * A listener for changes to the cloud provider.
+ */
+ public interface CloudProviderChangeListener {
+ /**
+ * Triggered when the cloud provider changes. A {@code null} value means there's currently
+ * no cloud provider.
+ */
+ void onCloudProviderChanged(int userId, @Nullable String authority);
+ }
+
+ /**
+ * Register a {@link CloudProviderChangeListener} to be notified when a cloud media provider
+ * changes. The listener will be called after registration with any currently set cloud media
+ * providers.
+ */
+ public abstract void registerCloudProviderChangeListener(
+ @NonNull CloudProviderChangeListener listener);
}
diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
index c806409..b20dccc 100644
--- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java
+++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
@@ -50,6 +50,13 @@
public abstract boolean hasActiveSession(String packageName);
/**
+ * Returns the package name of the active session.
+ *
+ * @param callingVoiceInteractor the voice interactor binder from the calling VoiceInteractor.
+ */
+ public abstract String getVoiceInteractorPackageName(IBinder callingVoiceInteractor);
+
+ /**
* Gets the identity of the currently active HotwordDetectionService.
*
* @see HotwordDetectionServiceIdentity
@@ -82,4 +89,4 @@
return mOwnerUid;
}
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/speech/RecognitionSupport.java b/core/java/android/speech/RecognitionSupport.java
index 3a86d0b..43c8e1b 100644
--- a/core/java/android/speech/RecognitionSupport.java
+++ b/core/java/android/speech/RecognitionSupport.java
@@ -36,13 +36,16 @@
/** Support for this request is ready for use on this device for the returned languages. */
@NonNull
+ @DataClass.PluralOf("installedLanguage")
private List<String> mInstalledLanguages = null;
/** Support for this request is scheduled for download for the returned languages. */
+ @DataClass.PluralOf("pendingLanguage")
@NonNull private List<String> mPendingLanguages = null;
/** These languages are supported but need to be downloaded before use. */
@NonNull
+ @DataClass.PluralOf("supportedLanguage")
private List<String> mSupportedLanguages = null;
@@ -231,10 +234,7 @@
/** @see #setInstalledLanguages */
@DataClass.Generated.Member
- public @NonNull Builder addInstalledLanguages(@NonNull String value) {
- // You can refine this method's name by providing item's singular name, e.g.:
- // @DataClass.PluralOf("item")) mItems = ...
-
+ public @NonNull Builder addInstalledLanguage(@NonNull String value) {
if (mInstalledLanguages == null) setInstalledLanguages(new java.util.ArrayList<>());
mInstalledLanguages.add(value);
return this;
@@ -253,10 +253,7 @@
/** @see #setPendingLanguages */
@DataClass.Generated.Member
- public @NonNull Builder addPendingLanguages(@NonNull String value) {
- // You can refine this method's name by providing item's singular name, e.g.:
- // @DataClass.PluralOf("item")) mItems = ...
-
+ public @NonNull Builder addPendingLanguage(@NonNull String value) {
if (mPendingLanguages == null) setPendingLanguages(new java.util.ArrayList<>());
mPendingLanguages.add(value);
return this;
@@ -275,10 +272,7 @@
/** @see #setSupportedLanguages */
@DataClass.Generated.Member
- public @NonNull Builder addSupportedLanguages(@NonNull String value) {
- // You can refine this method's name by providing item's singular name, e.g.:
- // @DataClass.PluralOf("item")) mItems = ...
-
+ public @NonNull Builder addSupportedLanguage(@NonNull String value) {
if (mSupportedLanguages == null) setSupportedLanguages(new java.util.ArrayList<>());
mSupportedLanguages.add(value);
return this;
@@ -314,10 +308,10 @@
}
@DataClass.Generated(
- time = 1639158640137L,
+ time = 1644582623366L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/speech/RecognitionSupport.java",
- inputSignatures = "private @android.annotation.NonNull java.util.List<java.lang.String> mInstalledLanguages\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mPendingLanguages\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mSupportedLanguages\nclass RecognitionSupport extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"installedLanguage\") java.util.List<java.lang.String> mInstalledLanguages\nprivate @com.android.internal.util.DataClass.PluralOf(\"pendingLanguage\") @android.annotation.NonNull java.util.List<java.lang.String> mPendingLanguages\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"supportedLanguage\") java.util.List<java.lang.String> mSupportedLanguages\nclass RecognitionSupport extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index 43df294..e313388 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -11,6 +11,9 @@
roosa@google.com
jreck@google.com
+# Autofill
+per-file ViewStructure.java = file:/core/java/android/service/autofill/OWNERS
+
# Display
per-file Display*.java = file:/services/core/java/com/android/server/display/OWNERS
per-file Display*.aidl = file:/services/core/java/com/android/server/display/OWNERS
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index a2fcf80..1a458ce 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -23,7 +23,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.CompatibilityInfo.Translator;
import android.graphics.BLASTBufferQueue;
@@ -42,7 +41,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.SystemClock;
-import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceControl.Transaction;
@@ -135,7 +133,7 @@
private boolean mDisableBackgroundLayer = false;
/**
- * We use this lock to protect access to mSurfaceControl and
+ * We use this lock to protect access to mSurfaceControl and
* SurfaceViewPositionUpdateListener#mPositionChangedTransaction. Both are accessed on the UI
* thread and the render thread.
*/
@@ -376,11 +374,7 @@
}
private void updateSurfaceAlpha() {
- if (!mUseAlpha) {
- if (DEBUG) {
- Log.d(TAG, System.identityHashCode(this)
- + " updateSurfaceAlpha: setUseAlpha() is not called, ignored.");
- }
+ if (!mUseAlpha || !mHaveFrame || mSurfaceControl == null) {
return;
}
final float viewAlpha = getAlpha();
@@ -389,88 +383,16 @@
+ " updateSurfaceAlpha:"
+ " translucent color is not supported for a surface placed z-below.");
}
- if (!mHaveFrame) {
- if (DEBUG) {
- Log.d(TAG, System.identityHashCode(this)
- + " updateSurfaceAlpha: has no surface.");
- }
- return;
- }
final ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot == null) {
- if (DEBUG) {
- Log.d(TAG, System.identityHashCode(this)
- + " updateSurfaceAlpha: ViewRootImpl not available.");
- }
- return;
- }
- if (mSurfaceControl == null) {
- if (DEBUG) {
- Log.d(TAG, System.identityHashCode(this)
- + "updateSurfaceAlpha:"
- + " surface is not yet created, or already released.");
- }
- return;
- }
- final Surface parent = viewRoot.mSurface;
- if (parent == null || !parent.isValid()) {
- if (DEBUG) {
- Log.d(TAG, System.identityHashCode(this)
- + " updateSurfaceAlpha: ViewRootImpl has no valid surface");
- }
return;
}
final float alpha = getFixedAlpha();
if (alpha != mSurfaceAlpha) {
- if (isHardwareAccelerated()) {
- /*
- * Schedule a callback that reflects an alpha value onto the underlying surfaces.
- * This gets called on a RenderThread worker thread, so members accessed here must
- * be protected by a lock.
- */
- viewRoot.registerRtFrameCallback(frame -> {
- try {
- synchronized (mSurfaceControlLock) {
- if (!parent.isValid()) {
- if (DEBUG) {
- Log.d(TAG, System.identityHashCode(this)
- + " updateSurfaceAlpha RT:"
- + " ViewRootImpl has no valid surface");
- }
- return;
- }
- if (mSurfaceControl == null) {
- if (DEBUG) {
- Log.d(TAG, System.identityHashCode(this)
- + "updateSurfaceAlpha RT:"
- + " mSurfaceControl has already released");
- }
- return;
- }
- if (DEBUG) {
- Log.d(TAG, System.identityHashCode(this)
- + " updateSurfaceAlpha RT: set alpha=" + alpha);
- }
-
- mFrameCallbackTransaction.setAlpha(mSurfaceControl, alpha);
- applyOrMergeTransaction(mFrameCallbackTransaction, frame);
- }
- // It's possible that mSurfaceControl is released in the UI thread before
- // the transaction completes. If that happens, an exception is thrown, which
- // must be caught immediately.
- } catch (Exception e) {
- Log.e(TAG, System.identityHashCode(this)
- + "updateSurfaceAlpha RT: Exception during surface transaction", e);
- }
- });
- damageInParent();
- } else {
- if (DEBUG) {
- Log.d(TAG, System.identityHashCode(this)
- + " updateSurfaceAlpha: set alpha=" + alpha);
- }
- mTmpTransaction.setAlpha(mSurfaceControl, alpha).apply();
- }
+ final Transaction transaction = new Transaction();
+ transaction.setAlpha(mSurfaceControl, alpha);
+ viewRoot.applyTransactionOnDraw(transaction);
+ damageInParent();
mSurfaceAlpha = alpha;
}
}
@@ -527,12 +449,7 @@
mRequestedVisible = false;
updateSurface();
-
- // We don't release this as part of releaseSurfaces as
- // that is also called on transient visibility changes. We can't
- // recreate this Surface, so only release it when we are fully
- // detached.
- tryReleaseSurfaces(true /* releaseSurfacePackage*/);
+ releaseSurfaces(true /* releaseSurfacePackage*/);
mHaveFrame = false;
super.onDetachedFromWindow();
@@ -625,7 +542,7 @@
public void setClipBounds(Rect clipBounds) {
super.setClipBounds(clipBounds);
- if (!mClipSurfaceToBounds) {
+ if (!mClipSurfaceToBounds || mSurfaceControl == null) {
return;
}
@@ -635,18 +552,15 @@
invalidate();
}
- if (mSurfaceControl != null) {
- if (mClipBounds != null) {
- mTmpRect.set(mClipBounds);
- } else {
- mTmpRect.set(0, 0, mSurfaceWidth, mSurfaceHeight);
- }
- SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(this);
- applier.scheduleApply(
- new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(mSurfaceControl)
- .withWindowCrop(mTmpRect)
- .build());
+ if (mClipBounds != null) {
+ mTmpRect.set(mClipBounds);
+ } else {
+ mTmpRect.set(0, 0, mSurfaceWidth, mSurfaceHeight);
}
+ final Transaction transaction = new Transaction();
+ transaction.setWindowCrop(mSurfaceControl, mTmpRect);
+ applyTransactionOnVriDraw(transaction);
+ invalidate();
}
private void clearSurfaceViewPort(Canvas canvas) {
@@ -782,37 +696,10 @@
if (viewRoot == null) {
return true;
}
- final Surface parent = viewRoot.mSurface;
- if (parent == null || !parent.isValid()) {
- return true;
- }
-
- /*
- * Schedule a callback that reflects an alpha value onto the underlying surfaces.
- * This gets called on a RenderThread worker thread, so members accessed here must
- * be protected by a lock.
- */
- viewRoot.registerRtFrameCallback(frame -> {
- try {
- synchronized (mSurfaceControlLock) {
- if (!parent.isValid() || mSurfaceControl == null) {
- return;
- }
-
- updateRelativeZ(mFrameCallbackTransaction);
- applyOrMergeTransaction(mFrameCallbackTransaction, frame);
- }
- // It's possible that mSurfaceControl is released in the UI thread before
- // the transaction completes. If that happens, an exception is thrown, which
- // must be caught immediately.
- } catch (Exception e) {
- Log.e(TAG, System.identityHashCode(this)
- + "setZOrderOnTop RT: Exception during surface transaction", e);
- }
- });
-
+ final Transaction transaction = new SurfaceControl.Transaction();
+ updateRelativeZ(transaction);
+ viewRoot.applyTransactionOnDraw(transaction);
invalidate();
-
return true;
}
@@ -863,7 +750,7 @@
return t;
}
- private void tryReleaseSurfaces(boolean releaseSurfacePackage) {
+ private void releaseSurfaces(boolean releaseSurfacePackage) {
mSurfaceAlpha = 1f;
synchronized (mSurfaceControlLock) {
@@ -892,12 +779,7 @@
mSurfacePackage = null;
}
- ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot != null) {
- viewRoot.applyTransactionOnDraw(transaction);
- } else {
- transaction.apply();
- }
+ applyTransactionOnVriDraw(transaction);
}
}
@@ -1041,7 +923,7 @@
if (viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
notifySurfaceDestroyed();
- tryReleaseSurfaces(false /* releaseSurfacePackage*/);
+ releaseSurfaces(false /* releaseSurfacePackage*/);
return;
}
@@ -1182,7 +1064,7 @@
} finally {
mIsCreating = false;
if (mSurfaceControl != null && !mSurfaceCreated) {
- tryReleaseSurfaces(false /* releaseSurfacePackage*/);
+ releaseSurfaces(false /* releaseSurfacePackage*/);
}
}
} catch (Exception ex) {
@@ -1317,17 +1199,6 @@
}
/**
- * A place to over-ride for applying child-surface transactions.
- * These can be synchronized with the viewroot surface using deferTransaction.
- *
- * Called from RenderWorker while UI thread is paused.
- * @hide
- */
- protected void applyChildSurfaceTransaction_renderWorker(SurfaceControl.Transaction t,
- Surface viewRootSurface, long nextViewRootFrameNumber) {
- }
-
- /**
* Sets the surface position and scale. Can be called on
* the UI thread as well as on the renderer thread.
*
@@ -1435,11 +1306,6 @@
if (mViewVisibility) {
mPositionChangedTransaction.show(mSurfaceControl);
}
- final ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot != null) {
- applyChildSurfaceTransaction_renderWorker(mPositionChangedTransaction,
- viewRoot.mSurface, frameNumber);
- }
applyOrMergeTransaction(mPositionChangedTransaction, frameNumber);
mPendingTransaction = false;
} catch (Exception ex) {
@@ -1837,12 +1703,7 @@
mSurfacePackage.release();
}
reparentSurfacePackage(transaction, p);
- final ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot != null) {
- viewRoot.applyTransactionOnDraw(transaction);
- } else {
- transaction.apply();
- }
+ applyTransactionOnVriDraw(transaction);
}
mSurfacePackage = p;
invalidate();
@@ -1948,4 +1809,13 @@
}
}
+ private void applyTransactionOnVriDraw(Transaction t) {
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ // If we are using BLAST, merge the transaction with the viewroot buffer transaction.
+ viewRoot.applyTransactionOnDraw(t);
+ } else {
+ t.apply();
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 150eb65..4ae6bf7 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -217,12 +217,6 @@
private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
- private static final String[] QUERY_FILE_INFO_PROJECTION = {
- OpenableColumns.DISPLAY_NAME,
- Downloads.Impl.COLUMN_TITLE,
- DocumentsContract.Document.COLUMN_FLAGS
- };
-
private static final String PLURALS_COUNT = "count";
private static final String PLURALS_FILE_NAME = "file_name";
@@ -1480,15 +1474,15 @@
* and to avoid mocking Android core classes.
*/
@VisibleForTesting
- public Cursor queryResolver(ContentResolver resolver, String[] projection, Uri uri) {
- return resolver.query(uri, projection, null, null, null);
+ public Cursor queryResolver(ContentResolver resolver, Uri uri) {
+ return resolver.query(uri, null, null, null, null);
}
private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) {
String fileName = null;
boolean hasThumbnail = false;
- try (Cursor cursor = queryResolver(resolver, QUERY_FILE_INFO_PROJECTION, uri)) {
+ try (Cursor cursor = queryResolver(resolver, uri)) {
if (cursor != null && cursor.getCount() > 0) {
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int titleIndex = cursor.getColumnIndex(Downloads.Impl.COLUMN_TITLE);
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 7b6df6c..e58f4f0 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -22,7 +22,6 @@
import android.view.View;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.FrameworkStatsLog;
/**
* Writes sysui_multi_event records to the system event log.
@@ -53,10 +52,7 @@
}
protected void saveLog(LogMaker log) {
- // TODO(b/116684537): Flag guard logging to event log and statsd socket.
EventLogTags.writeSysuiMultiAction(log.serialize());
- FrameworkStatsLog.write(FrameworkStatsLog.KEY_VALUE_PAIRS_ATOM,
- /* UID is retrieved from statsd side */ 0, log.getEntries());
}
public static final int VIEW_UNKNOWN = MetricsEvent.VIEW_UNKNOWN;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 955f46b..63b704c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -252,7 +252,6 @@
"libandroid_net",
"libandroidicu",
"libbattery",
- "libbpf_android",
"libnetdutils",
"libmemtrack",
"libandroidfw",
diff --git a/core/jni/android_app_admin_SecurityLog.cpp b/core/jni/android_app_admin_SecurityLog.cpp
index e5a13db..d197edc 100644
--- a/core/jni/android_app_admin_SecurityLog.cpp
+++ b/core/jni/android_app_admin_SecurityLog.cpp
@@ -85,10 +85,6 @@
(void*) android_app_admin_SecurityLog_isLoggingEnabled
},
{ "writeEvent",
- "(ILjava/lang/String;)I",
- (void*) SLog::writeEventString
- },
- { "writeEvent",
"(I[Ljava/lang/Object;)I",
(void*) SLog::writeEventArray
},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0c41f31..68c8143 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5917,10 +5917,9 @@
<!-- @SystemApi Allows an application to manage the wallpaper effects
generation service.
- @hide <p>Not for use by third-party applications.</p> -->
+ @hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION"
- android:protectionLevel="signature" />
-
+ android:protectionLevel="signature|role" />
<!-- Allows an app to set the theme overlay in /vendor/overlay
being used.
@@ -6256,7 +6255,10 @@
android:protectionLevel="signature|privileged" />
<!-- Allows an application to read nearby streaming policy. The policy controls
- whether to allow the device to stream its notifications and apps to nearby devices. -->
+ whether to allow the device to stream its notifications and apps to nearby devices.
+ Applications that are not the device owner will need this permission to call
+ {@link android.app.admin.DevicePolicyManager#getNearbyNotificationStreamingPolicy} or
+ {@link android.app.admin.DevicePolicyManager#getNearbyAppStreamingPolicy}. -->
<permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY"
android:protectionLevel="normal" />
@@ -6378,6 +6380,11 @@
<permission android:name="android.permission.TIS_EXTENSION_INTERFACE"
android:protectionLevel="signature|privileged|vendorPrivileged" />
+ <!-- @SystemApi Allows an application to write to the security log buffer in logd.
+ @hide -->
+ <permission android:name="android.permission.WRITE_SECURITY_LOG"
+ android:protectionLevel="signature|privileged" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/drawable/toast_frame.xml b/core/res/res/drawable/toast_frame.xml
index 44c00c0..a8cdef6 100644
--- a/core/res/res/drawable/toast_frame.xml
+++ b/core/res/res/drawable/toast_frame.xml
@@ -17,7 +17,7 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="?android:attr/colorBackground" />
+ <solid android:color="?android:attr/colorSurface" />
<corners android:radius="28dp" />
</shape>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3a9cb2e..fca2bd1 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3989,31 +3989,34 @@
<attr name="isAccessibilityTool" format="boolean" />
<!-- Animated image of the accessibility service purpose or behavior, to help users
- understand how the service can help them.-->
+ understand how the service can help them. -->
<attr name="animatedImageDrawable" format="reference"/>
- <!-- Html description of the accessibility service, to help users understand
- how the service can help them.-->
+ <!-- Html description of the accessibility service usage, availability, or limitations (e.g.
+ isn't supported by all apps). -->
<attr name="htmlDescription" format="reference"/>
-
- <!-- Short description of the accessibility service purpose or behavior.-->
+ <!-- Description of the accessibility service usage, availability, or limitations (e.g.
+ isn't supported by all apps). -->
<attr name="description" />
<!-- Brief summary of the accessibility service purpose or behavior. -->
<attr name="summary" />
+ <!-- Detailed intro of the accessibility service purpose or behavior. -->
+ <attr name="intro" format="reference" />
</declare-styleable>
<!-- Use <code>accessibility-shortcut-target</code> as the root tag of the XML resource that
describes an activity, which is referenced from the
<code>android.accessibilityshortcut.target</code> meta-data entry. -->
<declare-styleable name="AccessibilityShortcutTarget">
- <!-- Short description of the target of accessibility shortcut purpose or behavior.-->
+ <!-- Description of the target of accessibility shortcut usage, availability, or limitations
+ (e.g. isn't supported by all apps). -->
<attr name="description" />
<!-- Brief summary of the target of accessibility shortcut purpose or behavior. -->
<attr name="summary" />
<!-- Animated image of the target of accessibility shortcut purpose or behavior, to help
users understand how the target of accessibility shortcut can help them.-->
<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. -->
+ <!-- Html description of the target of accessibility shortcut usage, availability, or
+ limitations (e.g. isn't supported by all apps). -->
<attr name="htmlDescription" format="reference"/>
<!-- Component name of an activity that allows the user to modify the settings for this
target of accessibility shortcut. -->
@@ -4023,6 +4026,8 @@
settings to remind users this accessibility service has a
{@link android.service.quicksettings.TileService}. -->
<attr name="tileService" format="string" />
+ <!-- Detailed intro of the target of accessibility shortcut purpose or behavior. -->
+ <attr name="intro" 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/config.xml b/core/res/res/values/config.xml
index 6eb19e1..5e6d05a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1307,6 +1307,7 @@
<!-- Control the behavior when the user double-taps the home button.
0 - Nothing
1 - Recent apps view in SystemUI
+ 2 - Picture-in-picture menu
This needs to match the constants in
policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
-->
@@ -5771,4 +5772,21 @@
current drain threshold.
-->
<integer name="config_bg_current_drain_location_min_duration">1800</integer>
+
+ <!-- The behavior for an app with a FGS and its notification is still showing, when the system
+ detects it's abusive and should be put into bg restricted level. True - we'll
+ show the prompt to user, False - we'll not show it.
+ -->
+ <bool name="config_bg_prompt_fgs_with_noti_to_bg_restricted">false</bool>
+
+ <!-- The types of state where we'll exempt its battery usage during that state.
+ The state here must be one or a combination of STATE_TYPE_* in BaseAppStateTracker.
+ -->
+ <integer name="config_bg_current_drain_exempted_types">9</integer>
+
+ <!-- The behavior when an app has the permission ACCESS_BACKGROUND_LOCATION granted,
+ whether or not the system will use a higher threshold towards its background battery usage
+ because of it.
+ -->
+ <bool name="config_bg_current_drain_high_threshold_by_bg_location">false</bool>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3d80ca8..7bf34a1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3278,6 +3278,7 @@
<public name="windowSplashScreenBehavior" />
<public name="allowUntrustedActivityEmbedding" />
<public name="knownActivityEmbeddingCerts" />
+ <public name="intro" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c63e543..ff1c70a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3699,6 +3699,9 @@
<java-symbol type="string" name="config_defaultSystemCaptionsService" />
<java-symbol type="string" name="config_defaultSystemCaptionsManagerService" />
<java-symbol type="string" name="config_defaultAmbientContextDetectionService" />
+ <java-symbol type="string" name="config_defaultAmbientContextConsentComponent" />
+ <java-symbol type="string" name="config_ambientContextPackageNameExtraKey" />
+ <java-symbol type="string" name="config_ambientContextEventArrayExtraKey" />
<java-symbol type="string" name="config_retailDemoPackage" />
<java-symbol type="string" name="config_retailDemoPackageSignature" />
@@ -4747,4 +4750,7 @@
<java-symbol type="array" name="config_bg_current_drain_high_threshold_to_bg_restricted" />
<java-symbol type="integer" name="config_bg_current_drain_media_playback_min_duration" />
<java-symbol type="integer" name="config_bg_current_drain_location_min_duration" />
+ <java-symbol type="bool" name="config_bg_prompt_fgs_with_noti_to_bg_restricted" />
+ <java-symbol type="integer" name="config_bg_current_drain_exempted_types" />
+ <java-symbol type="bool" name="config_bg_current_drain_high_threshold_by_bg_location" />
</resources>
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index 21613a8..e51eab6 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -143,6 +143,9 @@
<!-- ResourcesLocaleResolutionTest -->
<string name="dummy_string">dummy string</string>
+ <!-- Intro of the accessibility shortcut [CHAR LIMIT=NONE] -->
+ <string name="accessibility_shortcut_intro">Accessibility shortcut intro</string>
+
<!-- Description of the accessibility shortcut [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_description">Accessibility shortcut description</string>
diff --git a/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml b/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
index 5fc536a..52fe65d 100644
--- a/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
+++ b/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
@@ -23,4 +23,4 @@
android:htmlDescription="@string/accessibility_shortcut_html_description"
android:settingsActivity="com.example.shortcut.target.SettingsActivity"
android:tileService="com.example.shortcut.target.TileService"
-/>
\ No newline at end of file
+ android:intro="@string/accessibility_shortcut_intro" />
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
index 76fb520..f605a00 100644
--- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
@@ -89,6 +89,16 @@
}
@Test
+ public void testIntro() {
+ final String intro = mTargetContext.getResources()
+ .getString(R.string.accessibility_shortcut_intro);
+
+ assertNotNull("Can't find intro string", intro);
+ assertThat("Intro is not correct",
+ mShortcutInfo.loadIntro(mPackageManager), is(intro));
+ }
+
+ @Test
public void testAnimatedImageRes() {
assertThat("Animated image resource id is not correct",
mShortcutInfo.getAnimatedImageRes(), is(R.drawable.bitmap_drawable));
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 139bc36..7f85982 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -192,7 +192,7 @@
}
@Override
- public Cursor queryResolver(ContentResolver resolver, String[] projection, Uri uri) {
+ public Cursor queryResolver(ContentResolver resolver, Uri uri) {
if (sOverrides.resolverCursor != null) {
return sOverrides.resolverCursor;
}
@@ -201,7 +201,7 @@
throw new SecurityException("Test exception handling");
}
- return super.queryResolver(resolver, projection, uri);
+ return super.queryResolver(resolver, uri);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 6ed1ba9..6ffcf10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -618,7 +618,8 @@
}
/** Contains information to help position things on the screen. */
- BubblePositioner getPositioner() {
+ @VisibleForTesting
+ public BubblePositioner getPositioner() {
return mBubblePositioner;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 9ae67a9..da8308e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -60,6 +60,7 @@
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.TaskView;
@@ -418,8 +419,9 @@
mPointerView.setBackground(mCurrentPointer);
}
- private String getBubbleKey() {
- return mBubble != null ? mBubble.getKey() : "null";
+ @VisibleForTesting
+ public String getBubbleKey() {
+ return mBubble != null ? mBubble.getKey() : mIsOverflow ? BubbleOverflow.KEY : null;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index dd751d2..eb7929b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -54,6 +54,7 @@
/** Call before use and again if cleanUpExpandedState was called. */
fun initialize(controller: BubbleController) {
+ createExpandedView()
getExpandedView()?.initialize(controller, controller.stackView, true /* isOverflow */)
}
@@ -123,13 +124,15 @@
overflowBtn?.updateDotVisibility(true /* animate */)
}
+ fun createExpandedView(): BubbleExpandedView? {
+ expandedView = inflater.inflate(R.layout.bubble_expanded_view,
+ null /* root */, false /* attachToRoot */) as BubbleExpandedView
+ expandedView?.applyThemeAttrs()
+ updateResources()
+ return expandedView
+ }
+
override fun getExpandedView(): BubbleExpandedView? {
- if (expandedView == null) {
- expandedView = inflater.inflate(R.layout.bubble_expanded_view,
- null /* root */, false /* attachToRoot */) as BubbleExpandedView
- expandedView?.applyThemeAttrs()
- updateResources()
- }
return expandedView
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index c2eb08c..9219baa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -787,7 +787,7 @@
ta.recycle();
final Runnable onBubbleAnimatedOut = () -> {
- if (getBubbleCount() == 0 && !mBubbleData.isShowingOverflow()) {
+ if (getBubbleCount() == 0) {
mBubbleController.onAllBubblesAnimatedOut();
}
};
@@ -1651,7 +1651,6 @@
} else {
bubble.cleanupViews();
}
- updatePointerPosition(false /* forIme */);
updateExpandedView();
logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 34d98ee..5af1530 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -419,8 +419,8 @@
// hasRoundedCorners is currently only enabled for tasks
final Context displayContext =
mDisplayController.getDisplayContext(change.getTaskInfo().displayId);
- cornerRadius =
- ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
+ cornerRadius = displayContext == null ? 0
+ : ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
} else {
cornerRadius = 0;
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 7a1d619..1e30f6b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -47,6 +47,7 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
+@FlakyTest(bugId = 218604389)
open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 27873bb..c8ced1c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -64,6 +64,7 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
+@FlakyTest(bugId = 218604389)
open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val fixedApp = FixedAppHelper(instrumentation)
private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.startRotation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index d35654e..d65388b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -47,6 +47,7 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
+@FlakyTest(bugId = 218604389)
open class SetRequestedOrientationWhilePinnedTest(
testSpec: FlickerTestParameter
) : PipTransition(testSpec) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java
new file mode 100644
index 0000000..8278c67
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.bubbles.BubbleOverflow;
+import com.android.wm.shell.bubbles.BubbleStackView;
+import com.android.wm.shell.bubbles.TestableBubblePositioner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link com.android.wm.shell.bubbles.BubbleOverflow}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class BubbleOverflowTest extends ShellTestCase {
+
+ private TestableBubblePositioner mPositioner;
+ private BubbleOverflow mOverflow;
+
+ @Mock
+ private BubbleController mBubbleController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mPositioner = new TestableBubblePositioner(mContext, mock(WindowManager.class));
+ when(mBubbleController.getPositioner()).thenReturn(mPositioner);
+ when(mBubbleController.getStackView()).thenReturn(mock(BubbleStackView.class));
+
+ mOverflow = new BubbleOverflow(mContext, mPositioner);
+ }
+
+ @Test
+ public void test_initialize() {
+ assertThat(mOverflow.getExpandedView()).isNull();
+
+ mOverflow.initialize(mBubbleController);
+
+ assertThat(mOverflow.getExpandedView()).isNotNull();
+ assertThat(mOverflow.getExpandedView().getBubbleKey()).isEqualTo(BubbleOverflow.KEY);
+ }
+
+ @Test
+ public void test_cleanUpExpandedState() {
+ mOverflow.createExpandedView();
+ assertThat(mOverflow.getExpandedView()).isNotNull();
+
+ mOverflow.cleanUpExpandedState();
+ assertThat(mOverflow.getExpandedView()).isNull();
+ }
+
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 8ba3539..a6fdf6c 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -402,6 +402,14 @@
boolean isSpatializerAvailable();
+ boolean isSpatializerAvailableForDevice(in AudioDeviceAttributes device);
+
+ boolean hasHeadTracker(in AudioDeviceAttributes device);
+
+ void setHeadTrackerEnabled(boolean enabled, in AudioDeviceAttributes device);
+
+ boolean isHeadTrackerEnabled(in AudioDeviceAttributes device);
+
void setSpatializerEnabled(boolean enabled);
boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af);
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index 030d212..be0ef37 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -108,6 +108,83 @@
}
}
+ /**
+ * @hide
+ * Returns whether spatialization is available for a given audio device
+ * Reasons for spatialization being unavailable include situations where audio output is
+ * incompatible with sound spatialization, such as the device being a monophonic speaker, or
+ * the spatializer effect not supporting transaural processing when querying for speaker.
+ * @param device the audio device for which spatializer availability is queried
+ * @return {@code true} if the spatializer effect is available and capable
+ * of processing the audio over the given audio device,
+ * {@code false} otherwise.
+ * @see #isEnabled()
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public boolean isAvailableForDevice(@NonNull AudioDeviceAttributes device) {
+ Objects.requireNonNull(device);
+ try {
+ return mAm.getService().isSpatializerAvailableForDevice(device);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ * Returns whether the given device has an associated headtracker
+ * @param device the audio device to query
+ * @return true if the device has a head tracker, false otherwise
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public boolean hasHeadTracker(@NonNull AudioDeviceAttributes device) {
+ Objects.requireNonNull(device);
+ try {
+ return mAm.getService().hasHeadTracker(device);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ * Enables or disables the head tracker of the given device
+ * @param enabled true to enable, false to disable
+ * @param device the device whose head tracker state is changed
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public void setHeadTrackerEnabled(boolean enabled, @NonNull AudioDeviceAttributes device) {
+ Objects.requireNonNull(device);
+ try {
+ mAm.getService().setHeadTrackerEnabled(enabled, device);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns whether the head tracker of the device is enabled
+ * @param device the device to query
+ * @return true if the head tracker is enabled, false if disabled or if there isn't one
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public boolean isHeadTrackerEnabled(@NonNull AudioDeviceAttributes device) {
+ Objects.requireNonNull(device);
+ try {
+ return mAm.getService().isHeadTrackerEnabled(device);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
/** @hide */
@IntDef(flag = false, value = {
SPATIALIZER_IMMERSIVE_LEVEL_OTHER,
diff --git a/media/java/android/media/metrics/BundleSession.java b/media/java/android/media/metrics/BundleSession.java
new file mode 100644
index 0000000..743d233
--- /dev/null
+++ b/media/java/android/media/metrics/BundleSession.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.Objects;
+
+/**
+ * An instances of this class represents a session with data stored in a bundle.
+ */
+public final class BundleSession implements AutoCloseable {
+ private final @NonNull String mId;
+ private final @NonNull MediaMetricsManager mManager;
+ private final @NonNull LogSessionId mLogSessionId;
+
+ /** @hide */
+ public BundleSession(@NonNull String id, @NonNull MediaMetricsManager manager) {
+ mId = id;
+ mManager = manager;
+ AnnotationValidations.validate(NonNull.class, null, mId);
+ AnnotationValidations.validate(NonNull.class, null, mManager);
+ mLogSessionId = new LogSessionId(mId);
+ }
+
+ /**
+ * Reports metrics via bundle.
+ *
+ */
+ public void reportBundleMetrics(@NonNull PersistableBundle metrics) {
+ mManager.reportBundleMetrics(mId, metrics);
+ }
+
+ public @NonNull LogSessionId getSessionId() {
+ return mLogSessionId;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ BundleSession that = (BundleSession) o;
+ return Objects.equals(mId, that.mId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId);
+ }
+
+ @Override
+ public void close() {
+ }
+}
diff --git a/media/java/android/media/metrics/EditingSession.java b/media/java/android/media/metrics/EditingSession.java
new file mode 100644
index 0000000..2a48a72
--- /dev/null
+++ b/media/java/android/media/metrics/EditingSession.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.Objects;
+
+/**
+ * An instances of this class represents a session of media editing.
+ */
+public final class EditingSession implements AutoCloseable {
+ private final @NonNull String mId;
+ private final @NonNull MediaMetricsManager mManager;
+ private final @NonNull LogSessionId mLogSessionId;
+
+ /** @hide */
+ public EditingSession(@NonNull String id, @NonNull MediaMetricsManager manager) {
+ mId = id;
+ mManager = manager;
+ AnnotationValidations.validate(NonNull.class, null, mId);
+ AnnotationValidations.validate(NonNull.class, null, mManager);
+ mLogSessionId = new LogSessionId(mId);
+ }
+
+ public @NonNull LogSessionId getSessionId() {
+ return mLogSessionId;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ EditingSession that = (EditingSession) o;
+ return Objects.equals(mId, that.mId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId);
+ }
+
+ @Override
+ public void close() {
+ }
+}
diff --git a/media/java/android/media/metrics/IMediaMetricsManager.aidl b/media/java/android/media/metrics/IMediaMetricsManager.aidl
index f2c0d44..a774403 100644
--- a/media/java/android/media/metrics/IMediaMetricsManager.aidl
+++ b/media/java/android/media/metrics/IMediaMetricsManager.aidl
@@ -21,6 +21,7 @@
import android.media.metrics.PlaybackMetrics;
import android.media.metrics.PlaybackStateEvent;
import android.media.metrics.TrackChangeEvent;
+import android.os.PersistableBundle;
/**
* Interface to the playback manager service.
@@ -28,10 +29,16 @@
*/
interface IMediaMetricsManager {
void reportPlaybackMetrics(in String sessionId, in PlaybackMetrics metrics, int userId);
+
String getPlaybackSessionId(int userId);
String getRecordingSessionId(int userId);
void reportNetworkEvent(in String sessionId, in NetworkEvent event, int userId);
void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId);
void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId);
void reportTrackChangeEvent(in String sessionId, in TrackChangeEvent event, int userId);
-}
\ No newline at end of file
+
+ String getTranscodingSessionId(int userId);
+ String getEditingSessionId(int userId);
+ String getBundleSessionId(int userId);
+ void reportBundleMetrics(in String sessionId, in PersistableBundle metrics, int userId);
+}
diff --git a/media/java/android/media/metrics/MediaMetricsManager.java b/media/java/android/media/metrics/MediaMetricsManager.java
index 23b697f..7229c47 100644
--- a/media/java/android/media/metrics/MediaMetricsManager.java
+++ b/media/java/android/media/metrics/MediaMetricsManager.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
+import android.os.PersistableBundle;
import android.os.RemoteException;
/**
@@ -53,6 +54,17 @@
}
}
/**
+ * Reports bundle metrics.
+ * @hide
+ */
+ public void reportBundleMetrics(@NonNull String sessionId, PersistableBundle metrics) {
+ try {
+ mService.reportBundleMetrics(sessionId, metrics, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ /**
* Reports network event.
* @hide
*/
@@ -117,6 +129,48 @@
}
/**
+ * Creates a transcoding session.
+ */
+ @NonNull
+ public TranscodingSession createTranscodingSession() {
+ try {
+ String id = mService.getTranscodingSessionId(mUserId);
+ TranscodingSession session = new TranscodingSession(id, this);
+ return session;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a editing session.
+ */
+ @NonNull
+ public EditingSession createEditingSession() {
+ try {
+ String id = mService.getEditingSessionId(mUserId);
+ EditingSession session = new EditingSession(id, this);
+ return session;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a generic bundle session.
+ */
+ @NonNull
+ public BundleSession createBundleSession() {
+ try {
+ String id = mService.getBundleSessionId(mUserId);
+ BundleSession session = new BundleSession(id, this);
+ return session;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Reports error event.
* @hide
*/
diff --git a/media/java/android/media/metrics/TranscodingSession.java b/media/java/android/media/metrics/TranscodingSession.java
new file mode 100644
index 0000000..e6d359a
--- /dev/null
+++ b/media/java/android/media/metrics/TranscodingSession.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.Objects;
+
+/**
+ * An instances of this class represents a session of media Transcoding.
+ */
+public final class TranscodingSession implements AutoCloseable {
+ private final @NonNull String mId;
+ private final @NonNull MediaMetricsManager mManager;
+ private final @NonNull LogSessionId mLogSessionId;
+
+ /** @hide */
+ public TranscodingSession(@NonNull String id, @NonNull MediaMetricsManager manager) {
+ mId = id;
+ mManager = manager;
+ AnnotationValidations.validate(NonNull.class, null, mId);
+ AnnotationValidations.validate(NonNull.class, null, mManager);
+ mLogSessionId = new LogSessionId(mId);
+ }
+
+ public @NonNull LogSessionId getSessionId() {
+ return mLogSessionId;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ TranscodingSession that = (TranscodingSession) o;
+ return Objects.equals(mId, that.mId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId);
+ }
+
+ @Override
+ public void close() {
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 4e06ed7..bd3deae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -444,7 +444,8 @@
}
final Intent intent = new Intent()
.setComponent(dreamInfo.settingsComponentName)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ .addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
uiContext.startActivity(intent);
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e9f940a..741fe4f 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -603,6 +603,9 @@
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
+ <!-- Permission required for CTS test - CtsAmbientContextDetectionServiceDeviceTest -->
+ <uses-permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT" />
+
<!-- Permission required for CTS test - CallAudioInterceptionTest -->
<uses-permission android:name="android.permission.CALL_AUDIO_INTERCEPTION" />
diff --git a/packages/SystemUI/res/layout/rounded_corners_bottom.xml b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
index b2857ab..bb6d4bd 100644
--- a/packages/SystemUI/res/layout/rounded_corners_bottom.xml
+++ b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
@@ -25,6 +25,7 @@
android:layout_height="12dp"
android:layout_gravity="left|bottom"
android:tint="#ff000000"
+ android:visibility="gone"
android:src="@drawable/rounded_corner_bottom"/>
<ImageView
@@ -32,6 +33,7 @@
android:layout_width="12dp"
android:layout_height="12dp"
android:tint="#ff000000"
+ android:visibility="gone"
android:layout_gravity="right|bottom"
android:src="@drawable/rounded_corner_bottom"/>
diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml
index 9937c21..46648c8 100644
--- a/packages/SystemUI/res/layout/rounded_corners_top.xml
+++ b/packages/SystemUI/res/layout/rounded_corners_top.xml
@@ -25,6 +25,7 @@
android:layout_height="12dp"
android:layout_gravity="left|top"
android:tint="#ff000000"
+ android:visibility="gone"
android:src="@drawable/rounded_corner_top"/>
<ImageView
@@ -32,6 +33,7 @@
android:layout_width="12dp"
android:layout_height="12dp"
android:tint="#ff000000"
+ android:visibility="gone"
android:layout_gravity="right|top"
android:src="@drawable/rounded_corner_top"/>
diff --git a/packages/SystemUI/res/layout/screen_decor_hwc_layer.xml b/packages/SystemUI/res/layout/screen_decor_hwc_layer.xml
new file mode 100644
index 0000000..1c17e75
--- /dev/null
+++ b/packages/SystemUI/res/layout/screen_decor_hwc_layer.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ 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.
+ -->
+<com.android.systemui.RegionInterceptingFrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/screen_decor_hwc_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
index 1633e52..2d883bc 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
@@ -21,7 +21,7 @@
android:id="@+id/user_switcher_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginBottom="64dp"
+ android:layout_marginBottom="40dp"
android:layout_marginEnd="60dp"
android:layout_marginStart="60dp">
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 5d252fd..b983545 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -754,11 +754,10 @@
<item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
</style>
- <style name="Theme.CreateUser" parent="@style/Theme.SystemUI">
+ <style name="Theme.CreateUser" parent="@android:style/Theme.DeviceDefault.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">#33000000</item>
- <item name="android:windowActionBar">false</item>
- <item name="android:windowNoTitle">true</item>
+ <item name="android:windowFullscreen">true</item>
</style>
<style name="Theme.PeopleTileConfigActivity" parent="@style/Theme.SystemUI">
diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
new file mode 100644
index 0000000..6bec8aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.annotation.Dimension
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Matrix
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.Region
+import android.util.AttributeSet
+import android.view.Display
+import android.view.DisplayCutout
+import android.view.DisplayInfo
+import android.view.Surface
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView
+import com.android.systemui.animation.Interpolators
+
+/**
+ * A class that handles common actions of display cutout view.
+ * - Draws cutouts.
+ * - Handles camera protection.
+ * - Intercepts touches on cutout areas.
+ */
+open class DisplayCutoutBaseView : View, RegionInterceptableView {
+
+ private val shouldDrawCutout: Boolean = DisplayCutout.getFillBuiltInDisplayCutout(
+ context.resources, context.display?.uniqueId)
+ private var displayMode: Display.Mode? = null
+ private val location = IntArray(2)
+ protected var displayRotation = 0
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ @JvmField val displayInfo = DisplayInfo()
+ @JvmField protected var pendingRotationChange = false
+ @JvmField protected val paint = Paint()
+ @JvmField protected val cutoutPath = Path()
+
+ @JvmField protected var showProtection = false
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ @JvmField val protectionRect: RectF = RectF()
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ @JvmField val protectionPath: Path = Path()
+ private val protectionRectOrig: RectF = RectF()
+ private val protectionPathOrig: Path = Path()
+ private var cameraProtectionProgress: Float = HIDDEN_CAMERA_PROTECTION_SCALE
+ private var cameraProtectionAnimator: ValueAnimator? = null
+
+ constructor(context: Context) : super(context)
+
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+ constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
+ : super(context, attrs, defStyleAttr)
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ updateCutout()
+ }
+
+ fun onDisplayChanged(displayId: Int) {
+ val oldMode: Display.Mode? = displayMode
+ displayMode = display.mode
+
+ // Skip if display mode or cutout hasn't changed.
+ if (!displayModeChanged(oldMode, displayMode) &&
+ display.cutout == displayInfo.displayCutout) {
+ return
+ }
+ if (displayId == display.displayId) {
+ updateCutout()
+ updateProtectionBoundingPath()
+ }
+ }
+
+ open fun updateRotation(rotation: Int) {
+ displayRotation = rotation
+ updateCutout()
+ updateProtectionBoundingPath()
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ public override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+ if (!shouldDrawCutout) {
+ return
+ }
+ canvas.save()
+ getLocationOnScreen(location)
+ canvas.translate(-location[0].toFloat(), -location[1].toFloat())
+
+ drawCutouts(canvas)
+ drawCutoutProtection(canvas)
+ canvas.restore()
+ }
+
+ override fun shouldInterceptTouch(): Boolean {
+ return displayInfo.displayCutout != null && visibility == VISIBLE && shouldDrawCutout
+ }
+
+ override fun getInterceptRegion(): Region? {
+ displayInfo.displayCutout ?: return null
+
+ val cutoutBounds: Region = rectsToRegion(displayInfo.displayCutout?.boundingRects)
+ // Transform to window's coordinate space
+ rootView.getLocationOnScreen(location)
+ cutoutBounds.translate(-location[0], -location[1])
+
+ // Intersect with window's frame
+ cutoutBounds.op(rootView.left, rootView.top, rootView.right, rootView.bottom,
+ Region.Op.INTERSECT)
+ return cutoutBounds
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ open fun updateCutout() {
+ if (pendingRotationChange) {
+ return
+ }
+ cutoutPath.reset()
+ display.getDisplayInfo(displayInfo)
+ displayInfo.displayCutout?.cutoutPath?.let { path -> cutoutPath.set(path) }
+ invalidate()
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ open fun drawCutouts(canvas: Canvas) {
+ displayInfo.displayCutout?.cutoutPath ?: return
+ canvas.drawPath(cutoutPath, paint)
+ }
+
+ protected open fun drawCutoutProtection(canvas: Canvas) {
+ if (cameraProtectionProgress > HIDDEN_CAMERA_PROTECTION_SCALE &&
+ !protectionRect.isEmpty) {
+ canvas.scale(cameraProtectionProgress, cameraProtectionProgress,
+ protectionRect.centerX(), protectionRect.centerY())
+ canvas.drawPath(protectionPath, paint)
+ }
+ }
+
+ /**
+ * Converts a set of [Rect]s into a [Region]
+ */
+ fun rectsToRegion(rects: List<Rect?>?): Region {
+ val result = Region.obtain()
+ if (rects != null) {
+ for (r in rects) {
+ if (r != null && !r.isEmpty) {
+ result.op(r, Region.Op.UNION)
+ }
+ }
+ }
+ return result
+ }
+
+ open fun enableShowProtection(show: Boolean) {
+ if (showProtection == show) {
+ return
+ }
+ showProtection = show
+ updateProtectionBoundingPath()
+ // Delay the relayout until the end of the animation when hiding the cutout,
+ // otherwise we'd clip it.
+ if (showProtection) {
+ requestLayout()
+ }
+ cameraProtectionAnimator?.cancel()
+ cameraProtectionAnimator = ValueAnimator.ofFloat(cameraProtectionProgress,
+ if (showProtection) 1.0f else HIDDEN_CAMERA_PROTECTION_SCALE).setDuration(750)
+ cameraProtectionAnimator?.interpolator = Interpolators.DECELERATE_QUINT
+ cameraProtectionAnimator?.addUpdateListener(ValueAnimator.AnimatorUpdateListener {
+ animation: ValueAnimator ->
+ cameraProtectionProgress = animation.animatedValue as Float
+ invalidate()
+ })
+ cameraProtectionAnimator?.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ cameraProtectionAnimator = null
+ if (!showProtection) {
+ requestLayout()
+ }
+ }
+ })
+ cameraProtectionAnimator?.start()
+ }
+
+ open fun setProtection(path: Path, pathBounds: Rect) {
+ protectionPathOrig.reset()
+ protectionPathOrig.set(path)
+ protectionPath.reset()
+ protectionRectOrig.setEmpty()
+ protectionRectOrig.set(pathBounds)
+ protectionRect.setEmpty()
+ }
+
+ protected open fun updateProtectionBoundingPath() {
+ if (pendingRotationChange) {
+ return
+ }
+ val lw: Int = displayInfo.logicalWidth
+ val lh: Int = displayInfo.logicalHeight
+ val flipped = (displayInfo.rotation == Surface.ROTATION_90 ||
+ displayInfo.rotation == Surface.ROTATION_270)
+ val dw = if (flipped) lh else lw
+ val dh = if (flipped) lw else lh
+ val m = Matrix()
+ transformPhysicalToLogicalCoordinates(displayInfo.rotation, dw, dh, m)
+ if (!protectionPathOrig.isEmpty) {
+ // Reset the protection path so we don't aggregate rotations
+ protectionPath.set(protectionPathOrig)
+ protectionPath.transform(m)
+ m.mapRect(protectionRect, protectionRectOrig)
+ }
+ }
+
+ private fun displayModeChanged(oldMode: Display.Mode?, newMode: Display.Mode?): Boolean {
+ if (oldMode == null) {
+ return true
+ }
+
+ // We purposely ignore refresh rate and id changes here, because we don't need to
+ // invalidate for those, and they can trigger the refresh rate to increase
+ return oldMode?.physicalHeight != newMode?.physicalHeight ||
+ oldMode?.physicalWidth != newMode?.physicalWidth
+ }
+
+ companion object {
+ private const val HIDDEN_CAMERA_PROTECTION_SCALE = 0.5f
+
+ @JvmStatic protected fun transformPhysicalToLogicalCoordinates(
+ @Surface.Rotation rotation: Int,
+ @Dimension physicalWidth: Int,
+ @Dimension physicalHeight: Int,
+ out: Matrix
+ ) {
+ when (rotation) {
+ Surface.ROTATION_0 -> out.reset()
+ Surface.ROTATION_90 -> {
+ out.setRotate(270f)
+ out.postTranslate(0f, physicalWidth.toFloat())
+ }
+ Surface.ROTATION_180 -> {
+ out.setRotate(180f)
+ out.postTranslate(physicalWidth.toFloat(), physicalHeight.toFloat())
+ }
+ Surface.ROTATION_270 -> {
+ out.setRotate(90f)
+ out.postTranslate(physicalHeight.toFloat(), 0f)
+ }
+ else -> throw IllegalArgumentException("Unknown rotation: $rotation")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
new file mode 100644
index 0000000..ee1d9a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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
+
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorFilter
+import android.graphics.Paint
+import android.graphics.PixelFormat
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.graphics.PorterDuffXfermode
+import android.graphics.drawable.Drawable
+import android.hardware.graphics.common.AlphaInterpretation
+import android.hardware.graphics.common.DisplayDecorationSupport
+import android.view.RoundedCorner
+import android.view.RoundedCorners
+
+/**
+ * When the HWC of the device supports Composition.DISPLAY_DECORATON, we use this layer to draw
+ * screen decorations.
+ */
+class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDecorationSupport)
+ : DisplayCutoutBaseView(context) {
+ public val colorMode: Int
+ private val useInvertedAlphaColor: Boolean
+ private val color: Int
+ private val bgColor: Int
+ private val cornerFilter: ColorFilter
+ private val cornerBgFilter: ColorFilter
+ private val clearPaint: Paint
+
+ private var roundedCornerTopSize = 0
+ private var roundedCornerBottomSize = 0
+ private var roundedCornerDrawableTop: Drawable? = null
+ private var roundedCornerDrawableBottom: Drawable? = null
+
+ init {
+ if (displayDecorationSupport.format != PixelFormat.R_8) {
+ throw IllegalArgumentException("Attempting to use unsupported mode " +
+ "${PixelFormat.formatToString(displayDecorationSupport.format)}")
+ }
+ if (DEBUG_COLOR) {
+ color = Color.GREEN
+ bgColor = Color.TRANSPARENT
+ colorMode = ActivityInfo.COLOR_MODE_DEFAULT
+ useInvertedAlphaColor = false
+ } else {
+ colorMode = ActivityInfo.COLOR_MODE_A8
+ useInvertedAlphaColor = displayDecorationSupport.alphaInterpretation ==
+ AlphaInterpretation.COVERAGE
+ if (useInvertedAlphaColor) {
+ color = Color.TRANSPARENT
+ bgColor = Color.BLACK
+ } else {
+ color = Color.BLACK
+ bgColor = Color.TRANSPARENT
+ }
+ }
+ cornerFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
+ cornerBgFilter = PorterDuffColorFilter(bgColor, PorterDuff.Mode.SRC_OUT)
+
+ clearPaint = Paint()
+ clearPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ viewRootImpl.setDisplayDecoration(true)
+
+ if (useInvertedAlphaColor) {
+ paint.set(clearPaint)
+ } else {
+ paint.color = color
+ paint.style = Paint.Style.FILL
+ }
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ if (useInvertedAlphaColor) {
+ canvas.drawColor(bgColor)
+ }
+ // Cutouts are drawn in DisplayCutoutBaseView.onDraw()
+ super.onDraw(canvas)
+ drawRoundedCorners(canvas)
+ }
+
+ private fun drawRoundedCorners(canvas: Canvas) {
+ if (roundedCornerTopSize == 0 && roundedCornerBottomSize == 0) {
+ return
+ }
+ var degree: Int
+ for (i in RoundedCorner.POSITION_TOP_LEFT
+ until RoundedCorners.ROUNDED_CORNER_POSITION_LENGTH) {
+ canvas.save()
+ degree = getRoundedCornerRotationDegree(90 * i)
+ canvas.rotate(degree.toFloat())
+ canvas.translate(
+ getRoundedCornerTranslationX(degree).toFloat(),
+ getRoundedCornerTranslationY(degree).toFloat())
+ if (i == RoundedCorner.POSITION_TOP_LEFT || i == RoundedCorner.POSITION_TOP_RIGHT) {
+ drawRoundedCorner(canvas, roundedCornerDrawableTop, roundedCornerTopSize)
+ } else {
+ drawRoundedCorner(canvas, roundedCornerDrawableBottom, roundedCornerBottomSize)
+ }
+ canvas.restore()
+ }
+ }
+
+ private fun drawRoundedCorner(canvas: Canvas, drawable: Drawable?, size: Int) {
+ if (useInvertedAlphaColor) {
+ canvas.drawRect(0f, 0f, size.toFloat(), size.toFloat(), clearPaint)
+ drawable?.colorFilter = cornerBgFilter
+ } else {
+ drawable?.colorFilter = cornerFilter
+ }
+ drawable?.draw(canvas)
+ // Clear color filter when we are done with drawing.
+ drawable?.clearColorFilter()
+ }
+
+ private fun getRoundedCornerRotationDegree(defaultDegree: Int): Int {
+ return (defaultDegree - 90 * displayRotation + 360) % 360
+ }
+
+ private fun getRoundedCornerTranslationX(degree: Int): Int {
+ return when (degree) {
+ 0, 90 -> 0
+ 180 -> -width
+ 270 -> -height
+ else -> throw IllegalArgumentException("Incorrect degree: $degree")
+ }
+ }
+
+ private fun getRoundedCornerTranslationY(degree: Int): Int {
+ return when (degree) {
+ 0, 270 -> 0
+ 90 -> -width
+ 180 -> -height
+ else -> throw IllegalArgumentException("Incorrect degree: $degree")
+ }
+ }
+
+ /**
+ * Update the rounded corner drawables.
+ */
+ fun updateRoundedCornerDrawable(top: Drawable, bottom: Drawable) {
+ roundedCornerDrawableTop = top
+ roundedCornerDrawableBottom = bottom
+ updateRoundedCornerDrawableBounds()
+ invalidate()
+ }
+
+ /**
+ * Update the rounded corner size.
+ */
+ fun updateRoundedCornerSize(top: Int, bottom: Int) {
+ roundedCornerTopSize = top
+ roundedCornerBottomSize = bottom
+ updateRoundedCornerDrawableBounds()
+ invalidate()
+ }
+
+ private fun updateRoundedCornerDrawableBounds() {
+ if (roundedCornerDrawableTop != null) {
+ roundedCornerDrawableTop?.setBounds(0, 0, roundedCornerTopSize,
+ roundedCornerTopSize)
+ }
+ if (roundedCornerDrawableBottom != null) {
+ roundedCornerDrawableBottom?.setBounds(0, 0, roundedCornerBottomSize,
+ roundedCornerBottomSize)
+ }
+ invalidate()
+ }
+
+ companion object {
+ private val DEBUG_COLOR = ScreenDecorations.DEBUG_COLOR
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 783415e..2ec5f4f 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -14,24 +14,17 @@
package com.android.systemui;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH;
import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.Dimension;
import android.annotation.IdRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -40,11 +33,11 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
@@ -52,10 +45,10 @@
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
+import android.hardware.graphics.common.AlphaInterpretation;
+import android.hardware.graphics.common.DisplayDecorationSupport;
import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -63,14 +56,11 @@
import android.util.DisplayMetrics;
import android.util.DisplayUtils;
import android.util.Log;
-import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayCutout.BoundsPosition;
-import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.RoundedCorners;
-import android.view.Surface;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
@@ -83,8 +73,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -106,6 +94,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -117,7 +106,7 @@
* for antialiasing and emulation purposes.
*/
@SysUISingleton
-public class ScreenDecorations extends CoreStartable implements Tunable , Dumpable{
+public class ScreenDecorations extends CoreStartable implements Tunable , Dumpable {
private static final boolean DEBUG = false;
private static final String TAG = "ScreenDecorations";
@@ -129,7 +118,7 @@
private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
private static final boolean VERBOSE = false;
- private static final boolean DEBUG_COLOR = DEBUG_SCREENSHOT_ROUNDED_CORNERS;
+ static final boolean DEBUG_COLOR = DEBUG_SCREENSHOT_ROUNDED_CORNERS;
private DisplayManager mDisplayManager;
@VisibleForTesting
@@ -138,7 +127,8 @@
private final Executor mMainExecutor;
private final TunerService mTunerService;
private final SecureSettings mSecureSettings;
- private DisplayManager.DisplayListener mDisplayListener;
+ @VisibleForTesting
+ DisplayManager.DisplayListener mDisplayListener;
private CameraAvailabilityListener mCameraListener;
private final UserTracker mUserTracker;
private final PrivacyDotViewController mDotViewController;
@@ -158,23 +148,36 @@
protected OverlayWindow[] mOverlays = null;
@Nullable
private DisplayCutoutView[] mCutoutViews;
+ @VisibleForTesting
+ ViewGroup mScreenDecorHwcWindow;
+ @VisibleForTesting
+ ScreenDecorHwcLayer mScreenDecorHwcLayer;
private float mDensity;
private WindowManager mWindowManager;
private int mRotation;
private SettingObserver mColorInversionSetting;
private DelayableExecutor mExecutor;
private Handler mHandler;
- private boolean mPendingRotationChange;
+ boolean mPendingRotationChange;
private boolean mIsRoundedCornerMultipleRadius;
private Drawable mRoundedCornerDrawable;
private Drawable mRoundedCornerDrawableTop;
private Drawable mRoundedCornerDrawableBottom;
- private String mDisplayUniqueId;
+ @VisibleForTesting
+ String mDisplayUniqueId;
+ private int mTintColor = Color.BLACK;
+ @VisibleForTesting
+ protected DisplayDecorationSupport mHwcScreenDecorationSupport;
private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback =
new CameraAvailabilityListener.CameraTransitionCallback() {
@Override
public void onApplyCameraProtection(@NonNull Path protectionPath, @NonNull Rect bounds) {
+ if (mScreenDecorHwcLayer != null) {
+ mScreenDecorHwcLayer.setProtection(protectionPath, bounds);
+ mScreenDecorHwcLayer.enableShowProtection(true);
+ return;
+ }
if (mCutoutViews == null) {
Log.w(TAG, "DisplayCutoutView do not initialized");
return;
@@ -184,13 +187,17 @@
// Check Null since not all mCutoutViews[pos] be inflated at the meanwhile
if (dcv != null) {
dcv.setProtection(protectionPath, bounds);
- dcv.setShowProtection(true);
+ dcv.enableShowProtection(true);
}
}
}
@Override
public void onHideCameraProtection() {
+ if (mScreenDecorHwcLayer != null) {
+ mScreenDecorHwcLayer.enableShowProtection(false);
+ return;
+ }
if (mCutoutViews == null) {
Log.w(TAG, "DisplayCutoutView do not initialized");
return;
@@ -199,27 +206,59 @@
for (DisplayCutoutView dcv : mCutoutViews) {
// Check Null since not all mCutoutViews[pos] be inflated at the meanwhile
if (dcv != null) {
- dcv.setShowProtection(false);
+ dcv.enableShowProtection(false);
}
}
}
};
- /**
- * Converts a set of {@link Rect}s into a {@link Region}
- *
- * @hide
- */
- public static Region rectsToRegion(List<Rect> rects) {
- Region result = Region.obtain();
- if (rects != null) {
- for (Rect r : rects) {
- if (r != null && !r.isEmpty()) {
- result.op(r, Region.Op.UNION);
- }
+ private PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener =
+ new PrivacyDotViewController.ShowingListener() {
+ @Override
+ public void onPrivacyDotShown(@Nullable View v) {
+ // We don't need to control the window visibility when the hwc doesn't support screen
+ // decoration since the overlay windows are always visible in this case.
+ if (mHwcScreenDecorationSupport == null || v == null) {
+ return;
}
+ mExecutor.execute(() -> {
+ for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
+ if (mOverlays[i] == null) {
+ continue;
+ }
+ final ViewGroup overlayView = mOverlays[i].getRootView();
+ if (overlayView.findViewById(v.getId()) != null) {
+ overlayView.setVisibility(View.VISIBLE);
+ }
+ }
+ });
}
- return result;
+
+ @Override
+ public void onPrivacyDotHidden(@Nullable View v) {
+ // We don't need to control the window visibility when the hwc doesn't support screen
+ // decoration since the overlay windows are always visible in this case.
+ if (mHwcScreenDecorationSupport == null || v == null) {
+ return;
+ }
+ mExecutor.execute(() -> {
+ for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
+ if (mOverlays[i] == null) {
+ continue;
+ }
+ final ViewGroup overlayView = mOverlays[i].getRootView();
+ if (overlayView.findViewById(v.getId()) != null) {
+ overlayView.setVisibility(View.INVISIBLE);
+ }
+ }
+ });
+ }
+ };
+
+ private static boolean eq(DisplayDecorationSupport a, DisplayDecorationSupport b) {
+ if (a == null) return (b == null);
+ if (b == null) return false;
+ return a.format == b.format && a.alphaInterpretation == b.alphaInterpretation;
}
@Inject
@@ -241,6 +280,7 @@
mDotViewController = dotViewController;
mThreadFactory = threadFactory;
mDotFactory = dotFactory;
+ dotViewController.setShowingListener(mPrivacyDotShowingListener);
}
@Override
@@ -265,6 +305,7 @@
mIsRoundedCornerMultipleRadius = isRoundedCornerMultipleRadius(mContext, mDisplayUniqueId);
mWindowManager = mContext.getSystemService(WindowManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport();
updateRoundedCornerDrawable();
updateRoundedCornerRadii();
setupDecorations();
@@ -305,15 +346,36 @@
new RestartingPreDrawListener(overlayView, i, newRotation));
}
}
+
+ if (mScreenDecorHwcWindow != null) {
+ mScreenDecorHwcWindow.getViewTreeObserver().addOnPreDrawListener(
+ new RestartingPreDrawListener(
+ mScreenDecorHwcWindow,
+ -1, // Pass -1 for views with no specific position.
+ newRotation));
+ }
}
+
final String newUniqueId = mContext.getDisplay().getUniqueId();
- if ((newUniqueId != null && !newUniqueId.equals(mDisplayUniqueId))
- || (mDisplayUniqueId != null && !mDisplayUniqueId.equals(newUniqueId))) {
+ if (!Objects.equals(newUniqueId, mDisplayUniqueId)) {
mDisplayUniqueId = newUniqueId;
mIsRoundedCornerMultipleRadius =
isRoundedCornerMultipleRadius(mContext, mDisplayUniqueId);
+ final DisplayDecorationSupport newScreenDecorationSupport =
+ mContext.getDisplay().getDisplayDecorationSupport();
+ // When the value of mSupportHwcScreenDecoration is changed, re-setup the whole
+ // screen decoration.
+ if (!eq(newScreenDecorationSupport, mHwcScreenDecorationSupport)) {
+ mHwcScreenDecorationSupport = newScreenDecorationSupport;
+ removeAllOverlays();
+ setupDecorations();
+ return;
+ }
updateRoundedCornerDrawable();
}
+ if (mScreenDecorHwcLayer != null) {
+ mScreenDecorHwcLayer.onDisplayChanged(displayId);
+ }
updateOrientation();
}
};
@@ -359,6 +421,11 @@
List<DecorProvider> decorProviders = mDotFactory.getProviders();
if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) {
+ if (mHwcScreenDecorationSupport != null) {
+ createHwcOverlay();
+ } else {
+ removeHwcOverlay();
+ }
final DisplayCutout cutout = getCutout();
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout)
@@ -383,14 +450,15 @@
}
} else {
removeAllOverlays();
+ removeHwcOverlay();
}
- if (hasOverlays()) {
+ if (hasOverlays() || hasHwcOverlay()) {
if (mIsRegistered) {
return;
}
DisplayMetrics metrics = new DisplayMetrics();
- mDisplayManager.getDisplay(DEFAULT_DISPLAY).getMetrics(metrics);
+ mContext.getDisplay().getMetrics(metrics);
mDensity = metrics.density;
mMainExecutor.execute(() -> mTunerService.addTunable(this, SIZE));
@@ -475,25 +543,27 @@
mOverlays = new OverlayWindow[BOUNDS_POSITION_LENGTH];
}
- if (mCutoutViews == null) {
- mCutoutViews = new DisplayCutoutView[BOUNDS_POSITION_LENGTH];
- }
-
if (mOverlays[pos] != null) {
return;
}
mOverlays[pos] = overlayForPosition(pos, decorProviders);
-
- mCutoutViews[pos] = new DisplayCutoutView(mContext, pos, this);
- mOverlays[pos].getRootView().addView(mCutoutViews[pos]);
-
final ViewGroup overlayView = mOverlays[pos].getRootView();
overlayView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
overlayView.setAlpha(0);
overlayView.setForceDarkAllowed(false);
- updateView(pos, cutout);
+ // Only show cutout and rounded corners in mOverlays when hwc don't support screen
+ // decoration.
+ if (mHwcScreenDecorationSupport == null) {
+ if (mCutoutViews == null) {
+ mCutoutViews = new DisplayCutoutView[BOUNDS_POSITION_LENGTH];
+ }
+ mCutoutViews[pos] = new DisplayCutoutView(mContext, pos);
+ mCutoutViews[pos].setColor(mTintColor);
+ overlayView.addView(mCutoutViews[pos]);
+ updateView(pos, cutout);
+ }
mWindowManager.addView(overlayView, getWindowLayoutParams(pos));
@@ -509,8 +579,37 @@
}
});
- mOverlays[pos].getRootView().getViewTreeObserver().addOnPreDrawListener(
- new ValidatingPreDrawListener(mOverlays[pos].getRootView()));
+ overlayView.getRootView().getViewTreeObserver().addOnPreDrawListener(
+ new ValidatingPreDrawListener(overlayView.getRootView()));
+ }
+
+ private boolean hasHwcOverlay() {
+ return mScreenDecorHwcWindow != null;
+ }
+
+ private void removeHwcOverlay() {
+ if (mScreenDecorHwcWindow == null) {
+ return;
+ }
+ mWindowManager.removeViewImmediate(mScreenDecorHwcWindow);
+ mScreenDecorHwcWindow = null;
+ mScreenDecorHwcLayer = null;
+ }
+
+ private void createHwcOverlay() {
+ if (mScreenDecorHwcWindow != null) {
+ return;
+ }
+ mScreenDecorHwcWindow = (ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.screen_decor_hwc_layer, null);
+ mScreenDecorHwcLayer = new ScreenDecorHwcLayer(mContext, mHwcScreenDecorationSupport);
+ mScreenDecorHwcWindow.addView(mScreenDecorHwcLayer, new FrameLayout.LayoutParams(
+ MATCH_PARENT, MATCH_PARENT, Gravity.TOP | Gravity.START));
+ mWindowManager.addView(mScreenDecorHwcWindow, getHwcWindowLayoutParams());
+ updateRoundedCornerSize(mRoundedDefault, mRoundedDefaultTop, mRoundedDefaultBottom);
+ updateRoundedCornerImageView();
+ mScreenDecorHwcWindow.getViewTreeObserver().addOnPreDrawListener(
+ new ValidatingPreDrawListener(mScreenDecorHwcWindow));
}
/**
@@ -523,12 +622,18 @@
decorProviders.forEach(provider -> {
removeOverlayView(provider.getViewId());
currentOverlay.addDecorProvider(provider, mRotation);
+ // If the hwc supports screen decoration and privacy dot is enabled, it means there will
+ // be only privacy dot in mOverlay. So set the initial visibility of mOverlays to
+ // INVISIBLE and will only set it to VISIBLE when the privacy dot is showing.
+ if (mHwcScreenDecorationSupport != null) {
+ currentOverlay.getRootView().setVisibility(View.INVISIBLE);
+ }
});
return currentOverlay;
}
private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
- if (mOverlays == null || mOverlays[pos] == null) {
+ if (mOverlays == null || mOverlays[pos] == null || mHwcScreenDecorationSupport != null) {
return;
}
@@ -540,15 +645,34 @@
// update cutout view rotation
if (mCutoutViews != null && mCutoutViews[pos] != null) {
- mCutoutViews[pos].setRotation(mRotation);
+ mCutoutViews[pos].updateRotation(mRotation);
}
}
@VisibleForTesting
WindowManager.LayoutParams getWindowLayoutParams(@BoundsPosition int pos) {
+ final WindowManager.LayoutParams lp = getWindowLayoutBaseParams();
+ lp.width = getWidthLayoutParamByPos(pos);
+ lp.height = getHeightLayoutParamByPos(pos);
+ lp.setTitle(getWindowTitleByPos(pos));
+ lp.gravity = getOverlayWindowGravity(pos);
+ return lp;
+ }
+
+ private WindowManager.LayoutParams getHwcWindowLayoutParams() {
+ final WindowManager.LayoutParams lp = getWindowLayoutBaseParams();
+ lp.width = MATCH_PARENT;
+ lp.height = MATCH_PARENT;
+ lp.setTitle("ScreenDecorHwcOverlay");
+ lp.gravity = Gravity.TOP | Gravity.START;
+ if (!DEBUG_COLOR) {
+ lp.setColorMode(ActivityInfo.COLOR_MODE_A8);
+ }
+ return lp;
+ }
+
+ private WindowManager.LayoutParams getWindowLayoutBaseParams() {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- getWidthLayoutParamByPos(pos),
- getHeightLayoutParamByPos(pos),
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
@@ -566,8 +690,6 @@
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
}
- lp.setTitle(getWindowTitleByPos(pos));
- lp.gravity = getOverlayWindowGravity(pos);
lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
lp.setFitInsetsTypes(0 /* types */);
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
@@ -648,15 +770,19 @@
};
private void updateColorInversion(int colorsInvertedValue) {
- int tint = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
+ mTintColor = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
if (DEBUG_COLOR) {
- tint = Color.RED;
+ mTintColor = Color.RED;
}
- ColorStateList tintList = ColorStateList.valueOf(tint);
- if (mOverlays == null) {
+ // When the hwc supports screen decorations, the layer will use the A8 color mode which
+ // won't be affected by the color inversion. If the composition goes the client composition
+ // route, the color inversion will be handled by the RenderEngine.
+ if (mOverlays == null || mHwcScreenDecorationSupport != null) {
return;
}
+
+ ColorStateList tintList = ColorStateList.valueOf(mTintColor);
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
if (mOverlays[i] == null) {
continue;
@@ -676,7 +802,7 @@
if (child instanceof ImageView) {
((ImageView) child).setImageTintList(tintList);
} else if (child instanceof DisplayCutoutView) {
- ((DisplayCutoutView) child).setColor(tint);
+ ((DisplayCutoutView) child).setColor(mTintColor);
}
}
}
@@ -688,6 +814,7 @@
Log.i(TAG, "ScreenDecorations is disabled");
return;
}
+
mExecutor.execute(() -> {
int oldRotation = mRotation;
mPendingRotationChange = false;
@@ -705,6 +832,14 @@
});
}
+ private static String alphaInterpretationToString(int alpha) {
+ switch (alpha) {
+ case AlphaInterpretation.COVERAGE: return "COVERAGE";
+ case AlphaInterpretation.MASK: return "MASK";
+ default: return "Unknown: " + alpha;
+ }
+ }
+
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("ScreenDecorations state:");
@@ -712,6 +847,15 @@
pw.println(" mIsRoundedCornerMultipleRadius:" + mIsRoundedCornerMultipleRadius);
pw.println(" mIsPrivacyDotEnabled:" + isPrivacyDotEnabled());
pw.println(" mPendingRotationChange:" + mPendingRotationChange);
+ pw.println(" mHwcScreenDecorationSupport:");
+ if (mHwcScreenDecorationSupport == null) {
+ pw.println(" null");
+ } else {
+ pw.println(" format: "
+ + PixelFormat.formatToString(mHwcScreenDecorationSupport.format));
+ pw.println(" alphaInterpretation: "
+ + alphaInterpretationToString(mHwcScreenDecorationSupport.alphaInterpretation));
+ }
pw.println(" mRoundedDefault(x,y)=(" + mRoundedDefault.x + "," + mRoundedDefault.y + ")");
pw.println(" mRoundedDefaultTop(x,y)=(" + mRoundedDefaultTop.x + "," + mRoundedDefaultTop.y
+ ")");
@@ -739,7 +883,10 @@
}
if (newRotation != mRotation) {
mRotation = newRotation;
-
+ if (mScreenDecorHwcLayer != null) {
+ mScreenDecorHwcLayer.pendingRotationChange = false;
+ mScreenDecorHwcLayer.updateRotation(mRotation);
+ }
if (mOverlays != null) {
updateLayoutParams();
final DisplayCutout cutout = getCutout();
@@ -956,7 +1103,8 @@
private boolean shouldShowRoundedCorner(@BoundsPosition int pos,
@Nullable DisplayCutout cutout) {
- return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout);
+ return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout)
+ && mHwcScreenDecorationSupport == null;
}
private boolean shouldShowPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
@@ -966,7 +1114,8 @@
private boolean shouldShowCutout(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
- return (bounds != null && !bounds[rotatedPos].isEmpty());
+ return (bounds != null && !bounds[rotatedPos].isEmpty()
+ && mHwcScreenDecorationSupport == null);
}
private boolean shouldDrawCutout() {
@@ -1027,14 +1176,22 @@
final Drawable bottom = mRoundedCornerDrawableBottom != null
? mRoundedCornerDrawableBottom : mRoundedCornerDrawable;
+ if (mScreenDecorHwcLayer != null) {
+ mScreenDecorHwcLayer.updateRoundedCornerDrawable(top, bottom);
+ return;
+ }
+
if (mOverlays == null) {
return;
}
+ final ColorStateList colorStateList = ColorStateList.valueOf(mTintColor);
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
if (mOverlays[i] == null) {
continue;
}
final ViewGroup overlayView = mOverlays[i].getRootView();
+ ((ImageView) overlayView.findViewById(R.id.left)).setImageTintList(colorStateList);
+ ((ImageView) overlayView.findViewById(R.id.right)).setImageTintList(colorStateList);
((ImageView) overlayView.findViewById(R.id.left)).setImageDrawable(
isTopRoundedCorner(i, R.id.left) ? top : bottom);
((ImageView) overlayView.findViewById(R.id.right)).setImageDrawable(
@@ -1065,9 +1222,6 @@
Point sizeDefault,
Point sizeTop,
Point sizeBottom) {
- if (mOverlays == null) {
- return;
- }
if (sizeTop.x == 0) {
sizeTop = sizeDefault;
}
@@ -1075,6 +1229,14 @@
sizeBottom = sizeDefault;
}
+ if (mScreenDecorHwcLayer != null) {
+ mScreenDecorHwcLayer.updateRoundedCornerSize(sizeTop.x, sizeBottom.x);
+ return;
+ }
+
+ if (mOverlays == null) {
+ return;
+ }
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
if (mOverlays[i] == null) {
continue;
@@ -1095,40 +1257,21 @@
view.setLayoutParams(params);
}
- public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener,
- RegionInterceptableView {
-
- private static final float HIDDEN_CAMERA_PROTECTION_SCALE = 0.5f;
-
- private Display.Mode mDisplayMode = null;
- private final DisplayInfo mInfo = new DisplayInfo();
- private final Paint mPaint = new Paint();
+ public static class DisplayCutoutView extends DisplayCutoutBaseView {
private final List<Rect> mBounds = new ArrayList();
private final Rect mBoundingRect = new Rect();
- private final Path mBoundingPath = new Path();
- // Don't initialize these yet because they may never exist
- private RectF mProtectionRect;
- private RectF mProtectionRectOrig;
- private Path mProtectionPath;
- private Path mProtectionPathOrig;
private Rect mTotalBounds = new Rect();
- // Whether or not to show the cutout protection path
- private boolean mShowProtection = false;
- private final int[] mLocation = new int[2];
- private final ScreenDecorations mDecorations;
private int mColor = Color.BLACK;
private int mRotation;
private int mInitialPosition;
private int mPosition;
- private float mCameraProtectionProgress = HIDDEN_CAMERA_PROTECTION_SCALE;
- private ValueAnimator mCameraProtectionAnimator;
- public DisplayCutoutView(Context context, @BoundsPosition int pos,
- ScreenDecorations decorations) {
+ public DisplayCutoutView(Context context, @BoundsPosition int pos) {
super(context);
mInitialPosition = pos;
- mDecorations = decorations;
+ paint.setColor(mColor);
+ paint.setStyle(Paint.Style.FILL);
setId(R.id.display_cutout);
if (DEBUG) {
getViewTreeObserver().addOnDrawListener(() -> Log.i(TAG,
@@ -1138,145 +1281,31 @@
public void setColor(int color) {
mColor = color;
+ paint.setColor(mColor);
invalidate();
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
- getHandler());
- update();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- getLocationOnScreen(mLocation);
- canvas.translate(-mLocation[0], -mLocation[1]);
-
- if (!mBoundingPath.isEmpty()) {
- mPaint.setColor(mColor);
- mPaint.setStyle(Paint.Style.FILL);
- mPaint.setAntiAlias(true);
- canvas.drawPath(mBoundingPath, mPaint);
- }
- if (mCameraProtectionProgress > HIDDEN_CAMERA_PROTECTION_SCALE
- && !mProtectionRect.isEmpty()) {
- canvas.scale(mCameraProtectionProgress, mCameraProtectionProgress,
- mProtectionRect.centerX(), mProtectionRect.centerY());
- canvas.drawPath(mProtectionPath, mPaint);
- }
- }
-
- @Override
- public void onDisplayAdded(int displayId) {
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- Display.Mode oldMode = mDisplayMode;
- mDisplayMode = getDisplay().getMode();
-
- // Display mode hasn't meaningfully changed, we can ignore it
- if (!modeChanged(oldMode, mDisplayMode)) {
- return;
- }
-
- if (displayId == getDisplay().getDisplayId()) {
- update();
- }
- }
-
- private boolean modeChanged(Display.Mode oldMode, Display.Mode newMode) {
- if (oldMode == null) {
- return true;
- }
-
- boolean changed = false;
- changed |= oldMode.getPhysicalHeight() != newMode.getPhysicalHeight();
- changed |= oldMode.getPhysicalWidth() != newMode.getPhysicalWidth();
- // We purposely ignore refresh rate and id changes here, because we don't need to
- // invalidate for those, and they can trigger the refresh rate to increase
-
- return changed;
- }
-
- public void setRotation(int rotation) {
+ public void updateRotation(int rotation) {
mRotation = rotation;
- update();
+ updateCutout();
}
- void setProtection(Path protectionPath, Rect pathBounds) {
- if (mProtectionPathOrig == null) {
- mProtectionPathOrig = new Path();
- mProtectionPath = new Path();
- }
- mProtectionPathOrig.set(protectionPath);
- if (mProtectionRectOrig == null) {
- mProtectionRectOrig = new RectF();
- mProtectionRect = new RectF();
- }
- mProtectionRectOrig.set(pathBounds);
- }
-
- void setShowProtection(boolean shouldShow) {
- if (mShowProtection == shouldShow) {
- return;
- }
-
- mShowProtection = shouldShow;
- updateBoundingPath();
- // Delay the relayout until the end of the animation when hiding the cutout,
- // otherwise we'd clip it.
- if (mShowProtection) {
- requestLayout();
- }
- if (mCameraProtectionAnimator != null) {
- mCameraProtectionAnimator.cancel();
- }
- mCameraProtectionAnimator = ValueAnimator.ofFloat(mCameraProtectionProgress,
- mShowProtection ? 1.0f : HIDDEN_CAMERA_PROTECTION_SCALE).setDuration(750);
- mCameraProtectionAnimator.setInterpolator(Interpolators.DECELERATE_QUINT);
- mCameraProtectionAnimator.addUpdateListener(animation -> {
- mCameraProtectionProgress = (float) animation.getAnimatedValue();
- invalidate();
- });
- mCameraProtectionAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mCameraProtectionAnimator = null;
- if (!mShowProtection) {
- requestLayout();
- }
- }
- });
- mCameraProtectionAnimator.start();
- }
-
- private void update() {
- if (!isAttachedToWindow() || mDecorations.mPendingRotationChange) {
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ @Override
+ public void updateCutout() {
+ if (!isAttachedToWindow() || pendingRotationChange) {
return;
}
mPosition = getBoundPositionFromRotation(mInitialPosition, mRotation);
requestLayout();
- getDisplay().getDisplayInfo(mInfo);
+ getDisplay().getDisplayInfo(displayInfo);
mBounds.clear();
mBoundingRect.setEmpty();
- mBoundingPath.reset();
+ cutoutPath.reset();
int newVisible;
if (shouldDrawCutout(getContext()) && hasCutout()) {
- mBounds.addAll(mInfo.displayCutout.getBoundingRects());
+ mBounds.addAll(displayInfo.displayCutout.getBoundingRects());
localBounds(mBoundingRect);
updateGravity();
updateBoundingPath();
@@ -1291,10 +1320,11 @@
}
private void updateBoundingPath() {
- int lw = mInfo.logicalWidth;
- int lh = mInfo.logicalHeight;
+ int lw = displayInfo.logicalWidth;
+ int lh = displayInfo.logicalHeight;
- boolean flipped = mInfo.rotation == ROTATION_90 || mInfo.rotation == ROTATION_270;
+ boolean flipped = displayInfo.rotation == ROTATION_90
+ || displayInfo.rotation == ROTATION_270;
int dw = flipped ? lh : lw;
int dh = flipped ? lw : lh;
@@ -1302,49 +1332,20 @@
Path path = DisplayCutout.pathFromResources(
getResources(), getDisplay().getUniqueId(), dw, dh);
if (path != null) {
- mBoundingPath.set(path);
+ cutoutPath.set(path);
} else {
- mBoundingPath.reset();
+ cutoutPath.reset();
}
Matrix m = new Matrix();
- transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m);
- mBoundingPath.transform(m);
- if (mProtectionPathOrig != null) {
- // Reset the protection path so we don't aggregate rotations
- mProtectionPath.set(mProtectionPathOrig);
- mProtectionPath.transform(m);
- m.mapRect(mProtectionRect, mProtectionRectOrig);
- }
- }
-
- private static void transformPhysicalToLogicalCoordinates(@Surface.Rotation int rotation,
- @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
- switch (rotation) {
- case ROTATION_0:
- out.reset();
- break;
- case ROTATION_90:
- out.setRotate(270);
- out.postTranslate(0, physicalWidth);
- break;
- case ROTATION_180:
- out.setRotate(180);
- out.postTranslate(physicalWidth, physicalHeight);
- break;
- case ROTATION_270:
- out.setRotate(90);
- out.postTranslate(physicalHeight, 0);
- break;
- default:
- throw new IllegalArgumentException("Unknown rotation: " + rotation);
- }
+ transformPhysicalToLogicalCoordinates(displayInfo.rotation, dw, dh, m);
+ cutoutPath.transform(m);
}
private void updateGravity() {
LayoutParams lp = getLayoutParams();
if (lp instanceof FrameLayout.LayoutParams) {
FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) lp;
- int newGravity = getGravity(mInfo.displayCutout);
+ int newGravity = getGravity(displayInfo.displayCutout);
if (flp.gravity != newGravity) {
flp.gravity = newGravity;
setLayoutParams(flp);
@@ -1353,7 +1354,7 @@
}
private boolean hasCutout() {
- final DisplayCutout displayCutout = mInfo.displayCutout;
+ final DisplayCutout displayCutout = displayInfo.displayCutout;
if (displayCutout == null) {
return false;
}
@@ -1377,11 +1378,11 @@
return;
}
- if (mShowProtection) {
+ if (showProtection) {
// Make sure that our measured height encompases the protection
mTotalBounds.union(mBoundingRect);
- mTotalBounds.union((int) mProtectionRect.left, (int) mProtectionRect.top,
- (int) mProtectionRect.right, (int) mProtectionRect.bottom);
+ mTotalBounds.union((int) protectionRect.left, (int) protectionRect.top,
+ (int) protectionRect.right, (int) protectionRect.bottom);
setMeasuredDimension(
resolveSizeAndState(mTotalBounds.width(), widthMeasureSpec, 0),
resolveSizeAndState(mTotalBounds.height(), heightMeasureSpec, 0));
@@ -1413,7 +1414,7 @@
}
private void localBounds(Rect out) {
- DisplayCutout displayCutout = mInfo.displayCutout;
+ DisplayCutout displayCutout = displayInfo.displayCutout;
boundsFromDirection(displayCutout, getGravity(displayCutout), out);
}
@@ -1437,32 +1438,6 @@
}
return Gravity.NO_GRAVITY;
}
-
- @Override
- public boolean shouldInterceptTouch() {
- return mInfo.displayCutout != null && getVisibility() == VISIBLE;
- }
-
- @Override
- public Region getInterceptRegion() {
- if (mInfo.displayCutout == null) {
- return null;
- }
-
- View rootView = getRootView();
- Region cutoutBounds = rectsToRegion(
- mInfo.displayCutout.getBoundingRects());
-
- // Transform to window's coordinate space
- rootView.getLocationOnScreen(mLocation);
- cutoutBounds.translate(-mLocation[0], -mLocation[1]);
-
- // Intersect with window's frame
- cutoutBounds.op(rootView.getLeft(), rootView.getTop(), rootView.getRight(),
- rootView.getBottom(), Region.Op.INTERSECT);
-
- return cutoutBounds;
- }
}
/**
@@ -1473,6 +1448,8 @@
private final View mView;
private final int mTargetRotation;
+ // Pass -1 for ScreenDecorHwcLayer since it's a fullscreen window and has no specific
+ // position.
private final int mPosition;
private RestartingPreDrawListener(View view, @BoundsPosition int position,
@@ -1488,7 +1465,9 @@
if (mTargetRotation == mRotation) {
if (DEBUG) {
- Log.i(TAG, getWindowTitleByPos(mPosition) + " already in target rot "
+ final String title = mPosition < 0 ? "ScreenDecorHwcLayer"
+ : getWindowTitleByPos(mPosition);
+ Log.i(TAG, title + " already in target rot "
+ mTargetRotation + ", allow draw without restarting it");
}
return true;
@@ -1499,7 +1478,9 @@
// take effect.
updateOrientation();
if (DEBUG) {
- Log.i(TAG, getWindowTitleByPos(mPosition)
+ final String title = mPosition < 0 ? "ScreenDecorHwcLayer"
+ : getWindowTitleByPos(mPosition);
+ Log.i(TAG, title
+ " restarting listener fired, restarting draw for rot " + mRotation);
}
mView.invalidate();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index bbe0a99..89735c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -27,6 +27,7 @@
import android.provider.DeviceConfig.NAMESPACE_SYSTEMUI
import android.text.format.DateUtils
import android.util.ArrayMap
+import android.util.IndentingPrintWriter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -38,14 +39,19 @@
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED
+import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.indentIfPossible
import com.android.systemui.util.time.SystemClock
+import java.io.FileDescriptor
+import java.io.PrintWriter
import java.util.Objects
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -60,8 +66,9 @@
private val activityManager: IActivityManager,
private val packageManager: PackageManager,
private val deviceConfigProxy: DeviceConfigProxy,
- private val dialogLaunchAnimator: DialogLaunchAnimator
-) : IForegroundServiceObserver.Stub() {
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val dumpManager: DumpManager
+) : IForegroundServiceObserver.Stub(), Dumpable {
companion object {
private val LOG_TAG = FgsManagerController::class.java.simpleName
@@ -116,6 +123,8 @@
isAvailable = deviceConfigProxy
.getBoolean(NAMESPACE_SYSTEMUI, TASK_MANAGER_ENABLED, true)
+ dumpManager.registerDumpable(this)
+
initialized = true
}
}
@@ -379,6 +388,16 @@
}
override fun hashCode(): Int = Objects.hash(userId, packageName)
+
+ fun dump(pw: PrintWriter) {
+ pw.println("UserPackage: [")
+ pw.indentIfPossible {
+ pw.println("userId=$userId")
+ pw.println("packageName=$packageName")
+ pw.println("uiControl=$uiControl")
+ }
+ pw.println("]")
+ }
}
private data class StartTimeAndTokens(
@@ -398,6 +417,22 @@
fun isEmpty(): Boolean {
return tokens.isEmpty()
}
+
+ fun dump(pw: PrintWriter) {
+ pw.println("StartTimeAndTokens: [")
+ pw.indentIfPossible {
+ pw.println("startTime=$startTime (time running =" +
+ " ${systemClock.elapsedRealtime() - startTime}ms)")
+ pw.println("tokens: [")
+ pw.indentIfPossible {
+ for (token in tokens) {
+ pw.println("$token")
+ }
+ }
+ pw.println("]")
+ }
+ pw.println("]")
+ }
}
private class AppItemViewHolder(parent: View) : RecyclerView.ViewHolder(parent) {
@@ -429,9 +464,54 @@
var appLabel: CharSequence = ""
var icon: Drawable? = null
var stopped = false
+
+ fun dump(pw: PrintWriter, systemClock: SystemClock) {
+ pw.println("RunningApp: [")
+ pw.indentIfPossible {
+ pw.println("userId=$userId")
+ pw.println("packageName=$packageName")
+ pw.println("timeStarted=$timeStarted (time since start =" +
+ " ${systemClock.elapsedRealtime() - timeStarted}ms)\"")
+ pw.println("uiControl=$uiControl")
+ pw.println("appLabel=$appLabel")
+ pw.println("icon=$icon")
+ pw.println("stopped=$stopped")
+ }
+ pw.println("]")
+ }
}
private enum class UIControl {
NORMAL, HIDE_BUTTON, HIDE_ENTRY
}
+
+ override fun dump(fd: FileDescriptor, printwriter: PrintWriter, args: Array<out String>) {
+ val pw = IndentingPrintWriter(printwriter)
+ synchronized(lock) {
+ pw.println("changesSinceDialog=$changesSinceDialog")
+ pw.println("Running service tokens: [")
+ pw.indentIfPossible {
+ runningServiceTokens.forEach { (userPackage, startTimeAndTokens) ->
+ pw.println("{")
+ pw.indentIfPossible {
+ userPackage.dump(pw)
+ startTimeAndTokens.dump(pw)
+ }
+ pw.println("}")
+ }
+ }
+ pw.println("]")
+
+ pw.println("Loaded package UI info: [")
+ pw.indentIfPossible {
+ runningApps.forEach { (userPackage, runningApp) ->
+ pw.println("{")
+ userPackage.dump(pw)
+ runningApp.dump(pw, systemClock)
+ pw.println("}")
+ }
+ }
+ pw.println("]")
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index ea68c40..17f85ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -420,7 +420,6 @@
public void setBrightnessMirrorController(
BrightnessMirrorController brightnessMirrorController) {
mQSPanelController.setBrightnessMirror(brightnessMirrorController);
- mQuickQSPanelController.setBrightnessMirror(brightnessMirrorController);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt
deleted file mode 100644
index 65889d7..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs
-
-import androidx.annotation.VisibleForTesting
-import com.android.systemui.settings.brightness.BrightnessController
-import com.android.systemui.settings.brightness.BrightnessSliderController
-import com.android.systemui.settings.brightness.MirroredBrightnessController
-import com.android.systemui.statusbar.policy.BrightnessMirrorController
-import javax.inject.Inject
-
-/**
- * Controls brightness slider in QQS, which is visible only in split shade. It's responsible for
- * showing/hiding it when appropriate and (un)registering listeners
- */
-class QuickQSBrightnessController @VisibleForTesting constructor(
- private val brightnessControllerFactory: () -> BrightnessController
-) : MirroredBrightnessController {
-
- @Inject constructor(
- brightnessControllerFactory: BrightnessController.Factory,
- brightnessSliderControllerFactory: BrightnessSliderController.Factory,
- quickQSPanel: QuickQSPanel
- ) : this(brightnessControllerFactory = {
- val slider = brightnessSliderControllerFactory.create(quickQSPanel.context,
- quickQSPanel)
- slider.init()
- quickQSPanel.setBrightnessView(slider.rootView)
- brightnessControllerFactory.create(slider)
- })
-
- private var isListening = false
- private var brightnessController: BrightnessController? = null
- private var mirrorController: BrightnessMirrorController? = null
-
- fun init(shouldUseSplitNotificationShade: Boolean) {
- refreshVisibility(shouldUseSplitNotificationShade)
- }
-
- /**
- * Starts/Stops listening for brightness changing events.
- * It's fine to call this function even if slider is not visible (which would be the case for
- * all small screen devices), it will just do nothing in that case
- */
- fun setListening(listening: Boolean) {
- if (listening) {
- // controller can be null when slider was never shown
- if (!isListening && brightnessController != null) {
- brightnessController?.registerCallbacks()
- isListening = true
- }
- } else {
- brightnessController?.unregisterCallbacks()
- isListening = false
- }
- }
-
- fun checkRestrictionAndSetEnabled() {
- brightnessController?.checkRestrictionAndSetEnabled()
- }
-
- fun refreshVisibility(shouldUseSplitNotificationShade: Boolean) {
- if (shouldUseSplitNotificationShade) {
- showBrightnessSlider()
- } else {
- hideBrightnessSlider()
- }
- }
-
- override fun setMirror(controller: BrightnessMirrorController) {
- mirrorController = controller
- mirrorController?.let { brightnessController?.setMirror(it) }
- }
-
- private fun hideBrightnessSlider() {
- brightnessController?.hideSlider()
- }
-
- private fun showBrightnessSlider() {
- if (brightnessController == null) {
- brightnessController = brightnessControllerFactory()
- mirrorController?.also { brightnessController?.setMirror(it) }
- brightnessController?.registerCallbacks()
- isListening = true
- }
- brightnessController?.showSlider()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index a3af0e5..b2e008b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -33,8 +33,6 @@
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
-import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.leak.RotationUtils;
import java.util.ArrayList;
@@ -55,10 +53,6 @@
}
};
- // brightness is visible only in split shade
- private final QuickQSBrightnessController mBrightnessController;
- private final BrightnessMirrorHandler mBrightnessMirrorHandler;
-
private final MediaFlags mMediaFlags;
private final boolean mUsingCollapsedLandscapeMedia;
@@ -70,13 +64,10 @@
@Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA) boolean usingCollapsedLandscapeMedia,
MediaFlags mediaFlags,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager,
- QuickQSBrightnessController quickQSBrightnessController
+ DumpManager dumpManager
) {
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
uiEventLogger, qsLogger, dumpManager);
- mBrightnessController = quickQSBrightnessController;
- mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
mUsingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia;
mMediaFlags = mediaFlags;
}
@@ -87,7 +78,6 @@
updateMediaExpansion();
mMediaHost.setShowsOnlyActiveMedia(true);
mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
- mBrightnessController.init(mShouldUseSplitNotificationShade);
}
private void updateMediaExpansion() {
@@ -111,20 +101,17 @@
protected void onViewAttached() {
super.onViewAttached();
mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
- mBrightnessMirrorHandler.onQsPanelAttached();
}
@Override
protected void onViewDetached() {
super.onViewDetached();
mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
- mBrightnessMirrorHandler.onQsPanelDettached();
}
@Override
void setListening(boolean listening) {
super.setListening(listening);
- mBrightnessController.setListening(listening);
}
public boolean isListening() {
@@ -137,14 +124,7 @@
}
@Override
- public void refreshAllTiles() {
- mBrightnessController.checkRestrictionAndSetEnabled();
- super.refreshAllTiles();
- }
-
- @Override
protected void onConfigurationChanged() {
- mBrightnessController.refreshVisibility(mShouldUseSplitNotificationShade);
updateMediaExpansion();
}
@@ -168,8 +148,4 @@
public int getNumQuickTiles() {
return mView.getNumQuickTiles();
}
-
- public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) {
- mBrightnessMirrorHandler.setController(brightnessMirrorController);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 4d9c4c2..e8d27ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -199,7 +199,7 @@
holder.init(dialog);
SystemUIDialog.setShowForAllUsers(dialog, true);
SystemUIDialog.registerDismissListener(dialog);
- SystemUIDialog.setWindowOnTop(dialog);
+ SystemUIDialog.setWindowOnTop(dialog, mKeyguard.isShowing());
mUiHandler.post(() -> {
if (view != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index e116f75..04a25fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -59,6 +59,7 @@
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
@@ -68,7 +69,7 @@
private final NetworkController mController;
private final DataUsageController mDataController;
-
+ private final KeyguardStateController mKeyguard;
private final CellSignalCallback mSignalCallback = new CellSignalCallback();
@Inject
@@ -81,11 +82,14 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- NetworkController networkController
+ NetworkController networkController,
+ KeyguardStateController keyguardStateController
+
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = networkController;
+ mKeyguard = keyguardStateController;
mDataController = mController.getMobileDataController();
mController.observe(getLifecycle(), mSignalCallback);
}
@@ -145,7 +149,7 @@
dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
SystemUIDialog.setShowForAllUsers(dialog, true);
SystemUIDialog.registerDismissListener(dialog);
- SystemUIDialog.setWindowOnTop(dialog);
+ SystemUIDialog.setWindowOnTop(dialog, mKeyguard.isShowing());
dialog.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 8e01942..8b6ddb4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -65,6 +65,7 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
@@ -130,6 +131,7 @@
private Button mDoneButton;
private Button mAirplaneModeButton;
private Drawable mBackgroundOn;
+ private KeyguardStateController mKeyguard;
@Nullable
private Drawable mBackgroundOff = null;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -159,7 +161,8 @@
public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
InternetDialogController internetDialogController, boolean canConfigMobileData,
boolean canConfigWifi, boolean aboveStatusBar, UiEventLogger uiEventLogger,
- @Main Handler handler, @Background Executor executor) {
+ @Main Handler handler, @Background Executor executor,
+ KeyguardStateController keyguardStateController) {
super(context);
if (DEBUG) {
Log.d(TAG, "Init InternetDialog");
@@ -177,6 +180,7 @@
mWifiManager = mInternetDialogController.getWifiManager();
mCanConfigMobileData = canConfigMobileData;
mCanConfigWifi = canConfigWifi;
+ mKeyguard = keyguardStateController;
mUiEventLogger = uiEventLogger;
mAdapter = new InternetAdapter(mInternetDialogController);
@@ -615,7 +619,7 @@
mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
SystemUIDialog.registerDismissListener(mAlertDialog);
- SystemUIDialog.setWindowOnTop(mAlertDialog);
+ SystemUIDialog.setWindowOnTop(mAlertDialog, mKeyguard.isShowing());
mAlertDialog.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
index 79f7ac3..4386169 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
@@ -24,6 +24,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.policy.KeyguardStateController
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -40,7 +41,8 @@
private val internetDialogController: InternetDialogController,
private val context: Context,
private val uiEventLogger: UiEventLogger,
- private val dialogLaunchAnimator: DialogLaunchAnimator
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val keyguardStateController: KeyguardStateController
) {
companion object {
var internetDialog: InternetDialog? = null
@@ -61,7 +63,7 @@
} else {
internetDialog = InternetDialog(context, this, internetDialogController,
canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler,
- executor)
+ executor, keyguardStateController)
if (view != null) {
dialogLaunchAnimator.showFromView(internetDialog!!, view,
animateBackgroundBoundsChange = true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index ff9fc30..3dd717d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.statusbar.phone.NotificationIconContainer.MAX_ICONS_ON_LOCKSCREEN;
-
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -41,6 +39,7 @@
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.NotificationBackgroundView;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
@@ -48,7 +47,6 @@
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.util.Utils;
/**
* A notification shelf view that is placed inside the notification scroller. It manages the
@@ -85,7 +83,7 @@
private int mIndexOfFirstViewInShelf = -1;
private float mCornerAnimationDistance;
private NotificationShelfController mController;
- private int mActualWidth = -1;
+ private float mActualWidth = -1;
/** Fraction of lockscreen to shade animation (on lockscreen swipe down). */
private float mFractionToShade;
@@ -211,10 +209,6 @@
final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
viewState.yTranslation = stackEnd - viewState.height;
-
- final int shortestWidth = mShelfIcons.calculateWidthFor(MAX_ICONS_ON_LOCKSCREEN);
- final float fraction = Interpolators.STANDARD.getInterpolation(mFractionToShade);
- updateStateWidth(viewState, fraction, shortestWidth);
} else {
viewState.hidden = true;
viewState.location = ExpandableViewState.LOCATION_GONE;
@@ -223,15 +217,23 @@
}
/**
- * @param shelfState View state for NotificationShelf
- * @param fraction Fraction of lockscreen to shade transition
+ * @param fractionToShade Fraction of lockscreen to shade transition
* @param shortestWidth Shortest width to use for lockscreen shelf
*/
@VisibleForTesting
- public void updateStateWidth(ShelfState shelfState, float fraction, int shortestWidth) {
- shelfState.actualWidth = mAmbientState.isOnKeyguard()
- ? (int) MathUtils.lerp(shortestWidth, getWidth(), fraction)
+ public void updateActualWidth(float fractionToShade, float shortestWidth) {
+ final float actualWidth = mAmbientState.isOnKeyguard()
+ ? MathUtils.lerp(shortestWidth, getWidth(), fractionToShade)
: getWidth();
+ ActivatableNotificationView anv = (ActivatableNotificationView) this;
+ NotificationBackgroundView bg = anv.getBackgroundNormal();
+ if (bg != null) {
+ anv.getBackgroundNormal().setActualWidth((int) actualWidth);
+ }
+ if (mShelfIcons != null) {
+ mShelfIcons.setActualLayoutWidth((int) actualWidth);
+ }
+ mActualWidth = actualWidth;
}
/**
@@ -245,7 +247,7 @@
* @return Actual width of shelf, accounting for possible ongoing width animation
*/
public int getActualWidth() {
- return mActualWidth > -1 ? mActualWidth : getWidth();
+ return mActualWidth > -1 ? (int) mActualWidth : getWidth();
}
/**
@@ -412,6 +414,10 @@
|| !mShowNotificationShelf
|| numViewsInShelf < 1f;
+ final float fractionToShade = Interpolators.STANDARD.getInterpolation(mFractionToShade);
+ final float shortestWidth = mShelfIcons.calculateWidthFor(numViewsInShelf);
+ updateActualWidth(fractionToShade, shortestWidth);
+
// TODO(b/172289889) transition last icon in shelf to notification icon and vice versa.
setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
setBackgroundTop(backgroundTop);
@@ -921,30 +927,17 @@
public class ShelfState extends ExpandableViewState {
private boolean hasItemsInStableShelf;
private ExpandableView firstViewInShelf;
- public int actualWidth = -1;
-
- private void updateShelfWidth(View view) {
- if (actualWidth < 0) {
- return;
- }
- mActualWidth = actualWidth;
- ActivatableNotificationView anv = (ActivatableNotificationView) view;
- anv.getBackgroundNormal().setActualWidth(actualWidth);
- mShelfIcons.setActualLayoutWidth(actualWidth);
- }
@Override
public void applyToView(View view) {
if (!mShowNotificationShelf) {
return;
}
-
super.applyToView(view);
setIndexOfFirstViewInShelf(firstViewInShelf);
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
- updateShelfWidth(view);
}
@Override
@@ -952,13 +945,11 @@
if (!mShowNotificationShelf) {
return;
}
-
super.animateTo(view, properties);
setIndexOfFirstViewInShelf(firstViewInShelf);
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
- updateShelfWidth(view);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 962c7fa..1401423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -92,6 +92,8 @@
private val views: Sequence<View>
get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
+ private var showingListener: ShowingListener? = null
+
init {
contentInsetsProvider.addCallback(object : StatusBarContentInsetsChangedListener {
override fun onStatusBarContentInsetsChanged() {
@@ -132,6 +134,10 @@
uiExecutor = e
}
+ fun setShowingListener(l: ShowingListener) {
+ showingListener = l
+ }
+
fun setQsExpanded(expanded: Boolean) {
dlog("setQsExpanded $expanded")
synchronized(lock) {
@@ -176,15 +182,20 @@
.setDuration(DURATION)
.setInterpolator(Interpolators.ALPHA_OUT)
.alpha(0f)
- .withEndAction { dot.visibility = View.INVISIBLE }
+ .withEndAction {
+ dot.visibility = View.INVISIBLE
+ showingListener?.onPrivacyDotHidden(dot)
+ }
.start()
} else {
dot.visibility = View.INVISIBLE
+ showingListener?.onPrivacyDotHidden(dot)
}
}
@UiThread
private fun showDotView(dot: View, animate: Boolean) {
+ showingListener?.onPrivacyDotShown(dot)
dot.clearAnimation()
if (animate) {
dot.visibility = View.VISIBLE
@@ -320,6 +331,7 @@
@UiThread
private fun updateDesignatedCorner(newCorner: View?, shouldShowDot: Boolean) {
if (shouldShowDot) {
+ showingListener?.onPrivacyDotShown(newCorner)
newCorner?.apply {
clearAnimation()
visibility = View.VISIBLE
@@ -336,6 +348,11 @@
private fun setCornerVisibilities(vis: Int) {
views.forEach { corner ->
corner.visibility = vis
+ if (vis == View.VISIBLE) {
+ showingListener?.onPrivacyDotShown(corner)
+ } else {
+ showingListener?.onPrivacyDotHidden(corner)
+ }
}
}
@@ -555,6 +572,11 @@
)
}
}
+
+ public interface ShowingListener {
+ fun onPrivacyDotShown(v: View?)
+ fun onPrivacyDotHidden(v: View?)
+ }
}
private fun dlog(s: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index ebfed1a..034b751 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -387,16 +387,18 @@
}
/**
- * @return Width of shelf for the given number of icons and overflow dot
+ * @return Width of shelf for the given number of icons
*/
- public int calculateWidthFor(int numMaxIcons) {
+ public float calculateWidthFor(float numIcons) {
if (getChildCount() == 0) {
- return 0;
+ return 0f;
}
- return (int) (getActualPaddingStart()
- + numMaxIcons * mIconSize
- + mOverflowWidth
- + getActualPaddingEnd());
+ final float contentWidth = numIcons <= MAX_ICONS_ON_LOCKSCREEN + 1
+ ? numIcons * mIconSize
+ : MAX_ICONS_ON_LOCKSCREEN * mIconSize + (float) mOverflowWidth;
+ return getActualPaddingStart()
+ + contentWidth
+ + getActualPaddingEnd();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index d6fc0a4..6e1ec9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -43,7 +43,6 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
/**
* Base class for dialogs that should appear over panels and keyguard.
@@ -220,10 +219,13 @@
}
}
- public static void setWindowOnTop(Dialog dialog) {
+ /**
+ * Ensure the window type is set properly to show over all other screens
+ */
+ public static void setWindowOnTop(Dialog dialog, boolean isKeyguardShowing) {
final Window window = dialog.getWindow();
window.setType(LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
- if (Dependency.get(KeyguardStateController.class).isShowing()) {
+ if (isKeyguardShowing) {
window.getAttributes().setFitInsetsTypes(
window.getAttributes().getFitInsetsTypes() & ~Type.statusBars());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index e416ed1..f4e53e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -25,7 +25,6 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.IActivityManager;
-import android.app.IActivityTaskManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -39,7 +38,6 @@
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -78,11 +76,14 @@
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.CreateUserActivity;
import com.android.systemui.util.settings.SecureSettings;
+import dagger.Lazy;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -125,10 +126,10 @@
private final ActivityStarter mActivityStarter;
private final BroadcastDispatcher mBroadcastDispatcher;
private final TelephonyListenerManager mTelephonyListenerManager;
- private final IActivityTaskManager mActivityTaskManager;
private final InteractionJankMonitor mInteractionJankMonitor;
private final LatencyTracker mLatencyTracker;
private final DialogLaunchAnimator mDialogLaunchAnimator;
+ private final Lazy<ShadeController> mShadeController;
private ArrayList<UserRecord> mUsers = new ArrayList<>();
@VisibleForTesting
@@ -149,6 +150,7 @@
private final UiEventLogger mUiEventLogger;
private final IActivityManager mActivityManager;
private final Executor mBgExecutor;
+ private final Executor mUiExecutor;
private final boolean mGuestUserAutoCreated;
private final AtomicBoolean mGuestIsResetting;
private final AtomicBoolean mGuestCreationScheduled;
@@ -170,19 +172,19 @@
UiEventLogger uiEventLogger,
FalsingManager falsingManager,
TelephonyListenerManager telephonyListenerManager,
- IActivityTaskManager activityTaskManager,
SecureSettings secureSettings,
@Background Executor bgExecutor,
+ @Main Executor uiExecutor,
InteractionJankMonitor interactionJankMonitor,
LatencyTracker latencyTracker,
DumpManager dumpManager,
+ Lazy<ShadeController> shadeController,
DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
mActivityManager = activityManager;
mUserTracker = userTracker;
mBroadcastDispatcher = broadcastDispatcher;
mTelephonyListenerManager = telephonyListenerManager;
- mActivityTaskManager = activityTaskManager;
mUiEventLogger = uiEventLogger;
mFalsingManager = falsingManager;
mInteractionJankMonitor = interactionJankMonitor;
@@ -190,6 +192,7 @@
mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
this, mUserTracker, mUiEventLogger, secureSettings);
mBgExecutor = bgExecutor;
+ mUiExecutor = uiExecutor;
if (!UserManager.isGuestUserEphemeral()) {
mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
}
@@ -204,6 +207,7 @@
mActivityStarter = activityStarter;
mUserManager = userManager;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mShadeController = shadeController;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
@@ -289,109 +293,100 @@
mForcePictureLoadForUserId.clear();
final boolean addUsersWhenLocked = mAddUsersFromLockScreen;
- new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
- @SuppressWarnings("unchecked")
- @Override
- protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) {
- final SparseArray<Bitmap> bitmaps = params[0];
- List<UserInfo> infos = mUserManager.getAliveUsers();
- if (infos == null) {
- return null;
- }
- ArrayList<UserRecord> records = new ArrayList<>(infos.size());
- int currentId = mUserTracker.getUserId();
- // Check user switchability of the foreground user since SystemUI is running in
- // User 0
- boolean canSwitchUsers = mUserManager.getUserSwitchability(
- UserHandle.of(mUserTracker.getUserId())) == SWITCHABILITY_STATUS_OK;
- UserRecord guestRecord = null;
+ mBgExecutor.execute(() -> {
+ List<UserInfo> infos = mUserManager.getAliveUsers();
+ if (infos == null) {
+ return;
+ }
+ ArrayList<UserRecord> records = new ArrayList<>(infos.size());
+ int currentId = mUserTracker.getUserId();
+ // Check user switchability of the foreground user since SystemUI is running in
+ // User 0
+ boolean canSwitchUsers = mUserManager.getUserSwitchability(
+ UserHandle.of(mUserTracker.getUserId())) == SWITCHABILITY_STATUS_OK;
+ UserRecord guestRecord = null;
- for (UserInfo info : infos) {
- boolean isCurrent = currentId == info.id;
- boolean switchToEnabled = canSwitchUsers || isCurrent;
- if (info.isEnabled()) {
- if (info.isGuest()) {
- // Tapping guest icon triggers remove and a user switch therefore
- // the icon shouldn't be enabled even if the user is current
- guestRecord = new UserRecord(info, null /* picture */,
- true /* isGuest */, isCurrent, false /* isAddUser */,
- false /* isRestricted */, canSwitchUsers,
- false /* isAddSupervisedUser */);
- } else if (info.supportsSwitchToByUser()) {
- Bitmap picture = bitmaps.get(info.id);
- if (picture == null) {
- picture = mUserManager.getUserIcon(info.id);
-
- if (picture != null) {
- int avatarSize = mContext.getResources()
- .getDimensionPixelSize(R.dimen.max_avatar_size);
- picture = Bitmap.createScaledBitmap(
- picture, avatarSize, avatarSize, true);
- }
- }
- records.add(new UserRecord(info, picture, false /* isGuest */,
- isCurrent, false /* isAddUser */, false /* isRestricted */,
- switchToEnabled, false /* isAddSupervisedUser */));
- }
- }
- }
- if (records.size() > 1 || guestRecord != null) {
- Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true);
- }
-
- if (guestRecord == null) {
- if (mGuestUserAutoCreated) {
- // If mGuestIsResetting=true, the switch should be disabled since
- // we will just use it as an indicator for "Resetting guest...".
- // Otherwise, default to canSwitchUsers.
- boolean isSwitchToGuestEnabled =
- !mGuestIsResetting.get() && canSwitchUsers;
- guestRecord = new UserRecord(null /* info */, null /* picture */,
- true /* isGuest */, false /* isCurrent */,
- false /* isAddUser */, false /* isRestricted */,
- isSwitchToGuestEnabled, false /* isAddSupervisedUser */);
- checkIfAddUserDisallowedByAdminOnly(guestRecord);
- records.add(guestRecord);
- } else if (canCreateGuest(guestRecord != null)) {
- guestRecord = new UserRecord(null /* info */, null /* picture */,
- true /* isGuest */, false /* isCurrent */,
- false /* isAddUser */, createIsRestricted(), canSwitchUsers,
+ for (UserInfo info : infos) {
+ boolean isCurrent = currentId == info.id;
+ boolean switchToEnabled = canSwitchUsers || isCurrent;
+ if (info.isEnabled()) {
+ if (info.isGuest()) {
+ // Tapping guest icon triggers remove and a user switch therefore
+ // the icon shouldn't be enabled even if the user is current
+ guestRecord = new UserRecord(info, null /* picture */,
+ true /* isGuest */, isCurrent, false /* isAddUser */,
+ false /* isRestricted */, canSwitchUsers,
false /* isAddSupervisedUser */);
- checkIfAddUserDisallowedByAdminOnly(guestRecord);
- records.add(guestRecord);
+ } else if (info.supportsSwitchToByUser()) {
+ Bitmap picture = bitmaps.get(info.id);
+ if (picture == null) {
+ picture = mUserManager.getUserIcon(info.id);
+
+ if (picture != null) {
+ int avatarSize = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.max_avatar_size);
+ picture = Bitmap.createScaledBitmap(
+ picture, avatarSize, avatarSize, true);
+ }
+ }
+ records.add(new UserRecord(info, picture, false /* isGuest */,
+ isCurrent, false /* isAddUser */, false /* isRestricted */,
+ switchToEnabled, false /* isAddSupervisedUser */));
}
- } else {
+ }
+ }
+ if (records.size() > 1 || guestRecord != null) {
+ Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true);
+ }
+
+ if (guestRecord == null) {
+ if (mGuestUserAutoCreated) {
+ // If mGuestIsResetting=true, the switch should be disabled since
+ // we will just use it as an indicator for "Resetting guest...".
+ // Otherwise, default to canSwitchUsers.
+ boolean isSwitchToGuestEnabled = !mGuestIsResetting.get() && canSwitchUsers;
+ guestRecord = new UserRecord(null /* info */, null /* picture */,
+ true /* isGuest */, false /* isCurrent */,
+ false /* isAddUser */, false /* isRestricted */,
+ isSwitchToGuestEnabled, false /* isAddSupervisedUser */);
+ checkIfAddUserDisallowedByAdminOnly(guestRecord);
+ records.add(guestRecord);
+ } else if (canCreateGuest(guestRecord != null)) {
+ guestRecord = new UserRecord(null /* info */, null /* picture */,
+ true /* isGuest */, false /* isCurrent */,
+ false /* isAddUser */, createIsRestricted(), canSwitchUsers,
+ false /* isAddSupervisedUser */);
+ checkIfAddUserDisallowedByAdminOnly(guestRecord);
records.add(guestRecord);
}
-
- if (canCreateUser()) {
- UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
- false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
- createIsRestricted(), canSwitchUsers,
- false /* isAddSupervisedUser */);
- checkIfAddUserDisallowedByAdminOnly(addUserRecord);
- records.add(addUserRecord);
- }
-
- if (canCreateSupervisedUser()) {
- UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
- false /* isGuest */, false /* isCurrent */, false /* isAddUser */,
- createIsRestricted(), canSwitchUsers, true /* isAddSupervisedUser */);
- checkIfAddUserDisallowedByAdminOnly(addUserRecord);
- records.add(addUserRecord);
- }
-
- return records;
+ } else {
+ records.add(guestRecord);
}
- @Override
- protected void onPostExecute(ArrayList<UserRecord> userRecords) {
- if (userRecords != null) {
- mUsers = userRecords;
+ if (canCreateUser()) {
+ UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
+ false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
+ createIsRestricted(), canSwitchUsers,
+ false /* isAddSupervisedUser */);
+ checkIfAddUserDisallowedByAdminOnly(addUserRecord);
+ records.add(addUserRecord);
+ }
+
+ if (canCreateSupervisedUser()) {
+ UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
+ false /* isGuest */, false /* isCurrent */, false /* isAddUser */,
+ createIsRestricted(), canSwitchUsers, true /* isAddSupervisedUser */);
+ checkIfAddUserDisallowedByAdminOnly(addUserRecord);
+ records.add(addUserRecord);
+ }
+
+ mUiExecutor.execute(() -> {
+ if (records != null) {
+ mUsers = records;
notifyAdapters();
}
- }
- }.execute((SparseArray) bitmaps);
+ });
+ });
}
boolean systemCanCreateUsers() {
@@ -1159,7 +1154,7 @@
context.getString(mGuestUserAutoCreated
? com.android.settingslib.R.string.guest_reset_guest_confirm_button
: R.string.guest_exit_guest_dialog_remove), this);
- SystemUIDialog.setWindowOnTop(this);
+ SystemUIDialog.setWindowOnTop(this, mKeyguardStateController.isShowing());
setCanceledOnTouchOutside(false);
mGuestId = guestId;
mTargetId = targetId;
@@ -1194,7 +1189,7 @@
context.getString(android.R.string.cancel), this);
setButton(DialogInterface.BUTTON_POSITIVE,
context.getString(android.R.string.ok), this);
- SystemUIDialog.setWindowOnTop(this);
+ SystemUIDialog.setWindowOnTop(this, mKeyguardStateController.isShowing());
}
@Override
@@ -1211,33 +1206,8 @@
if (ActivityManager.isUserAMonkey()) {
return;
}
- Intent intent = CreateUserActivity.createIntentForStart(getContext());
-
- // There are some differences between ActivityStarter and ActivityTaskManager in
- // terms of how they start an activity. ActivityStarter hides the notification bar
- // before starting the activity to make sure nothing is in front of the new
- // activity. ActivityStarter also tries to unlock the device if it's locked.
- // When locked with PIN/pattern/password then it shows the prompt, if there are no
- // security steps then it dismisses the keyguard and then starts the activity.
- // ActivityTaskManager doesn't hide the notification bar or unlocks the device, but
- // it can start an activity on top of the locked screen.
- if (!mKeyguardStateController.isUnlocked()
- && !mKeyguardStateController.canDismissLockScreen()) {
- // Device is locked and can't be unlocked without a PIN/pattern/password so we
- // need to use ActivityTaskManager to start the activity on top of the locked
- // screen.
- try {
- mActivityTaskManager.startActivity(null,
- mContext.getBasePackageName(), mContext.getAttributionTag(), intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()), null,
- null, 0, 0, null, null);
- } catch (RemoteException e) {
- e.printStackTrace();
- Log.e(TAG, "Couldn't start create user activity", e);
- }
- } else {
- mActivityStarter.startActivity(intent, true);
- }
+ mShadeController.get().collapsePanel();
+ getContext().startActivity(CreateUserActivity.createIntentForStart(getContext()));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
index 890ee5f..c9de966 100644
--- a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
@@ -44,7 +44,9 @@
* Creates an intent to start this activity.
*/
public static Intent createIntentForStart(Context context) {
- return new Intent(context, CreateUserActivity.class);
+ Intent intent = new Intent(context, CreateUserActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
}
private static final String TAG = "CreateUserActivity";
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index 41da44a..14585fb 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -44,6 +44,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
+import com.android.systemui.statusbar.phone.ShadeController
import com.android.systemui.statusbar.policy.UserSwitcherController
import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter
import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord
@@ -63,7 +64,8 @@
private val broadcastDispatcher: BroadcastDispatcher,
private val layoutInflater: LayoutInflater,
private val falsingManager: FalsingManager,
- private val userManager: UserManager
+ private val userManager: UserManager,
+ private val shadeController: ShadeController
) : LifecycleActivity() {
private lateinit var parent: ViewGroup
@@ -250,7 +252,9 @@
dismiss()
popupMenu = null
- this@UserSwitcherActivity.finish()
+ if (!item.isAddUser) {
+ this@UserSwitcherActivity.finish()
+ }
}
show()
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
index 7f3d54d..d3c6e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
@@ -145,6 +145,10 @@
}
private void addCallbackLocked(@NotNull Callback callback) {
+ if (mCallbacks.contains(callback)) {
+ return;
+ }
+
if (shouldLog()) Log.d(mTag, "adding callback");
mCallbacks.add(callback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
new file mode 100644
index 0000000..e62b4e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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
+
+import android.graphics.Canvas
+import android.graphics.Insets
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.Region
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.DisplayCutout
+import android.view.DisplayInfo
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.internal.R
+import com.android.systemui.util.mockito.eq
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class DisplayCutoutBaseViewTest : SysuiTestCase() {
+
+ @Mock private lateinit var mockCanvas: Canvas
+ @Mock private lateinit var mockRootView: View
+ @Mock private lateinit var mockDisplay: Display
+
+ private lateinit var cutoutBaseView: DisplayCutoutBaseView
+ private val cutout: DisplayCutout = DisplayCutout.Builder()
+ .setSafeInsets(Insets.of(0, 2, 0, 0))
+ .setBoundingRectTop(Rect(1, 0, 2, 2))
+ .build()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun testBoundingRectsToRegion() {
+ setupDisplayCutoutBaseView(true /* fillCutout */, true /* hasCutout */)
+ val rect = Rect(1, 2, 3, 4)
+ assertThat(cutoutBaseView.rectsToRegion(listOf(rect)).bounds).isEqualTo(rect)
+ }
+
+ @Test
+ fun testDrawCutout_fillCutout() {
+ setupDisplayCutoutBaseView(true /* fillCutout */, true /* hasCutout */)
+ cutoutBaseView.onDraw(mockCanvas)
+
+ verify(cutoutBaseView).drawCutouts(mockCanvas)
+ }
+
+ @Test
+ fun testDrawCutout_notFillCutout() {
+ setupDisplayCutoutBaseView(false /* fillCutout */, true /* hasCutout */)
+ cutoutBaseView.onDraw(mockCanvas)
+
+ verify(cutoutBaseView, never()).drawCutouts(mockCanvas)
+ }
+
+ @Test
+ fun testShouldInterceptTouch_hasCutout() {
+ setupDisplayCutoutBaseView(true /* fillCutout */, true /* hasCutout */)
+ cutoutBaseView.updateCutout()
+
+ assertThat(cutoutBaseView.shouldInterceptTouch()).isTrue()
+ }
+
+ @Test
+ fun testShouldInterceptTouch_noCutout() {
+ setupDisplayCutoutBaseView(true /* fillCutout */, false /* hasCutout */)
+ cutoutBaseView.updateCutout()
+
+ assertThat(cutoutBaseView.shouldInterceptTouch()).isFalse()
+ }
+
+ @Test
+ fun testGetInterceptRegion_hasCutout() {
+ setupDisplayCutoutBaseView(true /* fillCutout */, true /* hasCutout */)
+ whenever(mockRootView.left).thenReturn(0)
+ whenever(mockRootView.top).thenReturn(0)
+ whenever(mockRootView.right).thenReturn(100)
+ whenever(mockRootView.bottom).thenReturn(200)
+
+ val expect = Region()
+ expect.op(cutout.boundingRectTop, Region.Op.UNION)
+ expect.op(0, 0, 100, 200, Region.Op.INTERSECT)
+
+ cutoutBaseView.updateCutout()
+
+ assertThat(cutoutBaseView.interceptRegion).isEqualTo(expect)
+ }
+
+ @Test
+ fun testGetInterceptRegion_noCutout() {
+ setupDisplayCutoutBaseView(true /* fillCutout */, false /* hasCutout */)
+ cutoutBaseView.updateCutout()
+
+ assertThat(cutoutBaseView.interceptRegion).isNull()
+ }
+
+ @Test
+ fun testCutoutProtection() {
+ setupDisplayCutoutBaseView(true /* fillCutout */, false /* hasCutout */)
+ val bounds = Rect(0, 0, 10, 10)
+ val path = Path()
+ val pathBounds = RectF(bounds)
+ path.addRect(pathBounds, Path.Direction.CCW)
+
+ context.mainExecutor.execute {
+ cutoutBaseView.setProtection(path, bounds)
+ cutoutBaseView.enableShowProtection(true)
+ }
+ waitForIdleSync()
+
+ assertThat(cutoutBaseView.protectionPath.isRect(pathBounds)).isTrue()
+ assertThat(cutoutBaseView.protectionRect).isEqualTo(pathBounds)
+ }
+
+ private fun setupDisplayCutoutBaseView(fillCutout: Boolean, hasCutout: Boolean) {
+ mContext.orCreateTestableResources.addOverride(
+ R.array.config_displayUniqueIdArray, arrayOf<String>())
+ mContext.orCreateTestableResources.addOverride(
+ R.bool.config_fillMainBuiltInDisplayCutout, fillCutout)
+
+ cutoutBaseView = spy(DisplayCutoutBaseView(mContext))
+ whenever(cutoutBaseView.display).thenReturn(mockDisplay)
+ whenever(cutoutBaseView.rootView).thenReturn(mockRootView)
+ whenever(mockDisplay.getDisplayInfo(eq(cutoutBaseView.displayInfo))
+ ).then {
+ val info = it.getArgument<DisplayInfo>(0)
+ info.displayCutout = if (hasCutout) cutout else null
+ return@then true
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 72d72c8..70f3251 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -14,7 +14,6 @@
package com.android.systemui;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH;
@@ -22,7 +21,7 @@
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
-import static com.android.systemui.ScreenDecorations.rectsToRegion;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.google.common.truth.Truth.assertThat;
@@ -32,7 +31,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.atLeastOnce;
@@ -49,10 +47,12 @@
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Insets;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.VectorDrawable;
import android.hardware.display.DisplayManager;
+import android.hardware.graphics.common.DisplayDecorationSupport;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -89,7 +89,6 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
-import java.util.Collections;
@RunWithLooper
@RunWith(AndroidTestingRunner.class)
@@ -106,6 +105,8 @@
private FakeThreadFactory mThreadFactory;
private ArrayList<DecorProvider> mDecorProviders;
@Mock
+ private Display mDisplay;
+ @Mock
private TunerService mTunerService;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@@ -140,14 +141,15 @@
.getMaximumWindowMetrics();
when(mWindowManager.getMaximumWindowMetrics()).thenReturn(metrics);
mContext.addMockSystemService(WindowManager.class, mWindowManager);
-
mDisplayManager = mock(DisplayManager.class);
- Display display = mContext.getSystemService(DisplayManager.class)
- .getDisplay(DEFAULT_DISPLAY);
- when(mDisplayManager.getDisplay(anyInt())).thenReturn(display);
mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
- when(mMockTypedArray.length()).thenReturn(0);
+ spyOn(mContext);
+ when(mContext.getDisplay()).thenReturn(mDisplay);
+ // Not support hwc layer by default
+ doReturn(null).when(mDisplay).getDisplayDecorationSupport();
+
+ when(mMockTypedArray.length()).thenReturn(0);
mPrivacyDotTopLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
R.id.privacy_dot_top_left_container,
DisplayCutout.BOUNDS_POSITION_TOP,
@@ -975,12 +977,6 @@
}
@Test
- public void testBoundingRectsToRegion() throws Exception {
- Rect rect = new Rect(1, 2, 3, 4);
- assertThat(rectsToRegion(Collections.singletonList(rect)).getBounds(), is(rect));
- }
-
- @Test
public void testRegistration_From_NoOverlay_To_HasOverlays() {
doReturn(false).when(mScreenDecorations).hasOverlays();
mScreenDecorations.start();
@@ -1029,6 +1025,114 @@
assertThat(mScreenDecorations.mIsRegistered, is(false));
}
+ @Test
+ public void testSupportHwcLayer_SwitchFrom_NotSupport() {
+ setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
+ 0 /* roundedPadding */, false /* multipleRadius */,
+ true /* fillCutout */, false /* privacyDot */);
+
+ // top cutout
+ final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
+ doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
+ .when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+ // should only inflate mOverlays when the hwc doesn't support screen decoration
+ assertNull(mScreenDecorations.mScreenDecorHwcWindow);
+ assertNotNull(mScreenDecorations.mOverlays);
+ assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
+ assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
+
+ final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
+ decorationSupport.format = PixelFormat.R_8;
+ doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
+ // Trigger the support hwc screen decoration change by changing the display unique id
+ mScreenDecorations.mDisplayUniqueId = "test";
+ mScreenDecorations.mDisplayListener.onDisplayChanged(1);
+
+ // should only inflate hwc layer when the hwc supports screen decoration
+ assertNotNull(mScreenDecorations.mScreenDecorHwcWindow);
+ assertNull(mScreenDecorations.mOverlays);
+ }
+
+ @Test
+ public void testNotSupportHwcLayer_SwitchFrom_Support() {
+ setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
+ 0 /* roundedPadding */, false /* multipleRadius */,
+ true /* fillCutout */, false /* privacyDot */);
+ final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
+ decorationSupport.format = PixelFormat.R_8;
+ doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
+
+ // top cutout
+ final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
+ doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
+ .when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+ // should only inflate hwc layer when the hwc supports screen decoration
+ assertNotNull(mScreenDecorations.mScreenDecorHwcWindow);
+ assertNull(mScreenDecorations.mOverlays);
+
+ doReturn(null).when(mDisplay).getDisplayDecorationSupport();
+ // Trigger the support hwc screen decoration change by changing the display unique id
+ mScreenDecorations.mDisplayUniqueId = "test";
+ mScreenDecorations.mDisplayListener.onDisplayChanged(1);
+
+ // should only inflate mOverlays when the hwc doesn't support screen decoration
+ assertNull(mScreenDecorations.mScreenDecorHwcWindow);
+ assertNotNull(mScreenDecorations.mOverlays);
+ assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
+ assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
+ }
+
+ @Test
+ public void testHwcLayer_noPrivacyDot() {
+ setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
+ 0 /* roundedPadding */, false /* multipleRadius */,
+ true /* fillCutout */, false /* privacyDot */);
+ final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
+ decorationSupport.format = PixelFormat.R_8;
+ doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
+
+ // top cutout
+ final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
+ doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
+ .when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+
+ // Should only inflate hwc layer.
+ assertNotNull(mScreenDecorations.mScreenDecorHwcWindow);
+ assertNull(mScreenDecorations.mOverlays);
+ }
+
+ @Test
+ public void testHwcLayer_PrivacyDot() {
+ setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
+ 0 /* roundedPadding */, false /* multipleRadius */,
+ true /* fillCutout */, true /* privacyDot */);
+ final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
+ decorationSupport.format = PixelFormat.R_8;
+ doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
+
+ // top cutout
+ final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
+ doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
+ .when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+
+ assertNotNull(mScreenDecorations.mScreenDecorHwcWindow);
+ // mOverlays are inflated but the visibility should be GONE.
+ assertNotNull(mScreenDecorations.mOverlays);
+ final View topOverlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView();
+ final View botOverlay = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView();
+ assertEquals(topOverlay.getVisibility(), View.INVISIBLE);
+ assertEquals(botOverlay.getVisibility(), View.INVISIBLE);
+
+ }
+
private void setupResources(int radius, int radiusTop, int radiusBottom, int roundedPadding,
boolean multipleRadius, boolean fillCutout, boolean privacyDot) {
mContext.getOrCreateTestableResources().addOverride(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt
deleted file mode 100644
index de1d86b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs
-
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.settings.brightness.BrightnessController
-import com.android.systemui.statusbar.policy.BrightnessMirrorController
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.mockito.Mock
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.never
-import org.mockito.Mockito.mock
-import org.mockito.junit.MockitoJUnit
-
-@SmallTest
-class QuickQSBrightnessControllerTest : SysuiTestCase() {
-
- @Mock
- lateinit var brightnessController: BrightnessController
- @get:Rule
- val mockito = MockitoJUnit.rule()
-
- lateinit var quickQSBrightnessController: QuickQSBrightnessController
-
- @Before
- fun setUp() {
- quickQSBrightnessController = QuickQSBrightnessController(
- brightnessControllerFactory = { brightnessController })
- }
-
- @Test
- fun testSliderIsShownWhenInitializedInSplitShade() {
- quickQSBrightnessController.init(shouldUseSplitNotificationShade = true)
-
- verify(brightnessController).showSlider()
- }
-
- @Test
- fun testSliderIsShownWhenRefreshedInSplitShade() {
- quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
-
- verify(brightnessController, times(1)).showSlider()
- }
-
- @Test
- fun testSliderIsHiddenWhenRefreshedInNonSplitShade() {
- // needs to be shown first
- quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
- quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
-
- verify(brightnessController).hideSlider()
- }
-
- @Test
- fun testSliderChangesVisibilityWhenRotating() {
- quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
- verify(brightnessController, times(1)).showSlider()
-
- quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
- verify(brightnessController, times(1)).hideSlider()
- }
-
- @Test
- fun testCallbacksAreRegisteredOnlyOnce() {
- // this flow simulates expanding shade in portrait...
- quickQSBrightnessController.setListening(true)
- quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
- // ... and rotating to landscape/split shade where slider is visible
- quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
-
- verify(brightnessController, times(1)).registerCallbacks()
- }
-
- @Test
- fun testCallbacksAreRegisteredOnlyOnceWhenRotatingPhone() {
- quickQSBrightnessController.setListening(true)
- quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
- quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
- quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
-
- verify(brightnessController, times(1)).registerCallbacks()
- }
-
- @Test
- fun testCallbacksAreNotRegisteredWhenSliderNotVisible() {
- quickQSBrightnessController.setListening(true)
- quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
-
- verify(brightnessController, never()).registerCallbacks()
- }
-
- @Test
- fun testMirrorIsSetWhenSliderIsShown() {
- val mirrorController = mock(BrightnessMirrorController::class.java)
- quickQSBrightnessController.setMirror(mirrorController)
- quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
-
- verify(brightnessController).setMirror(mirrorController)
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 1eb16fd..62915b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -71,8 +71,6 @@
private lateinit var tileLayout: TileLayout
@Mock
private lateinit var tileView: QSTileView
- @Mock
- private lateinit var quickQsBrightnessController: QuickQSBrightnessController
@Captor
private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
@@ -100,8 +98,7 @@
metricsLogger,
uiEventLogger,
qsLogger,
- dumpManager,
- quickQsBrightnessController
+ dumpManager
)
controller.init()
@@ -133,16 +130,6 @@
}
@Test
- fun testBrightnessRefreshedWhenConfigurationChanged() {
- // times(2) because both controller and base controller are registering their listeners
- verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture())
-
- captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) }
-
- verify(quickQsBrightnessController).refreshVisibility(anyBoolean())
- }
-
- @Test
fun testMediaExpansionUpdatedWhenConfigurationChanged() {
`when`(mediaFlags.useMediaSessionLayout()).thenReturn(true)
@@ -171,11 +158,10 @@
metricsLogger: MetricsLogger,
uiEventLogger: UiEventLoggerFake,
qsLogger: QSLogger,
- dumpManager: DumpManager,
- quickQSBrightnessController: QuickQSBrightnessController
+ dumpManager: DumpManager
) : QuickQSPanelController(view, qsTileHost, qsCustomizerController, usingMediaPlayer,
mediaHost, usingCollapsedLandscapeMedia, mediaFlags, metricsLogger, uiEventLogger, qsLogger,
- dumpManager, quickQSBrightnessController) {
+ dumpManager) {
private var rotation = RotationUtils.ROTATION_NONE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index c20e887..ed35dcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -29,6 +29,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.wifitrackerlib.WifiEntry;
@@ -68,6 +69,8 @@
private InternetAdapter mInternetAdapter;
@Mock
private InternetDialogController mInternetDialogController;
+ @Mock
+ private KeyguardStateController mKeyguard;
private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
private InternetDialog mInternetDialog;
@@ -100,7 +103,7 @@
mInternetDialog = new InternetDialog(mContext, mock(InternetDialogFactory.class),
mInternetDialogController, true, true, true, mock(UiEventLogger.class), mHandler,
- mBgExecutor);
+ mBgExecutor, mKeyguard);
mInternetDialog.mAdapter = mInternetAdapter;
mInternetDialog.mConnectedWifiEntry = mInternetWifiEntry;
mInternetDialog.mWifiEntriesCount = mWifiEntries.size();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index d280f54..5d16036 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -36,14 +36,14 @@
setFractionToShade(0f)
setOnLockscreen(true)
- shelf.updateStateWidth(shelfState, /* fraction */ 0f, /* shortestWidth */ 10)
- assertTrue(shelfState.actualWidth == 10)
+ shelf.updateActualWidth(/* fractionToShade */ 0f, /* shortestWidth */ 10f);
+ assertTrue(shelf.actualWidth == 10)
- shelf.updateStateWidth(shelfState, /* fraction */ 0.5f, /* shortestWidth */ 10)
- assertTrue(shelfState.actualWidth == 20)
+ shelf.updateActualWidth(/* fractionToShade */ 0.5f, /* shortestWidth */ 10f)
+ assertTrue(shelf.actualWidth == 20)
- shelf.updateStateWidth(shelfState, /* fraction */ 1f, /* shortestWidth */ 10)
- assertTrue(shelfState.actualWidth == 30)
+ shelf.updateActualWidth(/* fractionToShade */ 1f, /* shortestWidth */ 10f)
+ assertTrue(shelf.actualWidth == 30)
}
@Test
@@ -51,8 +51,8 @@
setFractionToShade(0f)
setOnLockscreen(false)
- shelf.updateStateWidth(shelfState, /* fraction */ 0f, /* shortestWidth */ 10)
- assertTrue(shelfState.actualWidth == 30)
+ shelf.updateActualWidth(/* fraction */ 0f, /* shortestWidth */ 10f)
+ assertTrue(shelf.actualWidth == 30)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index c344aea..07e0279 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.policy
import android.app.IActivityManager
-import android.app.IActivityTaskManager
import android.app.admin.DevicePolicyManager
import android.content.Context
import android.content.DialogInterface
@@ -49,6 +48,7 @@
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.NotificationShadeWindowView
+import com.android.systemui.statusbar.phone.ShadeController
import com.android.systemui.telephony.TelephonyListenerManager
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.settings.SecureSettings
@@ -85,7 +85,6 @@
@Mock private lateinit var userManager: UserManager
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
- @Mock private lateinit var activityTaskManager: IActivityTaskManager
@Mock private lateinit var telephonyListenerManager: TelephonyListenerManager
@Mock private lateinit var secureSettings: SecureSettings
@Mock private lateinit var falsingManager: FalsingManager
@@ -96,8 +95,10 @@
@Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
@Mock private lateinit var threadedRenderer: ThreadedRenderer
@Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+ @Mock private lateinit var shadeController: ShadeController
private lateinit var testableLooper: TestableLooper
- private lateinit var uiBgExecutor: FakeExecutor
+ private lateinit var bgExecutor: FakeExecutor
+ private lateinit var uiExecutor: FakeExecutor
private lateinit var uiEventLogger: UiEventLoggerFake
private lateinit var userSwitcherController: UserSwitcherController
private lateinit var picture: Bitmap
@@ -116,10 +117,11 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
- uiBgExecutor = FakeExecutor(FakeSystemClock())
+ bgExecutor = FakeExecutor(FakeSystemClock())
+ uiExecutor = FakeExecutor(FakeSystemClock())
uiEventLogger = UiEventLoggerFake()
- context.orCreateTestableResources.addOverride(
+ mContext.orCreateTestableResources.addOverride(
com.android.internal.R.bool.config_guestUserAutoCreated, false)
mContext.addMockSystemService(Context.FACE_SERVICE, mock(FaceManager::class.java))
@@ -141,12 +143,16 @@
picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
+ // Create defaults for the current user
+ `when`(userTracker.userId).thenReturn(ownerId)
+ `when`(userTracker.userInfo).thenReturn(ownerInfo)
+
setupController()
}
private fun setupController() {
userSwitcherController = UserSwitcherController(
- context,
+ mContext,
activityManager,
userManager,
userTracker,
@@ -159,14 +165,14 @@
uiEventLogger,
falsingManager,
telephonyListenerManager,
- activityTaskManager,
secureSettings,
- uiBgExecutor,
+ bgExecutor,
+ uiExecutor,
interactionJankMonitor,
latencyTracker,
dumpManager,
+ { shadeController },
dialogLaunchAnimator)
- userSwitcherController.mPauseRefreshUsers = true
userSwitcherController.init(notificationShadeWindowView)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
index dff77f3..5118637 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
@@ -195,6 +195,21 @@
}
@Test
+ public void addCallback_withMultipleInstancesOfTheSameCallback_registerOnlyOne() {
+ final Monitor monitor = new Monitor(mExecutor, new HashSet<>(), null /*callbacks*/);
+ final Monitor.Callback callback = mock(Monitor.Callback.class);
+
+ // Adds the same instance multiple times.
+ monitor.addCallback(callback);
+ monitor.addCallback(callback);
+ monitor.addCallback(callback);
+ mExecutor.runAllReady();
+
+ // Callback should only be triggered once.
+ verify(callback, times(1)).onConditionsChanged(true);
+ }
+
+ @Test
public void removeCallback_shouldNoLongerReceiveUpdate() {
final Condition condition = mock(Condition.class);
final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)),
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
index dafe7a4..daead0a 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
@@ -58,11 +58,14 @@
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+ private final Context mContext;
+
public CloudSearchManagerService(Context context) {
super(context, new FrameworkResourcesServiceNameResolver(context,
R.string.config_defaultCloudSearchService), null,
PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ mContext = context;
}
@Override
@@ -106,6 +109,8 @@
@Override
public void search(@NonNull SearchRequest searchRequest,
@NonNull ICloudSearchManagerCallback callBack) {
+ searchRequest.setSource(
+ mContext.getPackageManager().getNameForUid(Binder.getCallingUid()));
runForUserLocked("search", searchRequest.getRequestId(), (service) ->
service.onSearchLocked(searchRequest, callBack));
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 2f6a46a..387d911 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -606,6 +606,10 @@
}
}
+ boolean isDisplayOwnedByVirtualDevice(int displayId) {
+ return mVirtualDisplayIds.contains(displayId);
+ }
+
interface OnDeviceCloseListener {
void onClose(int associationId);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 7842697..c7d8daa 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -348,6 +348,19 @@
}
return false;
}
+
+ @Override
+ public boolean isDisplayOwnedByAnyVirtualDevice(int displayId) {
+ synchronized (mVirtualDeviceManagerLock) {
+ int size = mVirtualDevices.size();
+ for (int i = 0; i < size; i++) {
+ if (mVirtualDevices.valueAt(i).isDisplayOwnedByVirtualDevice(displayId)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
private static final class PendingTrampolineMap {
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index a8eeaf8..e335a16 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -49,6 +49,7 @@
import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.mutate.PackageStateMutator;
@@ -1262,6 +1263,21 @@
boolean migrateAppsData);
/**
+ * Returns an array of PackageStateInternal that are all part of a shared user setting which is
+ * denoted by the app ID. Returns an empty set if the shared user setting doesn't exist or does
+ * not contain any package.
+ */
+ @NonNull
+ public abstract ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId);
+
+ /**
+ * Returns the SharedUserApi denoted by the app ID of the shared user setting. Returns null if
+ * the corresponding shared user setting doesn't exist.
+ */
+ @Nullable
+ public abstract SharedUserApi getSharedUserApi(int sharedUserAppId);
+
+ /**
* Initiates a package state mutation request, returning the current state as known by
* PackageManager. This allows the later commit request to compare the initial values and
* determine if any state was changed or any packages were updated since the whole request
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 454028d..c194527 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -134,6 +134,7 @@
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -190,6 +191,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -471,6 +473,9 @@
@GuardedBy("mLock")
private String mMoveTargetUuid;
+ @GuardedBy("mCloudMediaProviders")
+ private final SparseArray<String> mCloudMediaProviders = new SparseArray<>();
+
private volatile int mMediaStoreAuthorityAppId = -1;
private volatile int mDownloadsAuthorityAppId = -1;
@@ -752,6 +757,7 @@
private static final int H_BOOT_COMPLETED = 13;
private static final int H_COMPLETE_UNLOCK_USER = 14;
private static final int H_VOLUME_STATE_CHANGED = 15;
+ private static final int H_CLOUD_MEDIA_PROVIDER_CHANGED = 16;
class StorageManagerServiceHandler extends Handler {
public StorageManagerServiceHandler(Looper looper) {
@@ -879,6 +885,17 @@
final SomeArgs args = (SomeArgs) msg.obj;
onVolumeStateChangedAsync((VolumeInfo) args.arg1, args.argi1, args.argi2);
args.recycle();
+ break;
+ }
+ case H_CLOUD_MEDIA_PROVIDER_CHANGED: {
+ final Object listener = msg.obj;
+ if (listener instanceof StorageManagerInternal.CloudProviderChangeListener) {
+ notifyCloudMediaProviderChangedAsync(
+ (StorageManagerInternal.CloudProviderChangeListener) listener);
+ } else {
+ onCloudMediaProviderChangedAsync(msg.arg1);
+ }
+ break;
}
}
}
@@ -1697,7 +1714,6 @@
return true;
}
-
private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
if (vol.type == VolumeInfo.TYPE_EMULATED) {
if (newState != VolumeInfo.STATE_MOUNTED) {
@@ -1836,6 +1852,27 @@
}
}
+ private void notifyCloudMediaProviderChangedAsync(
+ @NonNull StorageManagerInternal.CloudProviderChangeListener listener) {
+ synchronized (mCloudMediaProviders) {
+ for (int i = mCloudMediaProviders.size() - 1; i >= 0; --i) {
+ listener.onCloudProviderChanged(
+ mCloudMediaProviders.keyAt(i), mCloudMediaProviders.valueAt(i));
+ }
+ }
+ }
+
+ private void onCloudMediaProviderChangedAsync(int userId) {
+ final String authority;
+ synchronized (mCloudMediaProviders) {
+ authority = mCloudMediaProviders.get(userId);
+ }
+ for (StorageManagerInternal.CloudProviderChangeListener listener :
+ mStorageManagerInternal.mCloudProviderChangeListeners) {
+ listener.onCloudProviderChanged(userId, authority);
+ }
+ }
+
private void maybeLogMediaMount(VolumeInfo vol, int newState) {
if (!SecurityLog.isLoggingEnabled()) {
return;
@@ -3773,7 +3810,7 @@
Binder.restoreCallingIdentity(token);
}
}
-
+
@Override
public void notifyAppIoBlocked(String volumeUuid, int uid, int tid, int reason) {
enforceExternalStorageService();
@@ -3794,11 +3831,46 @@
return isAppIoBlocked(uid);
}
-
private boolean isAppIoBlocked(int uid) {
return mStorageSessionController.isAppIoBlocked(uid);
}
+ @Override
+ public void setCloudMediaProvider(@Nullable String authority) {
+ enforceExternalStorageService();
+
+ final int userId = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized (mCloudMediaProviders) {
+ final String oldAuthority = mCloudMediaProviders.get(userId);
+ if (!Objects.equals(authority, oldAuthority)) {
+ mCloudMediaProviders.put(userId, authority);
+ mHandler.obtainMessage(H_CLOUD_MEDIA_PROVIDER_CHANGED, userId, 0, authority)
+ .sendToTarget();
+ }
+ }
+ }
+
+ @Override
+ @Nullable
+ public String getCloudMediaProvider() {
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final String authority;
+ synchronized (mCloudMediaProviders) {
+ authority = mCloudMediaProviders.get(userId);
+ }
+ if (authority == null) {
+ return null;
+ }
+ final ProviderInfo pi = mPmInternal.resolveContentProvider(
+ authority, 0, userId, callingUid);
+ if (pi == null
+ || mPmInternal.filterAppAccess(pi.packageName, callingUid, userId)) {
+ return null;
+ }
+ return authority;
+ }
+
/**
* Enforces that the caller is the {@link ExternalStorageService}
*
@@ -4951,6 +5023,12 @@
pw.decreaseIndent();
}
+ synchronized (mCloudMediaProviders) {
+ pw.println();
+ pw.print("Media cloud providers: ");
+ pw.println(mCloudMediaProviders);
+ }
+
pw.println();
pw.print("Last maintenance: ");
pw.println(TimeUtils.formatForLogging(mLastMaintenance));
@@ -4971,6 +5049,9 @@
private final List<StorageManagerInternal.ResetListener> mResetListeners =
new ArrayList<>();
+ private final CopyOnWriteArraySet<StorageManagerInternal.CloudProviderChangeListener>
+ mCloudProviderChangeListeners = new CopyOnWriteArraySet<>();
+
@Override
public boolean isFuseMounted(int userId) {
synchronized (mLock) {
@@ -5193,5 +5274,12 @@
return mCeStoragePreparedUsers.contains(userId);
}
}
+
+ @Override
+ public void registerCloudProviderChangeListener(
+ @NonNull StorageManagerInternal.CloudProviderChangeListener listener) {
+ mCloudProviderChangeListeners.add(listener);
+ mHandler.obtainMessage(H_CLOUD_MEDIA_PROVIDER_CHANGED, listener);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 940ad73..e2e53cb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -20,6 +20,8 @@
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_NONE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY;
import android.annotation.NonNull;
import android.app.ActivityThread;
@@ -203,6 +205,10 @@
private static final boolean DEFAULT_ENABLE_COMPONENT_ALIAS = false;
private static final String DEFAULT_COMPONENT_ALIAS_OVERRIDES = "";
+ private static final int DEFAULT_DEFER_BOOT_COMPLETED_BROADCAST =
+ DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY
+ | DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY;
+
// Flag stored in the DeviceConfig API.
/**
* Maximum number of cached processes.
@@ -292,6 +298,9 @@
*/
private static final String KEY_PROCESS_KILL_TIMEOUT = "process_kill_timeout";
+ private static final String KEY_DEFER_BOOT_COMPLETED_BROADCAST =
+ "defer_boot_completed_broadcast";
+
// Maximum number of cached processes we will allow.
public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
@@ -603,6 +612,15 @@
volatile boolean mEnableComponentAlias = DEFAULT_ENABLE_COMPONENT_ALIAS;
/**
+ * Where or not to defer LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts until the first
+ * time the process of the UID is started.
+ * Defined in {@link BroadcastConstants#DeferBootCompletedBroadcastType}
+ */
+ @GuardedBy("mService")
+ volatile @BroadcastConstants.DeferBootCompletedBroadcastType int mDeferBootCompletedBroadcast =
+ DEFAULT_DEFER_BOOT_COMPLETED_BROADCAST;
+
+ /**
* Defines component aliases. Format
* ComponentName ":" ComponentName ( "," ComponentName ":" ComponentName )*
*/
@@ -846,6 +864,9 @@
case KEY_PROCESS_KILL_TIMEOUT:
updateProcessKillTimeout();
break;
+ case KEY_DEFER_BOOT_COMPLETED_BROADCAST:
+ updateDeferBootCompletedBroadcast();
+ break;
default:
break;
}
@@ -1258,6 +1279,13 @@
}
}
+ private void updateDeferBootCompletedBroadcast() {
+ mDeferBootCompletedBroadcast = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_DEFER_BOOT_COMPLETED_BROADCAST,
+ DEFAULT_DEFER_BOOT_COMPLETED_BROADCAST);
+ }
+
private long[] parseLongArray(@NonNull String key, @NonNull long[] def) {
final String val = DeviceConfig.getString(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
key, null);
@@ -1529,6 +1557,8 @@
pw.print("="); pw.println(mEnableComponentAlias);
pw.print(" "); pw.print(KEY_COMPONENT_ALIAS_OVERRIDES);
pw.print("="); pw.println(mComponentAliasOverrides);
+ pw.print(" "); pw.print(KEY_DEFER_BOOT_COMPLETED_BROADCAST);
+ pw.print("="); pw.println(mDeferBootCompletedBroadcast);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a478c31..2371101 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4856,6 +4856,10 @@
}
}
+ if (!badApp) {
+ updateUidReadyForBootCompletedBroadcastLocked(app.uid);
+ }
+
// Check if a next-broadcast receiver is in this process...
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
@@ -6482,7 +6486,8 @@
return isBackgroundRestrictedNoCheck(callingUid, packageName);
}
- boolean isBackgroundRestrictedNoCheck(final int uid, final String packageName) {
+ @VisibleForTesting
+ public boolean isBackgroundRestrictedNoCheck(final int uid, final String packageName) {
final int mode = getAppOpsManager().checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
uid, packageName);
return mode != AppOpsManager.MODE_ALLOWED;
@@ -12764,6 +12769,13 @@
return didSomething;
}
+ void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ queue.updateUidReadyForBootCompletedBroadcastLocked(uid);
+ queue.scheduleBroadcastsLocked();
+ }
+ }
+
/**
* @deprecated Use {@link #registerReceiverWithFeature}
*/
@@ -13389,10 +13401,8 @@
}
}
- // TODO (206518114): We need to use the "real" package name which sent the broadcast,
- // in case the broadcast is sent via PendingIntent.
if (brOptions.getIdForResponseEvent() > 0) {
- enforceUsageStatsPermission(callerPackage, realCallingUid, realCallingPid,
+ enforceUsageStatsPermission(callerPackage, callingUid, callingPid,
"recordResponseEventWhileInBackground()");
}
}
diff --git a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
index 3c780aa..d1cf004 100644
--- a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
@@ -20,7 +20,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.AppBatteryTracker.BATTERY_USAGE_NONE;
import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX;
-import static com.android.server.am.BaseAppStateDurationsTracker.EVENT_NUM;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_NUM;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -36,9 +36,9 @@
import com.android.server.am.AppBatteryTracker.AppBatteryPolicy;
import com.android.server.am.AppBatteryTracker.BatteryUsage;
import com.android.server.am.AppBatteryTracker.ImmutableBatteryUsage;
-import com.android.server.am.BaseAppStateDurationsTracker.EventListener;
import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent;
import com.android.server.am.BaseAppStateTracker.Injector;
+import com.android.server.am.BaseAppStateTracker.StateListener;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
@@ -57,13 +57,13 @@
*/
final class AppBatteryExemptionTracker
extends BaseAppStateDurationsTracker<AppBatteryExemptionPolicy, UidBatteryStates>
- implements BaseAppStateEvents.Factory<UidBatteryStates>, EventListener {
+ implements BaseAppStateEvents.Factory<UidBatteryStates>, StateListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "AppBatteryExemptionTracker" : TAG_AM;
private static final boolean DEBUG_BACKGROUND_BATTERY_EXEMPTION_TRACKER = false;
// As it's a UID-based tracker, anywhere which requires a package name, use this default name.
- private static final String DEFAULT_NAME = "";
+ static final String DEFAULT_NAME = "";
AppBatteryExemptionTracker(Context context, AppRestrictionController controller) {
this(context, controller, null, null);
@@ -80,9 +80,7 @@
void onSystemReady() {
super.onSystemReady();
mAppRestrictionController.forEachTracker(tracker -> {
- if (tracker instanceof BaseAppStateDurationsTracker) {
- ((BaseAppStateDurationsTracker) tracker).registerEventListener(this);
- }
+ tracker.registerStateListener(this);
});
}
@@ -97,19 +95,20 @@
}
@Override
- public void onNewEvent(int uid, String packageName, boolean start, long now, int eventType) {
+ public void onStateChange(int uid, String packageName, boolean start, long now, int stateType) {
if (!mInjector.getPolicy().isEnabled()) {
return;
}
final ImmutableBatteryUsage batteryUsage = mAppRestrictionController
.getUidBatteryUsage(uid);
+ final int stateTypeIndex = stateTypeToIndex(stateType);
synchronized (mLock) {
UidBatteryStates pkg = mPkgEvents.get(uid, DEFAULT_NAME);
if (pkg == null) {
pkg = createAppStateEvents(uid, DEFAULT_NAME);
mPkgEvents.put(uid, DEFAULT_NAME, pkg);
}
- pkg.addEvent(start, now, batteryUsage, eventType);
+ pkg.addEvent(start, now, batteryUsage, stateTypeIndex);
}
}
@@ -125,7 +124,8 @@
* @return The to-be-exempted battery usage of the given UID in the given duration; it could
* be considered as "exempted" due to various use cases, i.e. media playback.
*/
- ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now) {
+ ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now,
+ int types) {
if (!mInjector.getPolicy().isEnabled()) {
return BATTERY_USAGE_NONE;
}
@@ -135,7 +135,7 @@
if (pkg == null) {
return BATTERY_USAGE_NONE;
}
- result = pkg.getBatteryUsageSince(since, now);
+ result = pkg.getBatteryUsageSince(since, now, types);
}
if (!result.second.isEmpty()) {
// We have an open event (just start, no stop), get the battery usage till now.
@@ -149,7 +149,7 @@
static final class UidBatteryStates extends BaseAppStateDurations<UidStateEventWithBattery> {
UidBatteryStates(int uid, @NonNull String tag,
@NonNull MaxTrackingDurationConfig maxTrackingDurationConfig) {
- super(uid, DEFAULT_NAME, EVENT_NUM, tag, maxTrackingDurationConfig);
+ super(uid, DEFAULT_NAME, STATE_TYPE_NUM, tag, maxTrackingDurationConfig);
}
UidBatteryStates(@NonNull UidBatteryStates other) {
@@ -160,7 +160,7 @@
* @param start {@code true} if it's a start event.
* @param now The timestamp when this event occurred.
* @param batteryUsage The background current drain since the system boots.
- * @param eventType One of EVENT_TYPE_* defined in the class BaseAppStateDurationsTracker.
+ * @param eventType One of STATE_TYPE_INDEX_* defined in the class BaseAppStateTracker.
*/
void addEvent(boolean start, long now, ImmutableBatteryUsage batteryUsage, int eventType) {
if (start) {
@@ -184,17 +184,6 @@
return mEvents[eventType] != null ? mEvents[eventType].peekLast() : null;
}
- /**
- * @return The pair of bg battery usage of given duration; the first value in the pair
- * is the aggregated battery usage of all event pairs in this duration; while
- * the second value is the battery usage since the system boots, if there is
- * an open event(just start, no stop) at the end of the duration.
- */
- Pair<ImmutableBatteryUsage, ImmutableBatteryUsage> getBatteryUsageSince(long since,
- long now, int eventType) {
- return getBatteryUsageSince(since, now, mEvents[eventType]);
- }
-
private Pair<ImmutableBatteryUsage, ImmutableBatteryUsage> getBatteryUsageSince(long since,
long now, LinkedList<UidStateEventWithBattery> events) {
if (events == null || events.size() == 0) {
@@ -217,13 +206,18 @@
}
/**
- * @return The aggregated battery usage amongst all the event types we're tracking.
+ * @return The pair of bg battery usage of given duration; the first value in the pair
+ * is the aggregated battery usage of selected events in this duration; while
+ * the second value is the battery usage since the system boots, if there is
+ * an open event(just start, no stop) at the end of the duration.
*/
Pair<ImmutableBatteryUsage, ImmutableBatteryUsage> getBatteryUsageSince(long since,
- long now) {
+ long now, int types) {
LinkedList<UidStateEventWithBattery> result = new LinkedList<>();
for (int i = 0; i < mEvents.length; i++) {
- result = add(result, mEvents[i]);
+ if ((types & stateIndexToType(i)) != 0) {
+ result = add(result, mEvents[i]);
+ }
}
return getBatteryUsageSince(since, now, result);
}
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 6492662..655e309 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -325,7 +325,8 @@
final int uid = uidConsumers.keyAt(i);
final ImmutableBatteryUsage actualUsage = uidConsumers.valueAt(i);
final ImmutableBatteryUsage exemptedUsage = mAppRestrictionController
- .getUidBatteryExemptedUsageSince(uid, since, now);
+ .getUidBatteryExemptedUsageSince(uid, since, now,
+ bgPolicy.mBgCurrentDrainExemptedTypes);
// It's possible the exemptedUsage could be larger than actualUsage,
// as the former one is an approximate value.
final BatteryUsage bgUsage = actualUsage.mutate()
@@ -656,7 +657,8 @@
final BatteryUsage bgUsage = uidConsumers.valueAt(i)
.calcPercentage(uid, bgPolicy);
final BatteryUsage exemptedUsage = mAppRestrictionController
- .getUidBatteryExemptedUsageSince(uid, since, now)
+ .getUidBatteryExemptedUsageSince(uid, since, now,
+ bgPolicy.mBgCurrentDrainExemptedTypes)
.calcPercentage(uid, bgPolicy);
final BatteryUsage reportedUsage = new BatteryUsage(bgUsage)
.subtract(exemptedUsage)
@@ -1027,6 +1029,21 @@
DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_power_components";
/**
+ * The types of state where we'll exempt its battery usage when it's in that state.
+ * The state here must be one or a combination of STATE_TYPE_* in BaseAppStateTracker.
+ */
+ static final String KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_exempted_types";
+
+ /**
+ * The behavior when an app has the permission ACCESS_BACKGROUND_LOCATION granted,
+ * whether or not the system will use a higher threshold towards its background battery
+ * usage because of it.
+ */
+ static final String KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_high_threshold_by_bg_location";
+
+ /**
* Default value to the {@link #INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD} of
* the {@link #mBgCurrentDrainRestrictedBucketThreshold}.
*/
@@ -1089,6 +1106,16 @@
final int mDefaultBgCurrentDrainPowerComponent;
/**
+ * Default value to {@link #mBgCurrentDrainExmptedTypes}.
+ **/
+ final int mDefaultBgCurrentDrainExemptedTypes;
+
+ /**
+ * Default value to {@link #mBgCurrentDrainHighThresholdByBgLocation}.
+ */
+ final boolean mDefaultBgCurrentDrainHighThresholdByBgLocation;
+
+ /**
* The index to {@link #mBgCurrentDrainRestrictedBucketThreshold}
* and {@link #mBgCurrentDrainBgRestrictedThreshold}.
*/
@@ -1146,6 +1173,16 @@
volatile Dimensions[] mBatteryDimensions;
/**
+ * @see #KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES.
+ */
+ volatile int mBgCurrentDrainExemptedTypes;
+
+ /**
+ * @see #KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION.
+ */
+ volatile boolean mBgCurrentDrainHighThresholdByBgLocation;
+
+ /**
* The capacity of the battery when fully charged in mAh.
*/
private int mBatteryFullChargeMah;
@@ -1201,6 +1238,10 @@
R.integer.config_bg_current_drain_types_to_bg_restricted);
mDefaultBgCurrentDrainPowerComponent = resources.getInteger(
R.integer.config_bg_current_drain_power_components);
+ mDefaultBgCurrentDrainExemptedTypes = resources.getInteger(
+ R.integer.config_bg_current_drain_exempted_types);
+ mDefaultBgCurrentDrainHighThresholdByBgLocation = resources.getBoolean(
+ R.bool.config_bg_current_drain_high_threshold_by_bg_location);
mBgCurrentDrainRestrictedBucketThreshold[0] =
mDefaultBgCurrentDrainRestrictedBucket;
mBgCurrentDrainRestrictedBucketThreshold[1] =
@@ -1230,6 +1271,7 @@
switch (name) {
case KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET:
case KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_BG_RESTRICTED:
+ case KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION:
case KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_RESTRICTED_BUCKET:
case KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_BG_RESTRICTED:
case KEY_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET:
@@ -1249,6 +1291,9 @@
case KEY_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED:
updateCurrentDrainEventDurationBasedThresholdEnabled();
break;
+ case KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES:
+ updateCurrentDrainExemptedTypes();
+ break;
default:
super.onPropertiesChanged(name);
break;
@@ -1305,6 +1350,10 @@
mBatteryDimensions[i] = new Dimensions(mBgCurrentDrainPowerComponents, i);
}
}
+ mBgCurrentDrainHighThresholdByBgLocation =
+ DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION,
+ mDefaultBgCurrentDrainHighThresholdByBgLocation);
}
private void updateCurrentDrainWindow() {
@@ -1335,6 +1384,13 @@
mDefaultBgCurrentDrainEventDurationBasedThresholdEnabled);
}
+ private void updateCurrentDrainExemptedTypes() {
+ mBgCurrentDrainExemptedTypes = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES,
+ mDefaultBgCurrentDrainExemptedTypes);
+ }
+
@Override
public void onSystemReady() {
mBatteryFullChargeMah =
@@ -1345,6 +1401,7 @@
updateCurrentDrainMediaPlaybackMinDuration();
updateCurrentDrainLocationMinDuration();
updateCurrentDrainEventDurationBasedThresholdEnabled();
+ updateCurrentDrainExemptedTypes();
}
@Override
@@ -1454,16 +1511,18 @@
notifyController = true;
} else {
excessive = true;
- if (brPercentage >= mBgCurrentDrainBgRestrictedThreshold[thresholdIndex]) {
+ if (brPercentage >= mBgCurrentDrainBgRestrictedThreshold[thresholdIndex]
+ && curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
// If we're in the restricted standby bucket but still seeing high
// current drains, tell the controller again.
- if (curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET
- && ts[TIME_STAMP_INDEX_BG_RESTRICTED] == 0) {
- if (now > ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET]
- + mBgCurrentDrainWindowMs) {
- ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now;
- notifyController = true;
- }
+ final long lastResbucket = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET];
+ final long lastBgRes = ts[TIME_STAMP_INDEX_BG_RESTRICTED];
+ // If it has been a while since restricting the app and since the last
+ // time we notify the controller, notify it again.
+ if ((now >= lastResbucket + mBgCurrentDrainWindowMs) && (lastBgRes == 0
+ || (now >= lastBgRes + mBgCurrentDrainWindowMs))) {
+ ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now;
+ notifyController = true;
}
}
}
@@ -1505,6 +1564,9 @@
}
private boolean hasLocation(int uid, long now, long window) {
+ if (!mBgCurrentDrainHighThresholdByBgLocation) {
+ return false;
+ }
final AppRestrictionController controller = mTracker.mAppRestrictionController;
if (mInjector.getPermissionManagerServiceInternal().checkUidPermission(
uid, ACCESS_BACKGROUND_LOCATION) == PERMISSION_GRANTED) {
@@ -1618,6 +1680,14 @@
pw.print(KEY_BG_CURRENT_DRAIN_POWER_COMPONENTS);
pw.print('=');
pw.println(mBgCurrentDrainPowerComponents);
+ pw.print(prefix);
+ pw.print(KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES);
+ pw.print('=');
+ pw.println(BaseAppStateTracker.stateTypesToString(mBgCurrentDrainExemptedTypes));
+ pw.print(prefix);
+ pw.print(KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION);
+ pw.print('=');
+ pw.println(mBgCurrentDrainHighThresholdByBgLocation);
pw.print(prefix);
pw.println("Excessive current drain detected:");
diff --git a/services/core/java/com/android/server/am/AppFGSTracker.java b/services/core/java/com/android/server/am/AppFGSTracker.java
index 9c775b3..5ac5a70 100644
--- a/services/core/java/com/android/server/am/AppFGSTracker.java
+++ b/services/core/java/com/android/server/am/AppFGSTracker.java
@@ -16,8 +16,6 @@
package com.android.server.am;
-import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE;
@@ -43,6 +41,7 @@
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -54,6 +53,7 @@
import com.android.server.am.BaseAppStateEventsTracker.BaseAppStateEventsPolicy;
import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent;
import com.android.server.am.BaseAppStateTracker.Injector;
+import com.android.server.notification.NotificationManagerInternal;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
@@ -71,6 +71,9 @@
private final MyHandler mHandler;
+ @GuardedBy("mLock")
+ private final UidProcessMap<ArraySet<Integer>> mFGSNotificationIDs = new UidProcessMap<>();
+
// Unlocked since it's only accessed in single thread.
private final ArrayMap<PackageDurations, Long> mTmpPkgDurations = new ArrayMap<>();
@@ -100,11 +103,19 @@
: MyHandler.MSG_FOREGROUND_SERVICES_STOPPED, pid, uid, packageName).sendToTarget();
}
+ @Override
+ public void onForegroundServiceNotificationUpdated(String packageName, int uid,
+ int foregroundId) {
+ mHandler.obtainMessage(MyHandler.MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED,
+ uid, foregroundId, packageName).sendToTarget();
+ }
+
private static class MyHandler extends Handler {
static final int MSG_FOREGROUND_SERVICES_STARTED = 0;
static final int MSG_FOREGROUND_SERVICES_STOPPED = 1;
static final int MSG_FOREGROUND_SERVICES_CHANGED = 2;
- static final int MSG_CHECK_LONG_RUNNING_FGS = 3;
+ static final int MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED = 3;
+ static final int MSG_CHECK_LONG_RUNNING_FGS = 4;
private final AppFGSTracker mTracker;
@@ -128,6 +139,10 @@
mTracker.handleForegroundServicesChanged(
(String) msg.obj, msg.arg1, msg.arg2);
break;
+ case MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED:
+ mTracker.handleForegroundServiceNotificationUpdated(
+ (String) msg.obj, msg.arg1, msg.arg2);
+ break;
case MSG_CHECK_LONG_RUNNING_FGS:
mTracker.checkLongRunningFgs();
break;
@@ -205,6 +220,44 @@
}
}
+ private void handleForegroundServiceNotificationUpdated(String packageName, int uid,
+ int notificationId) {
+ synchronized (mLock) {
+ if (notificationId > 0) {
+ ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName);
+ if (notificationIDs == null) {
+ notificationIDs = new ArraySet<>();
+ mFGSNotificationIDs.put(uid, packageName, notificationIDs);
+ }
+ notificationIDs.add(notificationId);
+ } else if (notificationId < 0) {
+ final ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName);
+ if (notificationIDs != null) {
+ notificationIDs.remove(-notificationId);
+ if (notificationIDs.isEmpty()) {
+ mFGSNotificationIDs.remove(uid, packageName);
+ }
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean hasForegroundServiceNotificationsLocked(String packageName, int uid) {
+ final ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName);
+ if (notificationIDs == null || notificationIDs.isEmpty()) {
+ return false;
+ }
+ final NotificationManagerInternal nm = mInjector.getNotificationManagerInternal();
+ final int userId = UserHandle.getUserId(uid);
+ for (int i = notificationIDs.size() - 1; i >= 0; i--) {
+ if (nm.isNotificationShown(packageName, null, notificationIDs.valueAt(i), userId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@GuardedBy("mLock")
private void scheduleDurationCheckLocked(long now) {
// Look for the active FGS with longest running time till now.
@@ -375,6 +428,28 @@
}
}
+ boolean hasForegroundServiceNotifications(String packageName, int uid) {
+ synchronized (mLock) {
+ return hasForegroundServiceNotificationsLocked(packageName, uid);
+ }
+ }
+
+ boolean hasForegroundServiceNotifications(int uid) {
+ synchronized (mLock) {
+ final SparseArray<ArrayMap<String, ArraySet<Integer>>> map =
+ mFGSNotificationIDs.getMap();
+ final ArrayMap<String, ArraySet<Integer>> pkgs = map.get(uid);
+ if (pkgs != null) {
+ for (int i = pkgs.size() - 1; i >= 0; i--) {
+ if (hasForegroundServiceNotificationsLocked(pkgs.keyAt(i), uid)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
@Override
void dump(PrintWriter pw, String prefix) {
pw.print(prefix);
@@ -382,6 +457,35 @@
super.dump(pw, " " + prefix);
}
+ @Override
+ void dumpOthers(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.println("APPS WITH ACTIVE FOREGROUND SERVICES:");
+ prefix = " " + prefix;
+ synchronized (mLock) {
+ final SparseArray<ArrayMap<String, ArraySet<Integer>>> map =
+ mFGSNotificationIDs.getMap();
+ if (map.size() == 0) {
+ pw.print(prefix);
+ pw.println("(none)");
+ }
+ for (int i = 0, size = map.size(); i < size; i++) {
+ final int uid = map.keyAt(i);
+ final String uidString = UserHandle.formatUid(uid);
+ final ArrayMap<String, ArraySet<Integer>> pkgs = map.valueAt(i);
+ for (int j = 0, numOfPkgs = pkgs.size(); j < numOfPkgs; j++) {
+ final String pkgName = pkgs.keyAt(j);
+ pw.print(prefix);
+ pw.print(pkgName);
+ pw.print('/');
+ pw.print(uidString);
+ pw.print(" notification=");
+ pw.println(hasForegroundServiceNotificationsLocked(pkgName, uid));
+ }
+ }
+ }
+ }
+
/**
* Tracks the durations with active FGS for a given package.
*/
@@ -437,7 +541,7 @@
}
if (isActive(i)) {
mEvents[i].add(new BaseTimeEvent(now));
- notifyListenersOnEventIfNecessary(false, now,
+ notifyListenersOnStateChangeIfNecessary(false, now,
indexToForegroundServiceType(i));
}
}
@@ -463,13 +567,13 @@
}
if (!isActive(i)) {
mEvents[i].add(new BaseTimeEvent(now));
- notifyListenersOnEventIfNecessary(true, now, serviceType);
+ notifyListenersOnStateChangeIfNecessary(true, now, serviceType);
}
} else {
// Stop this type.
if (mEvents[i] != null && isActive(i)) {
mEvents[i].add(new BaseTimeEvent(now));
- notifyListenersOnEventIfNecessary(false, now, serviceType);
+ notifyListenersOnStateChangeIfNecessary(false, now, serviceType);
}
}
changes &= ~serviceType;
@@ -478,20 +582,20 @@
mForegroundServiceTypes = serviceTypes;
}
- private void notifyListenersOnEventIfNecessary(boolean start, long now,
+ private void notifyListenersOnStateChangeIfNecessary(boolean start, long now,
@ForegroundServiceType int serviceType) {
- int eventType;
+ int stateType;
switch (serviceType) {
case FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK:
- eventType = BaseAppStateDurationsTracker.EVENT_TYPE_FGS_MEDIA_PLAYBACK;
+ stateType = BaseAppStateDurationsTracker.STATE_TYPE_FGS_MEDIA_PLAYBACK;
break;
case FOREGROUND_SERVICE_TYPE_LOCATION:
- eventType = BaseAppStateDurationsTracker.EVENT_TYPE_FGS_LOCATION;
+ stateType = BaseAppStateDurationsTracker.STATE_TYPE_FGS_LOCATION;
break;
default:
return;
}
- mTracker.notifyListenersOnEvent(mUid, mPackageName, start, now, eventType);
+ mTracker.notifyListenersOnStateChange(mUid, mPackageName, start, now, stateType);
}
void setIsLongRunning(boolean isLongRunning) {
@@ -687,10 +791,6 @@
if (shouldExemptLocationFGS(packageName, uid, now, since)) {
return;
}
- if (hasBackgroundLocationPermission(packageName, uid)) {
- // This package has background location permission, ignore it.
- return;
- }
mTracker.mAppRestrictionController.postLongRunningFgsIfNecessary(packageName, uid);
}
@@ -723,19 +823,6 @@
return false;
}
- boolean hasBackgroundLocationPermission(String packageName, int uid) {
- if (mInjector.getPermissionManagerServiceInternal().checkPermission(
- packageName, ACCESS_BACKGROUND_LOCATION, UserHandle.getUserId(uid))
- == PERMISSION_GRANTED) {
- if (DEBUG_BACKGROUND_FGS_TRACKER) {
- Slog.i(TAG, "Ignoring bg-location FGS in " + packageName + "/"
- + UserHandle.formatUid(uid));
- }
- return true;
- }
- return false;
- }
-
@Override
String getExemptionReasonString(String packageName, int uid, @ReasonCode int reason) {
if (reason != REASON_DENIED) {
@@ -745,8 +832,7 @@
final long window = getFgsLongRunningWindowSize();
final long since = Math.max(0, now - getFgsLongRunningWindowSize());
return "{mediaPlayback=" + shouldExemptMediaPlaybackFGS(packageName, uid, now, window)
- + ", location=" + shouldExemptLocationFGS(packageName, uid, now, since)
- + ", bgLocationPerm=" + hasBackgroundLocationPermission(packageName, uid) + "}";
+ + ", location=" + shouldExemptLocationFGS(packageName, uid, now, since) + "}";
}
void onLongRunningFgsGone(String packageName, int uid) {
diff --git a/services/core/java/com/android/server/am/AppMediaSessionTracker.java b/services/core/java/com/android/server/am/AppMediaSessionTracker.java
index 3914f6f..4ce23f7 100644
--- a/services/core/java/com/android/server/am/AppMediaSessionTracker.java
+++ b/services/core/java/com/android/server/am/AppMediaSessionTracker.java
@@ -19,8 +19,8 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX;
-import static com.android.server.am.BaseAppStateDurationsTracker.EVENT_TYPE_MEDIA_SESSION;
import static com.android.server.am.BaseAppStateTracker.ONE_DAY;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_MEDIA_SESSION;
import android.annotation.NonNull;
import android.content.Context;
@@ -104,8 +104,8 @@
}
if (!pkg.isActive()) {
pkg.addEvent(true, now);
- notifyListenersOnEvent(pkg.mUid, pkg.mPackageName, true, now,
- EVENT_TYPE_MEDIA_SESSION);
+ notifyListenersOnStateChange(pkg.mUid, pkg.mPackageName, true, now,
+ STATE_TYPE_MEDIA_SESSION);
}
// Mark it as active, so we could filter out inactive ones below.
mTmpMediaControllers.put(packageName, uid, Boolean.TRUE);
@@ -127,8 +127,8 @@
&& mTmpMediaControllers.get(pkg.mPackageName, pkg.mUid) == null) {
// This package has removed its controller, issue a stop event.
pkg.addEvent(false, now);
- notifyListenersOnEvent(pkg.mUid, pkg.mPackageName, false, now,
- EVENT_TYPE_MEDIA_SESSION);
+ notifyListenersOnStateChange(pkg.mUid, pkg.mPackageName, false, now,
+ STATE_TYPE_MEDIA_SESSION);
}
}
}
@@ -146,8 +146,8 @@
final SimplePackageDurations pkg = val.valueAt(j);
if (pkg.isActive()) {
pkg.addEvent(false, now);
- notifyListenersOnEvent(pkg.mUid, pkg.mPackageName, false, now,
- EVENT_TYPE_MEDIA_SESSION);
+ notifyListenersOnStateChange(pkg.mUid, pkg.mPackageName, false, now,
+ STATE_TYPE_MEDIA_SESSION);
}
}
}
diff --git a/services/core/java/com/android/server/am/AppPermissionTracker.java b/services/core/java/com/android/server/am/AppPermissionTracker.java
new file mode 100644
index 0000000..7f48d52
--- /dev/null
+++ b/services/core/java/com/android/server/am/AppPermissionTracker.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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.am;
+
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.SYSTEM_UID;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.AppBatteryExemptionTracker.DEFAULT_NAME;
+import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_PERMISSION;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.OnPermissionsChangedListener;
+import android.content.pm.PackageManagerInternal;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.permission.PermissionManager;
+import android.provider.DeviceConfig;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.am.AppPermissionTracker.AppPermissionPolicy;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The tracker for monitoring selected permission state of apps.
+ */
+final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy>
+ implements OnPermissionsChangedListener {
+ static final String TAG = TAG_WITH_CLASS_NAME ? "AppPermissionTracker" : TAG_AM;
+
+ static final boolean DEBUG_PERMISSION_TRACKER = false;
+
+ private final MyHandler mHandler;
+
+ @GuardedBy("mLock")
+ private SparseArray<ArraySet<String>> mUidGrantedPermissionsInMonitor = new SparseArray<>();
+
+ AppPermissionTracker(Context context, AppRestrictionController controller) {
+ this(context, controller, null, null);
+ }
+
+ AppPermissionTracker(Context context, AppRestrictionController controller,
+ Constructor<? extends Injector<AppPermissionPolicy>> injector, Object outerContext) {
+ super(context, controller, injector, outerContext);
+ mHandler = new MyHandler(this);
+ mInjector.setPolicy(new AppPermissionPolicy(mInjector, this));
+ }
+
+ @Override
+ public void onPermissionsChanged(int uid) {
+ mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_CHANGED, uid, 0).sendToTarget();
+ }
+
+ private void handlePermissionsInit() {
+ final int[] allUsers = mInjector.getUserManagerInternal().getUserIds();
+ final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
+ final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal();
+ final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+ for (int userId : allUsers) {
+ final List<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, SYSTEM_UID);
+ if (apps == null) {
+ continue;
+ }
+ synchronized (mLock) {
+ final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor;
+ final long now = SystemClock.elapsedRealtime();
+ for (int i = 0, size = apps.size(); i < size; i++) {
+ final ApplicationInfo ai = apps.get(i);
+ for (String permission : permissions) {
+ if (pm.checkUidPermission(ai.uid, permission) != PERMISSION_GRANTED) {
+ continue;
+ }
+ ArraySet<String> grantedPermissions = uidPerms.get(ai.uid);
+ if (grantedPermissions == null) {
+ grantedPermissions = new ArraySet<String>();
+ uidPerms.put(ai.uid, grantedPermissions);
+ }
+ grantedPermissions.add(permission);
+ notifyListenersOnStateChange(ai.uid, DEFAULT_NAME, true, now,
+ STATE_TYPE_PERMISSION);
+ }
+ }
+ }
+ }
+ }
+
+ private void handlePermissionsDestroy() {
+ synchronized (mLock) {
+ final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor;
+ final long now = SystemClock.elapsedRealtime();
+ for (int i = 0, size = uidPerms.size(); i < size; i++) {
+ final int uid = uidPerms.keyAt(i);
+ final ArraySet<String> grantedPermissions = uidPerms.valueAt(i);
+ for (int j = 0, numOfPerms = grantedPermissions.size(); j < numOfPerms; j++) {
+ notifyListenersOnStateChange(uid, DEFAULT_NAME, false, now,
+ STATE_TYPE_PERMISSION);
+ }
+ }
+ uidPerms.clear();
+ }
+ }
+
+ private void handlePermissionsChanged(int uid) {
+ final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+ if (permissions != null && permissions.length > 0) {
+ synchronized (mLock) {
+ handlePermissionsChangedLocked(uid);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void handlePermissionsChangedLocked(int uid) {
+ final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal();
+ final int index = mUidGrantedPermissionsInMonitor.indexOfKey(uid);
+ ArraySet<String> grantedPermissions = index >= 0
+ ? mUidGrantedPermissionsInMonitor.valueAt(index) : null;
+ final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+ final long now = SystemClock.elapsedRealtime();
+ for (String permission: permissions) {
+ boolean granted = pm.checkUidPermission(uid, permission) == PERMISSION_GRANTED;
+ if (DEBUG_PERMISSION_TRACKER) {
+ Slog.i(TAG, UserHandle.formatUid(uid) + " " + permission + "=" + granted);
+ }
+ boolean changed = false;
+ if (granted) {
+ if (grantedPermissions == null) {
+ grantedPermissions = new ArraySet<>();
+ mUidGrantedPermissionsInMonitor.put(uid, grantedPermissions);
+ }
+ changed = grantedPermissions.add(permission);
+ } else if (grantedPermissions != null) {
+ changed = grantedPermissions.remove(permission);
+ if (grantedPermissions.isEmpty()) {
+ mUidGrantedPermissionsInMonitor.removeAt(index);
+ }
+ }
+ if (changed) {
+ notifyListenersOnStateChange(uid, DEFAULT_NAME, granted, now,
+ STATE_TYPE_PERMISSION);
+ }
+ }
+ }
+
+ private static class MyHandler extends Handler {
+ static final int MSG_PERMISSIONS_INIT = 0;
+ static final int MSG_PERMISSIONS_DESTROY = 1;
+ static final int MSG_PERMISSIONS_CHANGED = 2;
+
+ private @NonNull AppPermissionTracker mTracker;
+
+ MyHandler(@NonNull AppPermissionTracker tracker) {
+ super(tracker.mBgHandler.getLooper());
+ mTracker = tracker;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PERMISSIONS_INIT:
+ mTracker.handlePermissionsInit();
+ break;
+ case MSG_PERMISSIONS_DESTROY:
+ mTracker.handlePermissionsDestroy();
+ break;
+ case MSG_PERMISSIONS_CHANGED:
+ mTracker.handlePermissionsChanged(msg.arg1);
+ break;
+ }
+ }
+ }
+
+ private void onPermissionTrackerEnabled(boolean enabled) {
+ final PermissionManager pm = mInjector.getPermissionManager();
+ if (enabled) {
+ pm.addOnPermissionsChangeListener(this);
+ mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_INIT).sendToTarget();
+ } else {
+ pm.removeOnPermissionsChangeListener(this);
+ mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_DESTROY).sendToTarget();
+ }
+ }
+
+ @Override
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.println("APP PERMISSIONS TRACKER:");
+ final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+ final String prefixMore = " " + prefix;
+ final String prefixMoreMore = " " + prefixMore;
+ for (String permission : permissions) {
+ pw.print(prefixMore);
+ pw.print(permission);
+ pw.println(':');
+ synchronized (mLock) {
+ final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor;
+ pw.print(prefixMoreMore);
+ pw.print('[');
+ boolean needDelimiter = false;
+ for (int i = 0, size = uidPerms.size(); i < size; i++) {
+ if (uidPerms.valueAt(i).contains(permission)) {
+ if (needDelimiter) {
+ pw.print(',');
+ }
+ needDelimiter = true;
+ pw.print(UserHandle.formatUid(uidPerms.keyAt(i)));
+ }
+ }
+ pw.println(']');
+ }
+ }
+ super.dump(pw, prefix);
+ }
+
+ static final class AppPermissionPolicy extends BaseAppStatePolicy<AppPermissionTracker> {
+ /**
+ * Whether or not we should enable the monitoring on app permissions.
+ */
+ static final String KEY_BG_PERMISSION_MONITOR_ENABLED =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "permission_monitor_enabled";
+
+ /**
+ * The names of the permissions we're monitoring its changes.
+ */
+ static final String KEY_BG_PERMISSIONS_IN_MONITOR =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "permission_in_monitor";
+
+ /**
+ * Default value to {@link #mTrackerEnabled}.
+ */
+ static final boolean DEFAULT_BG_PERMISSION_MONITOR_ENABLED = true;
+
+ /**
+ * Default value to {@link #mBgPermissionsInMonitor}.
+ */
+ static final String[] DEFAULT_BG_PERMISSIONS_IN_MONITOR = new String[] {
+ ACCESS_FINE_LOCATION,
+ };
+
+ /**
+ * @see #KEY_BG_PERMISSIONS_IN_MONITOR.
+ */
+ volatile String[] mBgPermissionsInMonitor = DEFAULT_BG_PERMISSIONS_IN_MONITOR;
+
+ AppPermissionPolicy(@NonNull Injector injector, @NonNull AppPermissionTracker tracker) {
+ super(injector, tracker, KEY_BG_PERMISSION_MONITOR_ENABLED,
+ DEFAULT_BG_PERMISSION_MONITOR_ENABLED);
+ }
+
+ @Override
+ public void onSystemReady() {
+ super.onSystemReady();
+ updateBgPermissionsInMonitor();
+ }
+
+ @Override
+ public void onPropertiesChanged(String name) {
+ switch (name) {
+ case KEY_BG_PERMISSIONS_IN_MONITOR:
+ updateBgPermissionsInMonitor();
+ break;
+ default:
+ super.onPropertiesChanged(name);
+ break;
+ }
+ }
+
+ String[] getBgPermissionsInMonitor() {
+ return mBgPermissionsInMonitor;
+ }
+
+ private void updateBgPermissionsInMonitor() {
+ final String config = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BG_PERMISSIONS_IN_MONITOR,
+ null);
+ mBgPermissionsInMonitor = config != null
+ ? config.split(",") : DEFAULT_BG_PERMISSIONS_IN_MONITOR;
+ }
+
+ @Override
+ public void onTrackerEnabled(boolean enabled) {
+ mTracker.onPermissionTrackerEnabled(enabled);
+ }
+
+ @Override
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.println("APP PERMISSION TRACKER POLICY SETTINGS:");
+ prefix = " " + prefix;
+ super.dump(pw, prefix);
+ pw.print(prefix);
+ pw.print(KEY_BG_PERMISSIONS_IN_MONITOR);
+ pw.print('=');
+ pw.println(Arrays.toString(mBgPermissionsInMonitor));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index a3aa129..1129c19 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -69,6 +69,7 @@
import static android.os.PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
import static android.os.PowerExemptionManager.REASON_SYSTEM_MODULE;
import static android.os.PowerExemptionManager.REASON_SYSTEM_UID;
+import static android.os.PowerExemptionManager.reasonCodeToString;
import static android.os.Process.SYSTEM_UID;
import static com.android.internal.notification.SystemNotificationChannels.ABUSIVE_BACKGROUND_APPS;
@@ -157,6 +158,7 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;
@@ -329,6 +331,8 @@
}
}
}
+ pw.print(" effectiveExemption=");
+ pw.print(reasonCodeToString(getBackgroundRestrictionExemptionReason(mUid)));
}
String getPackageName() {
@@ -547,17 +551,50 @@
static final String KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL =
DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "abusive_notification_minimal_interval";
+ /**
+ * The behavior for an app with a FGS and its notification is still showing, when the system
+ * detects it's abusive and should be put into bg restricted level. {@code true} - we'll
+ * show the prompt to user, {@code false} - we'll not show it.
+ */
+ static final String KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "prompt_fgs_with_noti_to_bg_restricted";
+
+ /**
+ * The list of packages to be exempted from all these background restrictions.
+ */
+ static final String KEY_BG_RESTRICTION_EXEMPTED_PACKAGES =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "restriction_exempted_packages";
+
static final boolean DEFAULT_BG_AUTO_RESTRICTED_BUCKET_ON_BG_RESTRICTION = false;
static final long DEFAULT_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL_MS = 24 * 60 * 60 * 1000;
+ /**
+ * Default value to {@link #mBgPromptFgsWithNotiToBgRestricted}.
+ */
+ final boolean mDefaultBgPromptFgsWithNotiToBgRestricted;
+
volatile boolean mBgAutoRestrictedBucket;
volatile boolean mRestrictedBucketEnabled;
volatile long mBgNotificationMinIntervalMs;
- ConstantsObserver(Handler handler) {
+ /**
+ * @see #KEY_BG_RESTRICTION_EXEMPTED_PACKAGES.
+ *
+ *<p> Mutations on them would result in copy-on-write.</p>
+ */
+ volatile Set<String> mBgRestrictionExemptedPackages = Collections.emptySet();
+
+ /**
+ * @see #KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED.
+ */
+ volatile boolean mBgPromptFgsWithNotiToBgRestricted;
+
+ ConstantsObserver(Handler handler, Context context) {
super(handler);
+ mDefaultBgPromptFgsWithNotiToBgRestricted = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_bg_prompt_fgs_with_noti_to_bg_restricted);
}
@Override
@@ -573,6 +610,12 @@
case KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL:
updateBgAbusiveNotificationMinimalInterval();
break;
+ case KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED:
+ updateBgPromptFgsWithNotiToBgRestricted();
+ break;
+ case KEY_BG_RESTRICTION_EXEMPTED_PACKAGES:
+ updateBgRestrictionExemptedPackages();
+ break;
}
AppRestrictionController.this.onPropertiesChanged(name);
}
@@ -604,6 +647,8 @@
void updateDeviceConfig() {
updateBgAutoRestrictedBucketChanged();
updateBgAbusiveNotificationMinimalInterval();
+ updateBgPromptFgsWithNotiToBgRestricted();
+ updateBgRestrictionExemptedPackages();
}
private void updateBgAutoRestrictedBucketChanged() {
@@ -623,6 +668,53 @@
KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL,
DEFAULT_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL_MS);
}
+
+ private void updateBgPromptFgsWithNotiToBgRestricted() {
+ mBgPromptFgsWithNotiToBgRestricted = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED,
+ mDefaultBgPromptFgsWithNotiToBgRestricted);
+ }
+
+ private void updateBgRestrictionExemptedPackages() {
+ final String settings = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BG_RESTRICTION_EXEMPTED_PACKAGES,
+ null);
+ if (settings == null) {
+ mBgRestrictionExemptedPackages = Collections.emptySet();
+ return;
+ }
+ final String[] settingsList = settings.split(",");
+ final ArraySet<String> packages = new ArraySet<>();
+ for (String pkg : settingsList) {
+ packages.add(pkg);
+ }
+ mBgRestrictionExemptedPackages = Collections.unmodifiableSet(packages);
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.println("BACKGROUND RESTRICTION POLICY SETTINGS:");
+ final String indent = " ";
+ prefix = indent + prefix;
+ pw.print(prefix);
+ pw.print(KEY_BG_AUTO_RESTRICTED_BUCKET_ON_BG_RESTRICTION);
+ pw.print('=');
+ pw.println(mBgAutoRestrictedBucket);
+ pw.print(prefix);
+ pw.print(KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL);
+ pw.print('=');
+ pw.println(mBgNotificationMinIntervalMs);
+ pw.print(prefix);
+ pw.print(KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED);
+ pw.print('=');
+ pw.println(mBgPromptFgsWithNotiToBgRestricted);
+ pw.print(prefix);
+ pw.print(KEY_BG_RESTRICTION_EXEMPTED_PACKAGES);
+ pw.print('=');
+ pw.println(mBgRestrictionExemptedPackages.toString());
+ }
}
private final ConstantsObserver mConstantsObserver;
@@ -704,7 +796,7 @@
mBgHandlerThread.start();
mBgHandler = new BgHandler(mBgHandlerThread.getLooper(), injector);
mBgExecutor = new HandlerExecutor(mBgHandler);
- mConstantsObserver = new ConstantsObserver(mBgHandler);
+ mConstantsObserver = new ConstantsObserver(mBgHandler, mContext);
mNotificationHelper = new NotificationHelper(this);
injector.initAppStateTrackers(this);
}
@@ -1074,12 +1166,27 @@
}
/**
+ * @return If the given package/uid has a foreground service notification or not.
+ */
+ boolean hasForegroundServiceNotifications(String packageName, int uid) {
+ return mInjector.getAppFGSTracker().hasForegroundServiceNotifications(packageName, uid);
+ }
+
+ /**
+ * @return If the given uid has a foreground service notification or not.
+ */
+ boolean hasForegroundServiceNotifications(int uid) {
+ return mInjector.getAppFGSTracker().hasForegroundServiceNotifications(uid);
+ }
+
+ /**
* @return The to-be-exempted battery usage of the given UID in the given duration; it could
* be considered as "exempted" due to various use cases, i.e. media playback.
*/
- ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now) {
+ ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now,
+ int types) {
return mInjector.getAppBatteryExemptionTracker()
- .getUidBatteryExemptedUsageSince(uid, since, now);
+ .getUidBatteryExemptedUsageSince(uid, since, now, types);
}
/**
@@ -1098,11 +1205,14 @@
void dump(PrintWriter pw, String prefix) {
pw.print(prefix);
- pw.println("BACKGROUND RESTRICTION LEVEL SETTINGS");
+ pw.println("APP BACKGROUND RESTRICTIONS");
prefix = " " + prefix;
+ pw.print(prefix);
+ pw.println("BACKGROUND RESTRICTION LEVEL SETTINGS");
synchronized (mLock) {
- mRestrictionSettings.dumpLocked(pw, prefix);
+ mRestrictionSettings.dumpLocked(pw, " " + prefix);
}
+ mConstantsObserver.dump(pw, " " + prefix);
for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
pw.println();
mAppStateTrackers.get(i).dump(pw, prefix);
@@ -1366,8 +1476,21 @@
intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, null,
UserHandle.of(UserHandle.getUserId(uid)));
Notification.Action[] actions = null;
- if (ENABLE_SHOW_FOREGROUND_SERVICE_MANAGER
- && mBgController.hasForegroundServices(packageName, uid)) {
+ final boolean hasForegroundServices =
+ mBgController.hasForegroundServices(packageName, uid);
+ final boolean hasForegroundServiceNotifications =
+ mBgController.hasForegroundServiceNotifications(packageName, uid);
+ if (!mBgController.mConstantsObserver.mBgPromptFgsWithNotiToBgRestricted) {
+ // We're not going to prompt the user if the FGS is active and its notification
+ // is still showing (not dismissed/silenced/denied).
+ if (hasForegroundServices && hasForegroundServiceNotifications) {
+ if (DEBUG_BG_RESTRICTION_CONTROLLER) {
+ Slog.i(TAG, "Not requesting bg-restriction due to FGS with notification");
+ }
+ return;
+ }
+ }
+ if (ENABLE_SHOW_FOREGROUND_SERVICE_MANAGER && hasForegroundServices) {
final Intent trampoline = new Intent(ACTION_FGS_MANAGER_TRAMPOLINE);
trampoline.setPackage("android");
trampoline.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
@@ -1646,6 +1769,8 @@
return REASON_CARRIER_PRIVILEGED_APP;
} else if (isExemptedFromSysConfig(pkg)) {
return REASON_SYSTEM_ALLOW_LISTED;
+ } else if (mConstantsObserver.mBgRestrictionExemptedPackages.contains(pkg)) {
+ return REASON_ALLOWLISTED_PACKAGE;
}
}
}
@@ -1864,6 +1989,7 @@
private AppBatteryExemptionTracker mAppBatteryExemptionTracker;
private AppFGSTracker mAppFGSTracker;
private AppMediaSessionTracker mAppMediaSessionTracker;
+ private AppPermissionTracker mAppPermissionTracker;
private TelephonyManager mTelephonyManager;
Injector(Context context) {
@@ -1880,10 +2006,12 @@
mAppBatteryExemptionTracker = new AppBatteryExemptionTracker(mContext, controller);
mAppFGSTracker = new AppFGSTracker(mContext, controller);
mAppMediaSessionTracker = new AppMediaSessionTracker(mContext, controller);
+ mAppPermissionTracker = new AppPermissionTracker(mContext, controller);
controller.mAppStateTrackers.add(mAppBatteryTracker);
controller.mAppStateTrackers.add(mAppBatteryExemptionTracker);
controller.mAppStateTrackers.add(mAppFGSTracker);
controller.mAppStateTrackers.add(mAppMediaSessionTracker);
+ controller.mAppStateTrackers.add(mAppPermissionTracker);
controller.mAppStateTrackers.add(new AppBroadcastEventsTracker(mContext, controller));
controller.mAppStateTrackers.add(new AppBindServiceEventsTracker(mContext, controller));
}
@@ -1991,6 +2119,10 @@
return mAppBatteryExemptionTracker;
}
+ AppPermissionTracker getAppPermissionTracker() {
+ return mAppPermissionTracker;
+ }
+
String getPackageName(int pid) {
final ActivityManagerService am = getActivityManagerService();
final ProcessRecord app;
diff --git a/services/core/java/com/android/server/am/BaseAppStateDurationsTracker.java b/services/core/java/com/android/server/am/BaseAppStateDurationsTracker.java
index cc89e84..80a219d4 100644
--- a/services/core/java/com/android/server/am/BaseAppStateDurationsTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateDurationsTracker.java
@@ -19,7 +19,6 @@
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
-import android.annotation.NonNull;
import android.content.Context;
import android.os.SystemClock;
import android.util.SparseArray;
@@ -32,7 +31,6 @@
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
-import java.util.ArrayList;
import java.util.LinkedList;
/**
@@ -43,20 +41,9 @@
extends BaseAppStateEventsTracker<T, U> {
static final boolean DEBUG_BASE_APP_STATE_DURATION_TRACKER = false;
- static final int EVENT_TYPE_MEDIA_SESSION = 0;
- static final int EVENT_TYPE_FGS_MEDIA_PLAYBACK = 1;
- static final int EVENT_TYPE_FGS_LOCATION = 2;
- static final int EVENT_NUM = 3;
-
- final ArrayList<EventListener> mEventListeners = new ArrayList<>();
-
@GuardedBy("mLock")
final SparseArray<UidStateDurations> mUidStateDurations = new SparseArray<>();
- interface EventListener {
- void onNewEvent(int uid, String packageName, boolean start, long now, int eventType);
- }
-
BaseAppStateDurationsTracker(Context context, AppRestrictionController controller,
Constructor<? extends Injector<T>> injector, Object outerContext) {
super(context, controller, injector, outerContext);
@@ -104,21 +91,6 @@
mUidStateDurations.remove(uid);
}
- void registerEventListener(@NonNull EventListener listener) {
- synchronized (mLock) {
- mEventListeners.add(listener);
- }
- }
-
- void notifyListenersOnEvent(int uid, String packageName,
- boolean start, long now, int eventType) {
- synchronized (mLock) {
- for (int i = 0, size = mEventListeners.size(); i < size; i++) {
- mEventListeners.get(i).onNewEvent(uid, packageName, start, now, eventType);
- }
- }
- }
-
long getTotalDurations(String packageName, int uid, long now, int index, boolean bgOnly) {
synchronized (mLock) {
final U durations = mPkgEvents.get(uid, packageName);
diff --git a/services/core/java/com/android/server/am/BaseAppStateEventsTracker.java b/services/core/java/com/android/server/am/BaseAppStateEventsTracker.java
index 3e1bcae..c6900b2 100644
--- a/services/core/java/com/android/server/am/BaseAppStateEventsTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateEventsTracker.java
@@ -184,9 +184,13 @@
}
}
}
+ dumpOthers(pw, prefix);
policy.dump(pw, prefix);
}
+ void dumpOthers(PrintWriter pw, String prefix) {
+ }
+
@GuardedBy("mLock")
void dumpEventHeaderLocked(PrintWriter pw, String prefix, String packageName, int uid, U events,
T policy) {
diff --git a/services/core/java/com/android/server/am/BaseAppStateTracker.java b/services/core/java/com/android/server/am/BaseAppStateTracker.java
index 2846f6c..482d697 100644
--- a/services/core/java/com/android/server/am/BaseAppStateTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateTracker.java
@@ -28,19 +28,23 @@
import android.app.role.RoleManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.media.session.MediaSessionManager;
import android.os.BatteryManagerInternal;
import android.os.BatteryStatsInternal;
import android.os.Handler;
+import android.permission.PermissionManager;
import android.util.Slog;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
+import com.android.server.notification.NotificationManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
+import java.util.ArrayList;
/**
* Base class to track certain state of the app, could be used to determine the restriction level.
@@ -54,11 +58,27 @@
static final long ONE_HOUR = 60 * ONE_MINUTE;
static final long ONE_DAY = 24 * ONE_HOUR;
+ static final int STATE_TYPE_MEDIA_SESSION = 1;
+ static final int STATE_TYPE_FGS_MEDIA_PLAYBACK = 1 << 1;
+ static final int STATE_TYPE_FGS_LOCATION = 1 << 2;
+ static final int STATE_TYPE_PERMISSION = 1 << 3;
+ static final int STATE_TYPE_NUM = 4;
+
+ static final int STATE_TYPE_INDEX_MEDIA_SESSION = 0;
+ static final int STATE_TYPE_INDEX_FGS_MEDIA_PLAYBACK = 1;
+ static final int STATE_TYPE_INDEX_FGS_LOCATION = 2;
+ static final int STATE_TYPE_INDEX_PERMISSION = 3;
+
protected final AppRestrictionController mAppRestrictionController;
protected final Injector<T> mInjector;
protected final Context mContext;
protected final Handler mBgHandler;
protected final Object mLock;
+ protected final ArrayList<StateListener> mStateListeners = new ArrayList<>();
+
+ interface StateListener {
+ void onStateChange(int uid, String packageName, boolean start, long now, int stateType);
+ }
BaseAppStateTracker(Context context, AppRestrictionController controller,
@Nullable Constructor<? extends Injector<T>> injector, Object outerContext) {
@@ -79,6 +99,60 @@
}
}
+ static int stateTypeToIndex(int stateType) {
+ return Integer.numberOfTrailingZeros(stateType);
+ }
+
+ static int stateIndexToType(int stateTypeIndex) {
+ return 1 << stateTypeIndex;
+ }
+
+ static String stateTypesToString(int stateTypes) {
+ final StringBuilder sb = new StringBuilder("[");
+ boolean needDelimiter = false;
+ for (int stateType = Integer.highestOneBit(stateTypes); stateType != 0;
+ stateType = Integer.highestOneBit(stateTypes)) {
+ if (needDelimiter) {
+ sb.append('|');
+ }
+ needDelimiter = true;
+ switch (stateType) {
+ case STATE_TYPE_MEDIA_SESSION:
+ sb.append("MEDIA_SESSION");
+ break;
+ case STATE_TYPE_FGS_MEDIA_PLAYBACK:
+ sb.append("FGS_MEDIA_PLAYBACK");
+ break;
+ case STATE_TYPE_FGS_LOCATION:
+ sb.append("FGS_LOCATION");
+ break;
+ case STATE_TYPE_PERMISSION:
+ sb.append("PERMISSION");
+ break;
+ default:
+ return "[UNKNOWN(" + Integer.toHexString(stateTypes) + ")]";
+ }
+ stateTypes &= ~stateType;
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ void registerStateListener(@NonNull StateListener listener) {
+ synchronized (mLock) {
+ mStateListeners.add(listener);
+ }
+ }
+
+ void notifyListenersOnStateChange(int uid, String packageName,
+ boolean start, long now, int stateType) {
+ synchronized (mLock) {
+ for (int i = 0, size = mStateListeners.size(); i < size; i++) {
+ mStateListeners.get(i).onStateChange(uid, packageName, start, now, stateType);
+ }
+ }
+ }
+
/**
* Return the policy holder of this tracker.
*/
@@ -179,10 +253,13 @@
DeviceIdleInternal mDeviceIdleInternal;
UserManagerInternal mUserManagerInternal;
PackageManager mPackageManager;
+ PackageManagerInternal mPackageManagerInternal;
+ PermissionManager mPermissionManager;
PermissionManagerServiceInternal mPermissionManagerServiceInternal;
AppOpsManager mAppOpsManager;
MediaSessionManager mMediaSessionManager;
RoleManager mRoleManager;
+ NotificationManagerInternal mNotificationManagerInternal;
void setPolicy(T policy) {
mAppStatePolicy = policy;
@@ -194,13 +271,17 @@
mBatteryStatsInternal = LocalServices.getService(BatteryStatsInternal.class);
mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mPermissionManagerServiceInternal = LocalServices.getService(
PermissionManagerServiceInternal.class);
final Context context = mAppStatePolicy.mTracker.mContext;
mPackageManager = context.getPackageManager();
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
+ mPermissionManager = context.getSystemService(PermissionManager.class);
mRoleManager = context.getSystemService(RoleManager.class);
+ mNotificationManagerInternal = LocalServices.getService(
+ NotificationManagerInternal.class);
getPolicy().onSystemReady();
}
@@ -240,6 +321,14 @@
return mPackageManager;
}
+ PackageManagerInternal getPackageManagerInternal() {
+ return mPackageManagerInternal;
+ }
+
+ PermissionManager getPermissionManager() {
+ return mPermissionManager;
+ }
+
PermissionManagerServiceInternal getPermissionManagerServiceInternal() {
return mPermissionManagerServiceInternal;
}
@@ -259,5 +348,9 @@
RoleManager getRoleManager() {
return mRoleManager;
}
+
+ NotificationManagerInternal getNotificationManagerInternal() {
+ return mNotificationManagerInternal;
+ }
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index e580327..2ebe0b4 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -16,6 +16,10 @@
package com.android.server.am;
+import android.annotation.IntDef;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.Overridable;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.os.Build;
@@ -26,6 +30,8 @@
import android.util.TimeUtils;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Tunable parameters for broadcast dispatch policy
@@ -51,6 +57,47 @@
private static final long DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT =
10_000 * Build.HW_TIMEOUT_MULTIPLIER;
+ /**
+ * Defer LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts until the first time any process in
+ * the UID is started.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.S_V2)
+ @Overridable
+ static final long DEFER_BOOT_COMPLETED_BROADCAST_CHANGE_ID = 203704822L;
+
+ /**
+ * Do not defer LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts.
+ */
+ public static final int DEFER_BOOT_COMPLETED_BROADCAST_NONE = 0;
+ /**
+ * Defer all LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts.
+ */
+ public static final int DEFER_BOOT_COMPLETED_BROADCAST_ALL = 1 << 0;
+ /**
+ * Defer LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts if app is background restricted.
+ */
+ public static final int DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY = 1 << 1;
+ /**
+ * Defer LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts if app's targetSdkVersion is T
+ * and above.
+ */
+ public static final int DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY = 1 << 2;
+
+ /**
+ * The list of DEFER_BOOT_COMPLETED_BROADCAST types.
+ * If multiple flags are selected, all conditions must be met to defer the broadcast.
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "DEFER_BOOT_COMPLETED_BROADCAST_" }, value = {
+ DEFER_BOOT_COMPLETED_BROADCAST_NONE,
+ DEFER_BOOT_COMPLETED_BROADCAST_ALL,
+ DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY,
+ DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeferBootCompletedBroadcastType {}
+
// All time constants are in milliseconds
// Timeout period for this broadcast queue
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
index 8afd52e..01d8109 100644
--- a/services/core/java/com/android/server/am/BroadcastDispatcher.java
+++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java
@@ -18,14 +18,21 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
+import android.annotation.Nullable;
import android.content.Intent;
+import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.AlarmManagerInternal;
import com.android.server.LocalServices;
@@ -232,6 +239,253 @@
// Next outbound broadcast, established by getNextBroadcastLocked()
private BroadcastRecord mCurrentBroadcast;
+ // Map userId to its deferred boot completed broadcasts.
+ private SparseArray<DeferredBootCompletedBroadcastPerUser> mUser2Deferred = new SparseArray<>();
+
+ /**
+ * Deferred LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts that is sent to a user.
+ */
+ static class DeferredBootCompletedBroadcastPerUser {
+ private int mUserId;
+ // UID that has process started at least once, ready to execute LOCKED_BOOT_COMPLETED
+ // receivers.
+ @VisibleForTesting
+ SparseBooleanArray mUidReadyForLockedBootCompletedBroadcast = new SparseBooleanArray();
+ // UID that has process started at least once, ready to execute BOOT_COMPLETED receivers.
+ @VisibleForTesting
+ SparseBooleanArray mUidReadyForBootCompletedBroadcast = new SparseBooleanArray();
+ // Map UID to deferred LOCKED_BOOT_COMPLETED broadcasts.
+ // LOCKED_BOOT_COMPLETED broadcast receivers are deferred until the first time the uid has
+ // any process started.
+ @VisibleForTesting
+ SparseArray<BroadcastRecord> mDeferredLockedBootCompletedBroadcasts = new SparseArray<>();
+ // is the LOCKED_BOOT_COMPLETED broadcast received by the user.
+ @VisibleForTesting
+ boolean mLockedBootCompletedBroadcastReceived;
+ // Map UID to deferred BOOT_COMPLETED broadcasts.
+ // BOOT_COMPLETED broadcast receivers are deferred until the first time the uid has any
+ // process started.
+ @VisibleForTesting
+ SparseArray<BroadcastRecord> mDeferredBootCompletedBroadcasts = new SparseArray<>();
+ // is the BOOT_COMPLETED broadcast received by the user.
+ @VisibleForTesting
+ boolean mBootCompletedBroadcastReceived;
+
+ DeferredBootCompletedBroadcastPerUser(int userId) {
+ this.mUserId = userId;
+ }
+
+ public void updateUidReady(int uid) {
+ if (!mLockedBootCompletedBroadcastReceived
+ || mDeferredLockedBootCompletedBroadcasts.size() != 0) {
+ mUidReadyForLockedBootCompletedBroadcast.put(uid, true);
+ }
+ if (!mBootCompletedBroadcastReceived
+ || mDeferredBootCompletedBroadcasts.size() != 0) {
+ mUidReadyForBootCompletedBroadcast.put(uid, true);
+ }
+ }
+
+ public void enqueueBootCompletedBroadcasts(String action,
+ SparseArray<BroadcastRecord> deferred) {
+ if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) {
+ enqueueBootCompletedBroadcasts(deferred, mDeferredLockedBootCompletedBroadcasts,
+ mUidReadyForLockedBootCompletedBroadcast);
+ mLockedBootCompletedBroadcastReceived = true;
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ dumpBootCompletedBroadcastRecord(mDeferredLockedBootCompletedBroadcasts);
+ }
+ } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+ enqueueBootCompletedBroadcasts(deferred, mDeferredBootCompletedBroadcasts,
+ mUidReadyForBootCompletedBroadcast);
+ mBootCompletedBroadcastReceived = true;
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ dumpBootCompletedBroadcastRecord(mDeferredBootCompletedBroadcasts);
+ }
+ }
+ }
+
+ /**
+ * Merge UID to BroadcastRecord map into {@link #mDeferredBootCompletedBroadcasts} or
+ * {@link #mDeferredLockedBootCompletedBroadcasts}
+ * @param from the UID to BroadcastRecord map.
+ * @param into The UID to list of BroadcastRecord map.
+ */
+ private void enqueueBootCompletedBroadcasts(SparseArray<BroadcastRecord> from,
+ SparseArray<BroadcastRecord> into, SparseBooleanArray uidReadyForReceiver) {
+ // remove unwanted uids from uidReadyForReceiver.
+ for (int i = uidReadyForReceiver.size() - 1; i >= 0; i--) {
+ if (from.indexOfKey(uidReadyForReceiver.keyAt(i)) < 0) {
+ uidReadyForReceiver.removeAt(i);
+ }
+ }
+ for (int i = 0, size = from.size(); i < size; i++) {
+ final int uid = from.keyAt(i);
+ into.put(uid, from.valueAt(i));
+ if (uidReadyForReceiver.indexOfKey(uid) < 0) {
+ // uid is wanted but not ready.
+ uidReadyForReceiver.put(uid, false);
+ }
+ }
+ }
+
+ public @Nullable BroadcastRecord dequeueDeferredBootCompletedBroadcast(
+ boolean isAllUidReady) {
+ BroadcastRecord next = dequeueDeferredBootCompletedBroadcast(
+ mDeferredLockedBootCompletedBroadcasts,
+ mUidReadyForLockedBootCompletedBroadcast, isAllUidReady);
+ if (next == null) {
+ next = dequeueDeferredBootCompletedBroadcast(mDeferredBootCompletedBroadcasts,
+ mUidReadyForBootCompletedBroadcast, isAllUidReady);
+ }
+ return next;
+ }
+
+ private @Nullable BroadcastRecord dequeueDeferredBootCompletedBroadcast(
+ SparseArray<BroadcastRecord> uid2br, SparseBooleanArray uidReadyForReceiver,
+ boolean isAllUidReady) {
+ for (int i = 0, size = uid2br.size(); i < size; i++) {
+ final int uid = uid2br.keyAt(i);
+ if (isAllUidReady || uidReadyForReceiver.get(uid)) {
+ final BroadcastRecord br = uid2br.valueAt(i);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ final Object receiver = br.receivers.get(0);
+ if (receiver instanceof BroadcastFilter) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "getDeferredBootCompletedBroadcast uid:" + uid
+ + " BroadcastFilter:" + (BroadcastFilter) receiver
+ + " broadcast:" + br.intent.getAction());
+ }
+ } else /* if (receiver instanceof ResolveInfo) */ {
+ ResolveInfo info = (ResolveInfo) receiver;
+ String packageName = info.activityInfo.applicationInfo.packageName;
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "getDeferredBootCompletedBroadcast uid:" + uid
+ + " packageName:" + packageName
+ + " broadcast:" + br.intent.getAction());
+ }
+ }
+ }
+ // remove the BroadcastRecord.
+ uid2br.removeAt(i);
+ if (uid2br.size() == 0) {
+ // All deferred receivers are executed, do not need uidReadyForReceiver
+ // any more.
+ uidReadyForReceiver.clear();
+ }
+ return br;
+ }
+ }
+ return null;
+ }
+
+ private @Nullable SparseArray<BroadcastRecord> getDeferredList(String action) {
+ SparseArray<BroadcastRecord> brs = null;
+ if (action.equals(Intent.ACTION_LOCKED_BOOT_COMPLETED)) {
+ brs = mDeferredLockedBootCompletedBroadcasts;
+ } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
+ brs = mDeferredBootCompletedBroadcasts;
+ }
+ return brs;
+ }
+
+ /**
+ * Return the total number of UIDs in all BroadcastRecord in
+ * {@link #mDeferredBootCompletedBroadcasts} or
+ * {@link #mDeferredLockedBootCompletedBroadcasts}
+ */
+ private int getBootCompletedBroadcastsUidsSize(String action) {
+ SparseArray<BroadcastRecord> brs = getDeferredList(action);
+ return brs != null ? brs.size() : 0;
+ }
+
+ /**
+ * Return the total number of receivers in all BroadcastRecord in
+ * {@link #mDeferredBootCompletedBroadcasts} or
+ * {@link #mDeferredLockedBootCompletedBroadcasts}
+ */
+ private int getBootCompletedBroadcastsReceiversSize(String action) {
+ SparseArray<BroadcastRecord> brs = getDeferredList(action);
+ if (brs == null) {
+ return 0;
+ }
+ int size = 0;
+ for (int i = 0, s = brs.size(); i < s; i++) {
+ size += brs.valueAt(i).receivers.size();
+ }
+ return size;
+ }
+
+ public void dump(Dumper dumper, String action) {
+ SparseArray<BroadcastRecord> brs = getDeferredList(action);
+ if (brs == null) {
+ return;
+ }
+ for (int i = 0, size = brs.size(); i < size; i++) {
+ dumper.dump(brs.valueAt(i));
+ }
+ }
+
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ for (int i = 0, size = mDeferredLockedBootCompletedBroadcasts.size(); i < size; i++) {
+ mDeferredLockedBootCompletedBroadcasts.valueAt(i).dumpDebug(proto, fieldId);
+ }
+ for (int i = 0, size = mDeferredBootCompletedBroadcasts.size(); i < size; i++) {
+ mDeferredBootCompletedBroadcasts.valueAt(i).dumpDebug(proto, fieldId);
+ }
+ }
+
+ private void dumpBootCompletedBroadcastRecord(SparseArray<BroadcastRecord> brs) {
+ for (int i = 0, size = brs.size(); i < size; i++) {
+ final Object receiver = brs.valueAt(i).receivers.get(0);
+ String packageName = null;
+ if (receiver instanceof BroadcastFilter) {
+ BroadcastFilter recv = (BroadcastFilter) receiver;
+ packageName = recv.receiverList.app.processName;
+ } else /* if (receiver instanceof ResolveInfo) */ {
+ ResolveInfo info = (ResolveInfo) receiver;
+ packageName = info.activityInfo.applicationInfo.packageName;
+ }
+ Slog.i(TAG, "uid:" + brs.keyAt(i)
+ + " packageName:" + packageName
+ + " receivers:" + brs.valueAt(i).receivers.size());
+ }
+ }
+ }
+
+ private DeferredBootCompletedBroadcastPerUser getDeferredPerUser(int userId) {
+ if (mUser2Deferred.contains(userId)) {
+ return mUser2Deferred.get(userId);
+ } else {
+ final DeferredBootCompletedBroadcastPerUser temp =
+ new DeferredBootCompletedBroadcastPerUser(userId);
+ mUser2Deferred.put(userId, temp);
+ return temp;
+ }
+ }
+
+ /**
+ * ActivityManagerService.attachApplication() call this method to notify that the UID is ready
+ * to accept deferred LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts.
+ * @param uid
+ */
+ public void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
+ getDeferredPerUser(UserHandle.getUserId(uid)).updateUidReady(uid);
+ }
+
+ private @Nullable BroadcastRecord dequeueDeferredBootCompletedBroadcast() {
+ final boolean isAllUidReady = (mQueue.mService.mConstants.mDeferBootCompletedBroadcast
+ == DEFER_BOOT_COMPLETED_BROADCAST_NONE);
+ BroadcastRecord next = null;
+ for (int i = 0, size = mUser2Deferred.size(); i < size; i++) {
+ next = mUser2Deferred.valueAt(i).dequeueDeferredBootCompletedBroadcast(isAllUidReady);
+ if (next != null) {
+ break;
+ }
+ }
+ return next;
+ }
+
/**
* Constructed & sharing a lock with its associated BroadcastQueue instance
*/
@@ -261,7 +515,9 @@
return mCurrentBroadcast == null
&& mOrderedBroadcasts.isEmpty()
&& isDeferralsListEmpty(mDeferredBroadcasts)
- && isDeferralsListEmpty(mAlarmBroadcasts);
+ && isDeferralsListEmpty(mAlarmBroadcasts)
+ && getBootCompletedBroadcastsUidsSize(Intent.ACTION_LOCKED_BOOT_COMPLETED) == 0
+ && getBootCompletedBroadcastsUidsSize(Intent.ACTION_BOOT_COMPLETED) == 0;
}
}
@@ -301,14 +557,78 @@
sb.append(n);
sb.append(" deferred");
}
+ n = getBootCompletedBroadcastsUidsSize(Intent.ACTION_LOCKED_BOOT_COMPLETED);
+ if (n > 0) {
+ sb.append(", ");
+ sb.append(n);
+ sb.append(" deferred LOCKED_BOOT_COMPLETED/");
+ sb.append(getBootCompletedBroadcastsReceiversSize(Intent.ACTION_LOCKED_BOOT_COMPLETED));
+ sb.append(" receivers");
+ }
+
+ n = getBootCompletedBroadcastsUidsSize(Intent.ACTION_BOOT_COMPLETED);
+ if (n > 0) {
+ sb.append(", ");
+ sb.append(n);
+ sb.append(" deferred BOOT_COMPLETED/");
+ sb.append(getBootCompletedBroadcastsReceiversSize(Intent.ACTION_BOOT_COMPLETED));
+ sb.append(" receivers");
+ }
return sb.toString();
}
// ----------------------------------
// BroadcastQueue operation support
-
void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
- mOrderedBroadcasts.add(r);
+ if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(r.intent.getAction())) {
+ // Create one BroadcastRecord for each UID that can be deferred.
+ final SparseArray<BroadcastRecord> deferred =
+ r.splitDeferredBootCompletedBroadcastLocked(mQueue.mService.mInternal,
+ mQueue.mService.mConstants.mDeferBootCompletedBroadcast);
+ getDeferredPerUser(r.userId).enqueueBootCompletedBroadcasts(
+ Intent.ACTION_LOCKED_BOOT_COMPLETED, deferred);
+ if (!r.receivers.isEmpty()) {
+ // The non-deferred receivers.
+ mOrderedBroadcasts.add(r);
+ return;
+ }
+ } else if (Intent.ACTION_BOOT_COMPLETED.equals(r.intent.getAction())) {
+ // Create one BroadcastRecord for each UID that can be deferred.
+ final SparseArray<BroadcastRecord> deferred =
+ r.splitDeferredBootCompletedBroadcastLocked(mQueue.mService.mInternal,
+ mQueue.mService.mConstants.mDeferBootCompletedBroadcast);
+ getDeferredPerUser(r.userId).enqueueBootCompletedBroadcasts(
+ Intent.ACTION_BOOT_COMPLETED, deferred);
+ if (!r.receivers.isEmpty()) {
+ // The non-deferred receivers.
+ mOrderedBroadcasts.add(r);
+ return;
+ }
+ } else {
+ mOrderedBroadcasts.add(r);
+ }
+ }
+
+ /**
+ * Return the total number of UIDs in all deferred boot completed BroadcastRecord.
+ */
+ private int getBootCompletedBroadcastsUidsSize(String action) {
+ int size = 0;
+ for (int i = 0, s = mUser2Deferred.size(); i < s; i++) {
+ size += mUser2Deferred.valueAt(i).getBootCompletedBroadcastsUidsSize(action);
+ }
+ return size;
+ }
+
+ /**
+ * Return the total number of receivers in all deferred boot completed BroadcastRecord.
+ */
+ private int getBootCompletedBroadcastsReceiversSize(String action) {
+ int size = 0;
+ for (int i = 0, s = mUser2Deferred.size(); i < s; i++) {
+ size += mUser2Deferred.valueAt(i).getBootCompletedBroadcastsReceiversSize(action);
+ }
+ return size;
}
// Returns the now-replaced broadcast record, or null if none
@@ -369,6 +689,31 @@
boolean didSomething = cleanupBroadcastListDisabledReceiversLocked(mOrderedBroadcasts,
packageName, filterByClasses, userId, doit);
if (doit || !didSomething) {
+ ArrayList<BroadcastRecord> lockedBootCompletedBroadcasts = new ArrayList<>();
+ for (int u = 0, usize = mUser2Deferred.size(); u < usize; u++) {
+ SparseArray<BroadcastRecord> brs =
+ mUser2Deferred.valueAt(u).mDeferredLockedBootCompletedBroadcasts;
+ for (int i = 0, size = brs.size(); i < size; i++) {
+ lockedBootCompletedBroadcasts.add(brs.valueAt(i));
+ }
+ }
+ didSomething = cleanupBroadcastListDisabledReceiversLocked(
+ lockedBootCompletedBroadcasts,
+ packageName, filterByClasses, userId, doit);
+ }
+ if (doit || !didSomething) {
+ ArrayList<BroadcastRecord> bootCompletedBroadcasts = new ArrayList<>();
+ for (int u = 0, usize = mUser2Deferred.size(); u < usize; u++) {
+ SparseArray<BroadcastRecord> brs =
+ mUser2Deferred.valueAt(u).mDeferredBootCompletedBroadcasts;
+ for (int i = 0, size = brs.size(); i < size; i++) {
+ bootCompletedBroadcasts.add(brs.valueAt(i));
+ }
+ }
+ didSomething = cleanupBroadcastListDisabledReceiversLocked(bootCompletedBroadcasts,
+ packageName, filterByClasses, userId, doit);
+ }
+ if (doit || !didSomething) {
didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmBroadcasts,
packageName, filterByClasses, userId, doit);
}
@@ -428,6 +773,10 @@
for (Deferrals d : mDeferredBroadcasts) {
d.dumpDebug(proto, fieldId);
}
+
+ for (int i = 0, size = mUser2Deferred.size(); i < size; i++) {
+ mUser2Deferred.valueAt(i).dumpDebug(proto, fieldId);
+ }
}
// ----------------------------------
@@ -453,7 +802,12 @@
final boolean someQueued = !mOrderedBroadcasts.isEmpty();
BroadcastRecord next = null;
- if (!mAlarmBroadcasts.isEmpty()) {
+
+ if (next == null) {
+ next = dequeueDeferredBootCompletedBroadcast();
+ }
+
+ if (next == null && !mAlarmBroadcasts.isEmpty()) {
next = popLocked(mAlarmBroadcasts);
if (DEBUG_BROADCAST_DEFERRAL && next != null) {
Slog.i(TAG, "Next broadcast from alarm targets: " + next);
@@ -752,6 +1106,20 @@
}
printed |= dumper.didPrint();
+ dumper.setHeading("Deferred LOCKED_BOOT_COMPLETED broadcasts");
+ dumper.setLabel("Deferred LOCKED_BOOT_COMPLETED Broadcast");
+ for (int i = 0, size = mUser2Deferred.size(); i < size; i++) {
+ mUser2Deferred.valueAt(i).dump(dumper, Intent.ACTION_LOCKED_BOOT_COMPLETED);
+ }
+ printed |= dumper.didPrint();
+
+ dumper.setHeading("Deferred BOOT_COMPLETED broadcasts");
+ dumper.setLabel("Deferred BOOT_COMPLETED Broadcast");
+ for (int i = 0, size = mUser2Deferred.size(); i < size; i++) {
+ mUser2Deferred.valueAt(i).dump(dumper, Intent.ACTION_BOOT_COMPLETED);
+ }
+ printed |= dumper.didPrint();
+
return printed;
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a83fdd0..ea63c08 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -249,11 +249,13 @@
}
public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
+ r.enqueueClockTime = System.currentTimeMillis();
mParallelBroadcasts.add(r);
enqueueBroadcastHelper(r);
}
public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
+ r.enqueueClockTime = System.currentTimeMillis();
mDispatcher.enqueueOrderedBroadcastLocked(r);
enqueueBroadcastHelper(r);
}
@@ -263,8 +265,6 @@
* enqueueOrderedBroadcastLocked.
*/
private void enqueueBroadcastHelper(BroadcastRecord r) {
- r.enqueueClockTime = System.currentTimeMillis();
-
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
@@ -368,6 +368,15 @@
}
}
+ /**
+ * Called by ActivityManagerService to notify that the uid has process started, if there is any
+ * deferred BOOT_COMPLETED broadcast, the BroadcastDispatcher can dispatch the broadcast now.
+ * @param uid
+ */
+ public void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
+ mDispatcher.updateUidReadyForBootCompletedBroadcastLocked(uid);
+ }
+
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
final BroadcastRecord br = mPendingBroadcast;
@@ -1858,9 +1867,7 @@
}
private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) {
- // TODO (206518114): Only allow apps with ACCESS_PACKAGE_USAGE_STATS to set
- // getIdForResponseEvent.
- // TODO (217251579): Temporarily use temp-allowlist reason to identify
+ // STOPSHIP (217251579): Temporarily use temp-allowlist reason to identify
// push messages and record response events.
useTemporaryAllowlistReasonAsSignal(r);
if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 8015596..8b1e829 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -16,9 +16,20 @@
package com.android.server.am;
+import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
+
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_ALL;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_CHANGE_ID;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
+import android.app.compat.CompatChanges;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -30,6 +41,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.PrintWriterPrinter;
+import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -371,6 +383,80 @@
return split;
}
+ /**
+ * Split a BroadcastRecord to a map of deferred receiver UID to deferred BroadcastRecord.
+ *
+ * The receivers that are deferred are removed from original BroadcastRecord's receivers list.
+ * The receivers that are not deferred are kept in original BroadcastRecord's receivers list.
+ *
+ * Only used to split LOCKED_BOOT_COMPLETED or BOOT_COMPLETED BroadcastRecord.
+ * LOCKED_BOOT_COMPLETED or BOOT_COMPLETED broadcast can be deferred until the first time
+ * the receiver's UID has a process started.
+ *
+ * @param ams The ActivityManagerService object.
+ * @param deferType Defer what UID?
+ * @return the deferred UID to BroadcastRecord map, the BroadcastRecord has the list of
+ * receivers in that UID.
+ */
+ @NonNull SparseArray<BroadcastRecord> splitDeferredBootCompletedBroadcastLocked(
+ ActivityManagerInternal activityManagerInternal,
+ @BroadcastConstants.DeferBootCompletedBroadcastType int deferType) {
+ final SparseArray<BroadcastRecord> ret = new SparseArray<>();
+ if (deferType == DEFER_BOOT_COMPLETED_BROADCAST_NONE) {
+ return ret;
+ }
+
+ final String action = intent.getAction();
+ if (!Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)
+ && !Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+ return ret;
+ }
+
+ final SparseArray<List<Object>> uid2receiverList = new SparseArray<>();
+ for (int i = receivers.size() - 1; i >= 0; i--) {
+ final Object receiver = receivers.get(i);
+ final int uid = getReceiverUid(receiver);
+ if (deferType != DEFER_BOOT_COMPLETED_BROADCAST_ALL) {
+ if ((deferType & DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY) != 0) {
+ if (activityManagerInternal.getRestrictionLevel(uid)
+ < RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
+ // skip if the UID is not background restricted.
+ continue;
+ }
+ }
+ if ((deferType & DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY) != 0) {
+ if (!CompatChanges.isChangeEnabled(DEFER_BOOT_COMPLETED_BROADCAST_CHANGE_ID,
+ uid)) {
+ // skip if the UID is not targetSdkVersion T+.
+ continue;
+ }
+ }
+ }
+ // Remove receiver from original BroadcastRecord's receivers list.
+ receivers.remove(i);
+ final List<Object> receiverList = uid2receiverList.get(uid);
+ if (receiverList != null) {
+ receiverList.add(0, receiver);
+ } else {
+ ArrayList<Object> splitReceivers = new ArrayList<>();
+ splitReceivers.add(0, receiver);
+ uid2receiverList.put(uid, splitReceivers);
+ }
+ }
+ final int uidSize = uid2receiverList.size();
+ for (int i = 0; i < uidSize; i++) {
+ final BroadcastRecord br = new BroadcastRecord(queue, intent, callerApp, callerPackage,
+ callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
+ requiredPermissions, excludedPermissions, appOp, options,
+ uid2receiverList.valueAt(i), resultTo,
+ resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId,
+ allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt);
+ br.enqueueClockTime = this.enqueueClockTime;
+ ret.put(uid2receiverList.keyAt(i), br);
+ }
+ return ret;
+ }
+
int getReceiverUid(Object receiver) {
if (receiver instanceof BroadcastFilter) {
return ((BroadcastFilter) receiver).owningUid;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 711c576..bf2876f 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -1098,6 +1098,10 @@
userId);
foregroundNoti = localForegroundNoti; // save it for amending next time
+
+ signalForegroundServiceNotification(packageName, appInfo.uid,
+ localForegroundId);
+
} catch (RuntimeException e) {
Slog.w(TAG, "Error showing notification for service", e);
// If it gave us a garbage notification, it doesn't
@@ -1131,10 +1135,21 @@
} catch (RuntimeException e) {
Slog.w(TAG, "Error canceling notification for service", e);
}
+ signalForegroundServiceNotification(packageName, appInfo.uid, -localForegroundId);
}
});
}
+ private void signalForegroundServiceNotification(String packageName, int uid,
+ int foregroundId) {
+ synchronized (ams) {
+ for (int i = ams.mForegroundServiceStateListeners.size() - 1; i >= 0; i--) {
+ ams.mForegroundServiceStateListeners.get(i).onForegroundServiceNotificationUpdated(
+ packageName, appInfo.uid, foregroundId);
+ }
+ }
+ }
+
public void stripForegroundServiceFlagFromNotification() {
final int localForegroundId = foregroundId;
final int localUserId = userId;
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
index ab4d856..b73cf5b 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
@@ -29,6 +29,7 @@
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.app.ambientcontext.AmbientContextManager;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -39,7 +40,6 @@
import android.os.Bundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
-import android.provider.Settings;
import android.service.ambientcontext.AmbientContextDetectionResult;
import android.service.ambientcontext.AmbientContextDetectionServiceStatus;
import android.text.TextUtils;
@@ -289,7 +289,7 @@
return;
}
- if ((recentTasks != null) || recentTasks.getList().isEmpty()) {
+ if ((recentTasks == null) || recentTasks.getList().isEmpty()) {
Slog.e(TAG, "Recent task list is empty!");
return;
}
@@ -297,40 +297,48 @@
task = recentTasks.getList().get(0);
if (!callingPackage.equals(task.topActivityInfo.packageName)) {
Slog.e(TAG, "Recent task package name: " + task.topActivityInfo.packageName
- + " doesn't match with camera client package name: " + callingPackage);
+ + " doesn't match with client package name: " + callingPackage);
return;
}
// Start activity as the same task from the callingPackage
- Intent intent = new Intent();
ComponentName consentComponent = getConsentComponent();
- if (consentComponent != null) {
- Slog.e(TAG, "Starting consent activity for " + callingPackage);
- Context context = getContext();
- String packageNameExtraKey = Settings.Secure.getStringForUser(
- context.getContentResolver(),
- Settings.Secure.AMBIENT_CONTEXT_PACKAGE_NAME_EXTRA_KEY,
- userId);
- String eventArrayExtraKey = Settings.Secure.getStringForUser(
- context.getContentResolver(),
- Settings.Secure.AMBIENT_CONTEXT_EVENT_ARRAY_EXTRA_KEY,
- userId);
+ if (consentComponent == null) {
+ Slog.e(TAG, "Consent component not found!");
+ return;
+ }
- // Create consent activity intent with the calling package name and requested events.
+ Slog.d(TAG, "Starting consent activity for " + callingPackage);
+ Intent intent = new Intent();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Context context = getContext();
+ String packageNameExtraKey = context.getResources().getString(
+ com.android.internal.R.string.config_ambientContextPackageNameExtraKey);
+ String eventArrayExtraKey = context.getResources().getString(
+ com.android.internal.R.string.config_ambientContextEventArrayExtraKey);
+
+ // Create consent activity intent with the calling package name and requested events
intent.setComponent(consentComponent);
if (packageNameExtraKey != null) {
intent.putExtra(packageNameExtraKey, callingPackage);
+ } else {
+ Slog.d(TAG, "Missing packageNameExtraKey for consent activity");
}
if (eventArrayExtraKey != null) {
intent.putExtra(eventArrayExtraKey, eventTypes);
+ } else {
+ Slog.d(TAG, "Missing eventArrayExtraKey for consent activity");
}
// Set parent to the calling app's task
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchTaskId(task.taskId);
- context.startActivity(intent, options.toBundle());
- } else {
- Slog.e(TAG, "Consent component not found!");
+ context.startActivityAsUser(intent, options.toBundle(), context.getUser());
+ } catch (ActivityNotFoundException e) {
+ Slog.e(TAG, "unable to start consent activity");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -339,13 +347,12 @@
*/
private ComponentName getConsentComponent() {
Context context = getContext();
- String consentComponent = Settings.Secure.getStringForUser(
- context.getContentResolver(),
- Settings.Secure.AMBIENT_CONTEXT_CONSENT_COMPONENT,
- getUserId());
+ String consentComponent = context.getResources().getString(
+ com.android.internal.R.string.config_defaultAmbientContextConsentComponent);
if (TextUtils.isEmpty(consentComponent)) {
return null;
}
+ Slog.i(TAG, "Consent component name: " + consentComponent);
return ComponentName.unflattenFromString(consentComponent);
}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 551773e..9ba9d78 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -17,8 +17,8 @@
package com.android.server.app;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
-import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.EXTRA_REPLACING;
import static com.android.internal.R.styleable.GameModeConfig_allowGameAngleDriver;
import static com.android.internal.R.styleable.GameModeConfig_allowGameDownscaling;
@@ -1467,7 +1467,6 @@
private void registerPackageReceiver() {
final IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(ACTION_PACKAGE_ADDED);
- packageFilter.addAction(ACTION_PACKAGE_CHANGED);
packageFilter.addAction(ACTION_PACKAGE_REMOVED);
packageFilter.addDataScheme("package");
final BroadcastReceiver packageReceiver = new BroadcastReceiver() {
@@ -1492,16 +1491,29 @@
}
switch (intent.getAction()) {
case ACTION_PACKAGE_ADDED:
- case ACTION_PACKAGE_CHANGED:
updateConfigsForUser(userId, packageName);
break;
case ACTION_PACKAGE_REMOVED:
disableCompatScale(packageName);
- synchronized (mOverrideConfigLock) {
- mOverrideConfigs.remove(packageName);
- }
- synchronized (mDeviceConfigLock) {
- mConfigs.remove(packageName);
+ // If EXTRA_REPLACING is true, it means there will be an
+ // ACTION_PACKAGE_ADDED triggered after this because this
+ // is an updated package that gets installed. Hence, disable
+ // resolution downscaling effort but avoid removing the server
+ // or commandline overriding configurations because those will
+ // not change but the package game mode configurations may change
+ // which may opt in and/or opt out some game mode configurations.
+ if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
+ synchronized (mOverrideConfigLock) {
+ mOverrideConfigs.remove(packageName);
+ }
+ synchronized (mDeviceConfigLock) {
+ mConfigs.remove(packageName);
+ }
+ synchronized (mLock) {
+ if (mSettings.containsKey(userId)) {
+ mSettings.get(userId).removeGame(packageName);
+ }
+ }
}
break;
default:
diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java
index 2982545..1455a61 100644
--- a/services/core/java/com/android/server/app/GameManagerSettings.java
+++ b/services/core/java/com/android/server/app/GameManagerSettings.java
@@ -93,6 +93,14 @@
}
/**
+ * Remove the game mode of a given package.
+ * This operation must be synced with an external lock.
+ */
+ void removeGame(String packageName) {
+ mGameModes.remove(packageName);
+ }
+
+ /**
* Write all current game service settings into disk.
* This operation must be synced with an external lock.
*/
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d2c6c13..3e5e435 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -8574,6 +8574,30 @@
return mSpatializerHelper.isAvailable();
}
+ /** @see Spatializer#isAvailableForDevice(AudioDeviceAttributes) */
+ public boolean isSpatializerAvailableForDevice(@NonNull AudioDeviceAttributes device) {
+ enforceModifyDefaultAudioEffectsPermission();
+ return mSpatializerHelper.isAvailableForDevice(Objects.requireNonNull(device));
+ }
+
+ /** @see Spatializer#hasHeadTracker(AudioDeviceAttributes) */
+ public boolean hasHeadTracker(@NonNull AudioDeviceAttributes device) {
+ enforceModifyDefaultAudioEffectsPermission();
+ return mSpatializerHelper.hasHeadTracker(Objects.requireNonNull(device));
+ }
+
+ /** @see Spatializer#setHeadTrackerEnabled(boolean, AudioDeviceAttributes) */
+ public void setHeadTrackerEnabled(boolean enabled, @NonNull AudioDeviceAttributes device) {
+ enforceModifyDefaultAudioEffectsPermission();
+ mSpatializerHelper.setHeadTrackerEnabled(enabled, Objects.requireNonNull(device));
+ }
+
+ /** @see Spatializer#isHeadTrackerEnabled(AudioDeviceAttributes) */
+ public boolean isHeadTrackerEnabled(@NonNull AudioDeviceAttributes device) {
+ enforceModifyDefaultAudioEffectsPermission();
+ return mSpatializerHelper.isHeadTrackerEnabled(Objects.requireNonNull(device));
+ }
+
/** @see Spatializer#setSpatializerEnabled(boolean) */
public void setSpatializerEnabled(boolean enabled) {
enforceModifyDefaultAudioEffectsPermission();
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 106cbba..63b27d8 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -23,6 +23,7 @@
import android.hardware.SensorManager;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioSystem;
import android.media.INativeSpatializerCallback;
@@ -33,11 +34,14 @@
import android.media.ISpatializerHeadTrackingModeCallback;
import android.media.ISpatializerOutputCallback;
import android.media.SpatializationLevel;
+import android.media.SpatializationMode;
import android.media.Spatializer;
import android.media.SpatializerHeadTrackingMode;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Pair;
+import android.util.SparseIntArray;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -69,10 +73,42 @@
// TODO: replace with generic head tracker sensor name.
// the current implementation refers to the "google" namespace but will be replaced
// by an android name at the next API level revision, it is not Google-specific.
- // Also see "TODO-HT" in onInitSensors() method
private static final String HEADTRACKER_SENSOR =
"com.google.hardware.sensor.hid_dynamic.headtracker";
+ private static final SparseIntArray SPAT_MODE_FOR_DEVICE_TYPE = new SparseIntArray(15) {
+ {
+ append(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_WIRED_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
+ append(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, SpatializationMode.SPATIALIZER_BINAURAL);
+ // assumption for A2DP: mostly headsets
+ append(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, SpatializationMode.SPATIALIZER_BINAURAL);
+ append(AudioDeviceInfo.TYPE_DOCK, SpatializationMode.SPATIALIZER_TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_USB_ACCESSORY, SpatializationMode.SPATIALIZER_TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_USB_DEVICE, SpatializationMode.SPATIALIZER_TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_USB_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
+ append(AudioDeviceInfo.TYPE_LINE_ANALOG, SpatializationMode.SPATIALIZER_TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_LINE_DIGITAL, SpatializationMode.SPATIALIZER_TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_AUX_LINE, SpatializationMode.SPATIALIZER_TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_HEARING_AID, SpatializationMode.SPATIALIZER_BINAURAL);
+ append(AudioDeviceInfo.TYPE_BLE_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
+ append(AudioDeviceInfo.TYPE_BLE_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL);
+ // assumption that BLE broadcast would be mostly consumed on headsets
+ append(AudioDeviceInfo.TYPE_BLE_BROADCAST, SpatializationMode.SPATIALIZER_BINAURAL);
+ }
+ };
+
+ private static final int[] WIRELESS_TYPES = { AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ AudioDeviceInfo.TYPE_BLE_BROADCAST
+ };
+
+ private static final int[] WIRELESS_SPEAKER_TYPES = {
+ AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ };
+
// Spatializer state machine
private static final int STATE_UNINITIALIZED = 0;
private static final int STATE_NOT_SUPPORTED = 1;
@@ -82,11 +118,20 @@
private static final int STATE_DISABLED_AVAILABLE = 6;
private int mState = STATE_UNINITIALIZED;
+ private boolean mFeatureEnabled = false;
/** current level as reported by native Spatializer in callback */
private int mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
private int mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ private boolean mTransauralSupported = false;
+ private boolean mBinauralSupported = false;
private int mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
+ /**
+ * The desired head tracking mode when enabling head tracking, tracks mDesiredHeadTrackingMode,
+ * except when head tracking gets disabled through setting the desired mode to
+ * {@link Spatializer#HEAD_TRACKING_MODE_DISABLED}.
+ */
+ private int mDesiredHeadTrackingModeWhenEnabled = Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
private int mSpatOutput = 0;
private @Nullable ISpatializer mSpat;
private @Nullable SpatializerCallback mSpatCallback;
@@ -108,8 +153,18 @@
//---------------------------------------------------------------
// audio device compatibility / enabled
+ /**
+ * List of device types that can be used on this device with Spatial Audio.
+ * It is initialized based on the transaural/binaural capabilities
+ * of the effect.
+ */
+ private final ArrayList<Integer> mSACapableDeviceTypes = new ArrayList<>(0);
- private final ArrayList<AudioDeviceAttributes> mCompatibleAudioDevices = new ArrayList<>(0);
+ /**
+ * List of devices where Spatial Audio is possible. Each device can be enabled or disabled
+ * (== user choice to use or not)
+ */
+ private final ArrayList<SADeviceState> mSADevices = new ArrayList<>(0);
//------------------------------------------------------
// initialization
@@ -155,6 +210,46 @@
break;
}
}
+ byte[] spatModes = spat.getSupportedModes();
+ for (byte mode : spatModes) {
+ switch (mode) {
+ case SpatializationMode.SPATIALIZER_BINAURAL:
+ mBinauralSupported = true;
+ break;
+ case SpatializationMode.SPATIALIZER_TRANSAURAL:
+ mTransauralSupported = true;
+ break;
+ default:
+ Log.e(TAG, "Spatializer reports unknown supported mode:" + mode);
+ break;
+ }
+ }
+ // if neither transaural nor binaural is supported, bail
+ if (!mBinauralSupported && !mTransauralSupported) {
+ mState = STATE_NOT_SUPPORTED;
+ return;
+ }
+
+ // initialize list of compatible devices
+ for (int i = 0; i < SPAT_MODE_FOR_DEVICE_TYPE.size(); i++) {
+ int mode = SPAT_MODE_FOR_DEVICE_TYPE.valueAt(i);
+ if ((mode == (int) SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported)
+ || (mode == (int) SpatializationMode.SPATIALIZER_TRANSAURAL
+ && mTransauralSupported)) {
+ mSACapableDeviceTypes.add(SPAT_MODE_FOR_DEVICE_TYPE.keyAt(i));
+ }
+ }
+ if (mTransauralSupported) {
+ // TODO deal with persisted values
+ mSADevices.add(
+ new SADeviceState(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null));
+ }
+ if (mBinauralSupported) {
+ // TODO deal with persisted values
+ mSADevices.add(
+ new SADeviceState(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, null));
+ }
+ // TODO read persisted states
} catch (RemoteException e) {
/* capable level remains at NONE*/
} finally {
@@ -184,12 +279,15 @@
mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
init(true);
- setFeatureEnabled(featureEnabled);
+ setSpatializerEnabledInt(featureEnabled);
}
//------------------------------------------------------
// routing monitoring
- void onRoutingUpdated() {
+ synchronized void onRoutingUpdated() {
+ if (!mFeatureEnabled) {
+ return;
+ }
switch (mState) {
case STATE_UNINITIALIZED:
case STATE_NOT_SUPPORTED:
@@ -201,11 +299,36 @@
break;
}
mASA.getDevicesForAttributes(DEFAULT_ATTRIBUTES).toArray(ROUTING_DEVICES);
- final boolean able =
- AudioSystem.canBeSpatialized(DEFAULT_ATTRIBUTES, DEFAULT_FORMAT, ROUTING_DEVICES);
- logd("onRoutingUpdated: can spatialize media 5.1:" + able
- + " on device:" + ROUTING_DEVICES[0]);
- setDispatchAvailableState(able);
+
+ // is media routed to a new device?
+ if (isWireless(ROUTING_DEVICES[0].getType())) {
+ addWirelessDeviceIfNew(ROUTING_DEVICES[0]);
+ }
+
+ // find if media device enabled / available
+ final Pair<Boolean, Boolean> enabledAvailable = evaluateState(ROUTING_DEVICES[0]);
+
+ boolean able = false;
+ if (enabledAvailable.second) {
+ // available for Spatial audio, check w/ effect
+ able = canBeSpatializedOnDevice(DEFAULT_ATTRIBUTES, DEFAULT_FORMAT, ROUTING_DEVICES);
+ Log.i(TAG, "onRoutingUpdated: can spatialize media 5.1:" + able
+ + " on device:" + ROUTING_DEVICES[0]);
+ setDispatchAvailableState(able);
+ } else {
+ Log.i(TAG, "onRoutingUpdated: device:" + ROUTING_DEVICES[0]
+ + " not available for Spatial Audio");
+ setDispatchAvailableState(false);
+ }
+
+ if (able && enabledAvailable.first) {
+ Log.i(TAG, "Enabling Spatial Audio since enabled for media device:"
+ + ROUTING_DEVICES[0]);
+ } else {
+ Log.i(TAG, "Disabling Spatial Audio since disabled for media device:"
+ + ROUTING_DEVICES[0]);
+ }
+ setDispatchFeatureEnabledState(able && enabledAvailable.first);
if (mDesiredHeadTrackingMode != Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED
&& mDesiredHeadTrackingMode != Spatializer.HEAD_TRACKING_MODE_DISABLED) {
@@ -298,20 +421,145 @@
//------------------------------------------------------
// compatible devices
/**
- * @return a shallow copy of the list of compatible audio devices
+ * Return the list of compatible devices, which reflects the device compatible with the
+ * spatializer effect, and those that have been explicitly enabled or disabled
+ * @return the list of compatible audio devices
*/
synchronized @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() {
- return (List<AudioDeviceAttributes>) mCompatibleAudioDevices.clone();
+ // build unionOf(mCompatibleAudioDevices, mEnabledDevice) - mDisabledAudioDevices
+ ArrayList<AudioDeviceAttributes> compatList = new ArrayList<>();
+ for (SADeviceState dev : mSADevices) {
+ if (dev.mEnabled) {
+ compatList.add(new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
+ dev.mDeviceType, dev.mDeviceAddress == null ? "" : dev.mDeviceAddress));
+ }
+ }
+ return compatList;
}
synchronized void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
- if (!mCompatibleAudioDevices.contains(ada)) {
- mCompatibleAudioDevices.add(ada);
+ // TODO add log
+ final int deviceType = ada.getType();
+ final boolean wireless = isWireless(deviceType);
+ boolean updateRouting = false;
+ boolean isInList = false;
+
+ for (SADeviceState deviceState : mSADevices) {
+ if (deviceType == deviceState.mDeviceType
+ && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+ || !wireless) {
+ isInList = true;
+ // state change?
+ updateRouting = !deviceState.mEnabled;
+ deviceState.mEnabled = true;
+ break;
+ }
+ }
+ if (!isInList) {
+ final SADeviceState dev = new SADeviceState(deviceType,
+ wireless ? ada.getAddress() : null);
+ dev.mEnabled = true;
+ mSADevices.add(dev);
+ updateRouting = true;
+ }
+ if (updateRouting) {
+ onRoutingUpdated();
}
}
synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
- mCompatibleAudioDevices.remove(ada);
+ // TODO add log
+ final int deviceType = ada.getType();
+ final boolean wireless = isWireless(deviceType);
+ boolean updateRouting = false;
+
+ for (SADeviceState deviceState : mSADevices) {
+ if (deviceType == deviceState.mDeviceType
+ && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+ || !wireless) {
+ // state change?
+ updateRouting = deviceState.mEnabled;
+ deviceState.mEnabled = false;
+ break;
+ }
+ }
+ if (updateRouting) {
+ onRoutingUpdated();
+ }
+ }
+
+ /**
+ * Return if Spatial Audio is enabled and available for the given device
+ * @param ada
+ * @return a pair of boolean, 1/ enabled? 2/ available?
+ */
+ private synchronized Pair<Boolean, Boolean> evaluateState(AudioDeviceAttributes ada) {
+ // if not a wireless device, this value will be overwritten to map the type
+ // to TYPE_BUILTIN_SPEAKER or TYPE_WIRED_HEADPHONES
+ int deviceType = ada.getType();
+ final boolean wireless = isWireless(deviceType);
+
+ // if not a wireless device: find if media device is in the speaker, wired headphones
+ if (!wireless) {
+ // is the device type capable of doing SA?
+ if (!mSACapableDeviceTypes.contains(deviceType)) {
+ Log.i(TAG, "Device incompatible with Spatial Audio dev:" + ada);
+ return new Pair<>(false, false);
+ }
+ // what spatialization mode to use for this device?
+ final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE);
+ if (spatMode == Integer.MIN_VALUE) {
+ // error case, device not found
+ Log.e(TAG, "no spatialization mode found for device type:" + deviceType);
+ return new Pair<>(false, false);
+ }
+ // map the spatialization mode to the SPEAKER or HEADPHONES device
+ if (spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL) {
+ deviceType = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+ } else {
+ deviceType = AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
+ }
+ } else { // wireless device
+ if (isWirelessSpeaker(deviceType) && !mTransauralSupported) {
+ Log.i(TAG, "Device incompatible with Spatial Audio (no transaural) dev:"
+ + ada);
+ return new Pair<>(false, false);
+ }
+ if (!mBinauralSupported) {
+ Log.i(TAG, "Device incompatible with Spatial Audio (no binaural) dev:"
+ + ada);
+ return new Pair<>(false, false);
+ }
+ }
+
+ boolean enabled = false;
+ boolean available = false;
+ for (SADeviceState deviceState : mSADevices) {
+ if (deviceType == deviceState.mDeviceType
+ && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+ || !wireless) {
+ available = true;
+ enabled = deviceState.mEnabled;
+ break;
+ }
+ }
+ return new Pair<>(enabled, available);
+ }
+
+ private synchronized void addWirelessDeviceIfNew(@NonNull AudioDeviceAttributes ada) {
+ boolean knownDevice = false;
+ for (SADeviceState deviceState : mSADevices) {
+ // wireless device so always check address
+ if (ada.getType() == deviceState.mDeviceType
+ && ada.getAddress().equals(deviceState.mDeviceAddress)) {
+ knownDevice = true;
+ break;
+ }
+ }
+ if (!knownDevice) {
+ mSADevices.add(new SADeviceState(ada.getType(), ada.getAddress()));
+ //### TODO persist list
+ }
}
//------------------------------------------------------
@@ -345,7 +593,56 @@
}
}
+ synchronized boolean isAvailableForDevice(@NonNull AudioDeviceAttributes ada) {
+ if (ada.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
+ return false;
+ }
+
+ final int deviceType = ada.getType();
+ final boolean wireless = isWireless(deviceType);
+ for (SADeviceState deviceState : mSADevices) {
+ if (deviceType == deviceState.mDeviceType
+ && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+ || !wireless) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private synchronized boolean canBeSpatializedOnDevice(@NonNull AudioAttributes attributes,
+ @NonNull AudioFormat format, @NonNull AudioDeviceAttributes[] devices) {
+ final byte modeForDevice = (byte) SPAT_MODE_FOR_DEVICE_TYPE.get(devices[0].getType(),
+ /*default when type not found*/ SpatializationMode.SPATIALIZER_BINAURAL);
+ if ((modeForDevice == SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported)
+ || (modeForDevice == SpatializationMode.SPATIALIZER_TRANSAURAL
+ && mTransauralSupported)) {
+ return AudioSystem.canBeSpatialized(attributes, format, devices);
+ }
+ return false;
+ }
+
synchronized void setFeatureEnabled(boolean enabled) {
+ if (mFeatureEnabled == enabled) {
+ return;
+ }
+ mFeatureEnabled = enabled;
+ if (mFeatureEnabled) {
+ if (mState == STATE_NOT_SUPPORTED) {
+ Log.e(TAG, "Can't enabled Spatial Audio, unsupported");
+ return;
+ }
+ if (mState == STATE_UNINITIALIZED) {
+ init(true);
+ }
+ setSpatializerEnabledInt(true);
+ onRoutingUpdated();
+ } else {
+ setSpatializerEnabledInt(false);
+ }
+ }
+
+ synchronized void setSpatializerEnabledInt(boolean enabled) {
switch (mState) {
case STATE_UNINITIALIZED:
if (enabled) {
@@ -397,8 +694,7 @@
}
/**
- * precondition: mState = STATE_*
- * isFeatureEnabled() != featureEnabled
+ * Update the feature state, no-op if no change
* @param featureEnabled
*/
private synchronized void setDispatchFeatureEnabledState(boolean featureEnabled) {
@@ -410,6 +706,10 @@
case STATE_DISABLED_AVAILABLE:
mState = STATE_ENABLED_AVAILABLE;
break;
+ case STATE_ENABLED_AVAILABLE:
+ case STATE_ENABLED_UNAVAILABLE:
+ // already enabled: no-op
+ return;
default:
throw(new IllegalStateException("Invalid mState:" + mState
+ " for enabled true"));
@@ -422,6 +722,10 @@
case STATE_ENABLED_AVAILABLE:
mState = STATE_DISABLED_AVAILABLE;
break;
+ case STATE_DISABLED_AVAILABLE:
+ case STATE_DISABLED_UNAVAILABLE:
+ // already disabled: no-op
+ return;
default:
throw (new IllegalStateException("Invalid mState:" + mState
+ " for enabled false"));
@@ -562,7 +866,7 @@
AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1];
// going through adapter to take advantage of routing cache
mASA.getDevicesForAttributes(attributes).toArray(devices);
- final boolean able = AudioSystem.canBeSpatialized(attributes, format, devices);
+ final boolean able = canBeSpatializedOnDevice(attributes, format, devices);
logd("canBeSpatialized returning " + able);
return able;
}
@@ -688,6 +992,9 @@
if (!checkSpatForHeadTracking("setDesiredHeadTrackingMode")) {
return;
}
+ if (mode != Spatializer.HEAD_TRACKING_MODE_DISABLED) {
+ mDesiredHeadTrackingModeWhenEnabled = mode;
+ }
try {
if (mDesiredHeadTrackingMode != mode) {
mDesiredHeadTrackingMode = mode;
@@ -701,6 +1008,84 @@
}
}
+ synchronized void setHeadTrackerEnabled(boolean enabled, @NonNull AudioDeviceAttributes ada) {
+ final int deviceType = ada.getType();
+ final boolean wireless = isWireless(deviceType);
+
+ for (SADeviceState deviceState : mSADevices) {
+ if (deviceType == deviceState.mDeviceType
+ && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+ || !wireless) {
+ if (!deviceState.mHasHeadTracker) {
+ Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled
+ + " device:" + ada + " on a device without headtracker");
+ return;
+ }
+ Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada);
+ deviceState.mHeadTrackerEnabled = enabled;
+ break;
+ }
+ }
+ // check current routing to see if it affects the headtracking mode
+ if (ROUTING_DEVICES[0].getType() == deviceType
+ && ROUTING_DEVICES[0].getAddress().equals(ada.getAddress())) {
+ setDesiredHeadTrackingMode(enabled ? mDesiredHeadTrackingModeWhenEnabled
+ : Spatializer.HEAD_TRACKING_MODE_DISABLED);
+ }
+ }
+
+ synchronized boolean hasHeadTracker(@NonNull AudioDeviceAttributes ada) {
+ final int deviceType = ada.getType();
+ final boolean wireless = isWireless(deviceType);
+
+ for (SADeviceState deviceState : mSADevices) {
+ if (deviceType == deviceState.mDeviceType
+ && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+ || !wireless) {
+ return deviceState.mHasHeadTracker;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Configures device in list as having a head tracker
+ * @param ada
+ * @return true if the head tracker is enabled, false otherwise or if device not found
+ */
+ synchronized boolean setHasHeadTracker(@NonNull AudioDeviceAttributes ada) {
+ final int deviceType = ada.getType();
+ final boolean wireless = isWireless(deviceType);
+
+ for (SADeviceState deviceState : mSADevices) {
+ if (deviceType == deviceState.mDeviceType
+ && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+ || !wireless) {
+ deviceState.mHasHeadTracker = true;
+ return deviceState.mHeadTrackerEnabled;
+ }
+ }
+ Log.e(TAG, "setHasHeadTracker: device not found for:" + ada);
+ return false;
+ }
+
+ synchronized boolean isHeadTrackerEnabled(@NonNull AudioDeviceAttributes ada) {
+ final int deviceType = ada.getType();
+ final boolean wireless = isWireless(deviceType);
+
+ for (SADeviceState deviceState : mSADevices) {
+ if (deviceType == deviceState.mDeviceType
+ && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+ || !wireless) {
+ if (!deviceState.mHasHeadTracker) {
+ return false;
+ }
+ return deviceState.mHeadTrackerEnabled;
+ }
+ }
+ return false;
+ }
+
private boolean checkSpatForHeadTracking(String funcName) {
switch (mState) {
case STATE_UNINITIALIZED:
@@ -884,7 +1269,7 @@
}
synchronized void onInitSensors() {
- final boolean init = (mSpatLevel != SpatializationLevel.NONE);
+ final boolean init = mFeatureEnabled && (mSpatLevel != SpatializationLevel.NONE);
final String action = init ? "initializing" : "releasing";
if (mSpat == null) {
Log.e(TAG, "not " + action + " sensors, null spatializer");
@@ -926,6 +1311,12 @@
UUID uuid = sensor.getUuid();
if (uuid.equals(routingDeviceUuid)) {
headHandle = sensor.getHandle();
+ // TODO check risk of race condition:
+ // does this happen before routing is updated?
+ // avoid by supporting adding device here AND in onRoutingUpdated()
+ if (!setHasHeadTracker(ROUTING_DEVICES[0])) {
+ headHandle = -1;
+ }
break;
}
if (uuid.equals(UuidUtils.STANDALONE_UUID)) {
@@ -933,6 +1324,7 @@
}
}
}
+
Sensor screenSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
screenHandle = screenSensor.getHandle();
} else {
@@ -1017,7 +1409,50 @@
for (int mode : modes) {
modesString += Spatializer.headtrackingModeToString(mode) + " ";
}
+ pw.println("\tsupports binaural:" + mBinauralSupported + " / transaural"
+ + mTransauralSupported);
pw.println("\tsupported head tracking modes:" + modesString);
pw.println("\tmSpatOutput:" + mSpatOutput);
+ pw.println("\tdevices:\n");
+ for (SADeviceState device : mSADevices) {
+ pw.println("\t\t" + device + "\n");
+ }
+ }
+
+ private static final class SADeviceState {
+ final int mDeviceType;
+ final @Nullable String mDeviceAddress; // non-null for wireless devices
+ boolean mEnabled = true; // by default, SA is enabled on any device
+ boolean mHasHeadTracker = false;
+ boolean mHeadTrackerEnabled = true; // by default, if head tracker is present, use it
+
+ SADeviceState(int deviceType, @Nullable String address) {
+ mDeviceType = deviceType;
+ mDeviceAddress = address;
+ }
+
+ @Override
+ public String toString() {
+ return "type:" + mDeviceType + " addr:" + mDeviceAddress + " enabled:" + mEnabled
+ + " HT:" + mHasHeadTracker + " HTenabled:" + mHeadTrackerEnabled;
+ }
+ }
+
+ private static boolean isWireless(int deviceType) {
+ for (int type : WIRELESS_TYPES) {
+ if (type == deviceType) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isWirelessSpeaker(int deviceType) {
+ for (int type: WIRELESS_SPEAKER_TYPES) {
+ if (type == deviceType) {
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 05c3f68..aec98f0 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -104,7 +104,8 @@
final int requestedStrength = Utils.getPublicBiometricStrength(promptInfo);
final boolean credentialRequested = Utils.isCredentialRequested(promptInfo);
- final boolean credentialAvailable = trustManager.isDeviceSecure(userId);
+ final boolean credentialAvailable = trustManager.isDeviceSecure(userId,
+ context.getAssociatedDisplayId());
// Assuming that biometric authenticators are listed in priority-order, the rest of this
// function will attempt to find the first authenticator that's as strong or stronger than
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 135276e..94d3d15 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -67,4 +67,9 @@
* display.
*/
public abstract boolean isAppRunningOnAnyVirtualDevice(int uid);
+
+ /**
+ * Returns true if the {@code displayId} is owned by any virtual device
+ */
+ public abstract boolean isDisplayOwnedByAnyVirtualDevice(int displayId);
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 4d3e438..0f4648a 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1717,7 +1717,7 @@
return false;
}
setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
- notifyPasswordChanged(userId);
+ notifyPasswordChanged(credential, userId);
}
if (isCredentialSharedWithParent(userId)) {
// Make sure the profile doesn't get locked straight after setting work challenge.
@@ -2510,9 +2510,11 @@
* Call after {@link #setUserPasswordMetrics} so metrics are updated before
* reporting the password changed.
*/
- private void notifyPasswordChanged(@UserIdInt int userId) {
+ private void notifyPasswordChanged(LockscreenCredential newCredential, @UserIdInt int userId) {
mHandler.post(() -> {
- mInjector.getDevicePolicyManager().reportPasswordChanged(userId);
+ mInjector.getDevicePolicyManager().reportPasswordChanged(
+ PasswordMetrics.computeForCredential(newCredential),
+ userId);
LocalServices.getService(WindowManagerInternal.class).reportPasswordChanged(userId);
});
}
@@ -3447,7 +3449,7 @@
// the caller like DPMS), otherwise it can lead to deadlock.
mHandler.post(() -> unlockUser(userId, null));
}
- notifyPasswordChanged(userId);
+ notifyPasswordChanged(credential, userId);
notifySeparateProfileChallengeChanged(userId);
}
return result;
diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
index fd141bd7..1ae7ac0 100644
--- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
@@ -26,6 +26,7 @@
import android.media.metrics.PlaybackStateEvent;
import android.media.metrics.TrackChangeEvent;
import android.os.Binder;
+import android.os.PersistableBundle;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.util.Base64;
@@ -180,6 +181,20 @@
StatsLog.write(statsEvent);
}
+ public void reportBundleMetrics(String sessionId, PersistableBundle metrics, int userId) {
+ int level = loggingLevel();
+ if (level == LOGGING_LEVEL_BLOCKED) {
+ return;
+ }
+
+ int atomid = metrics.getInt("KEY_STATSD_ATOM_NUMBER");
+ switch (atomid) {
+ default:
+ return;
+ // to be extended as we define statsd atoms
+ }
+ }
+
@Override
public void reportPlaybackStateEvent(
String sessionId, PlaybackStateEvent event, int userId) {
@@ -222,6 +237,21 @@
}
@Override
+ public String getTranscodingSessionId(int userId) {
+ return getSessionIdInternal(userId);
+ }
+
+ @Override
+ public String getEditingSessionId(int userId) {
+ return getSessionIdInternal(userId);
+ }
+
+ @Override
+ public String getBundleSessionId(int userId) {
+ return getSessionIdInternal(userId);
+ }
+
+ @Override
public void reportPlaybackErrorEvent(
String sessionId, PlaybackErrorEvent event, int userId) {
int level = loggingLevel();
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 86ac7c1..d80d9f3 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -178,6 +178,12 @@
boolean userSet, boolean reviewRequired) {
assertFlag();
final long callingId = Binder.clearCallingIdentity();
+ // Do not change fixed permissions, and do not change non-user set permissions that are
+ // granted by default, or granted by role.
+ if (isPermissionFixed(packageName, userId)
+ || (isPermissionGrantedByDefaultOrRole(packageName, userId) && !userSet)) {
+ return;
+ }
try {
if (grant && !reviewRequired) {
mPermManager.grantRuntimePermission(packageName, NOTIFICATION_PERMISSION, userId);
@@ -252,6 +258,24 @@
}
}
+ boolean isPermissionGrantedByDefaultOrRole(String packageName, @UserIdInt int userId) {
+ assertFlag();
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ try {
+ int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION,
+ userId);
+ return (flags & (PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT
+ | PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE)) != 0;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not reach system server", e);
+ }
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
private void assertFlag() {
if (!mMigrationEnabled) {
throw new IllegalStateException("Method called without checking flag value");
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index e8ad2bb..1185890 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -391,6 +391,7 @@
if (migrateToPermission) {
r.importance = appImportance;
+ r.migrateToPm = true;
if (r.uid != UNKNOWN_UID) {
// Don't call into permission system until we have a valid uid
PackagePermission pkgPerm = new PackagePermission(
@@ -2575,7 +2576,7 @@
synchronized (mPackagePreferences) {
mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
}
- if (mPermissionHelper.isMigrationEnabled()) {
+ if (mPermissionHelper.isMigrationEnabled() && r.migrateToPm) {
try {
PackagePermission p = new PackagePermission(
r.pkg, UserHandle.getUserId(r.uid),
@@ -2841,6 +2842,8 @@
boolean userDemotedMsgApp = false;
boolean hasSentValidBubble = false;
+ boolean migrateToPm = false;
+
Delegate delegate = null;
ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 5b2e097..4f1a9e7 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -33,11 +33,6 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
-import com.android.server.pm.pkg.component.ParsedComponent;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProvider;
import android.os.Binder;
import android.os.Process;
import android.os.Trace;
@@ -62,6 +57,11 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ParsedComponent;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Snapshots;
@@ -158,6 +158,7 @@
private final FeatureConfig mFeatureConfig;
private final OverlayReferenceMapper mOverlayReferenceMapper;
private final StateProvider mStateProvider;
+ private final PackageManagerInternal mPmInternal;
private SigningDetails mSystemSigningDetails;
private Set<String> mProtectedBroadcasts = new ArraySet<>();
@@ -250,13 +251,15 @@
String[] forceQueryableList,
boolean systemAppsQueryable,
@Nullable OverlayReferenceMapper.Provider overlayProvider,
- Executor backgroundExecutor) {
+ Executor backgroundExecutor,
+ PackageManagerInternal pmInternal) {
mFeatureConfig = featureConfig;
mForceQueryableByDevicePackageNames = forceQueryableList;
mSystemAppsQueryable = systemAppsQueryable;
mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
overlayProvider);
mStateProvider = stateProvider;
+ mPmInternal = pmInternal;
mBackgroundExecutor = backgroundExecutor;
mSnapshot = makeCache();
}
@@ -289,6 +292,7 @@
}
mBackgroundExecutor = null;
+ mPmInternal = null;
mSnapshot = new SnapshotCache.Sealed<>();
}
@@ -508,7 +512,7 @@
};
AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig,
forcedQueryablePackageNames, forceSystemAppsQueryable, null,
- injector.getBackgroundExecutor());
+ injector.getBackgroundExecutor(), pms);
featureConfig.setAppsFilter(appsFilter);
return appsFilter;
}
@@ -1236,9 +1240,9 @@
// shared user members to re-establish visibility between them and other packages.
// NOTE: this must come after all removals from data structures but before we update the
// cache
- if (setting.getSharedUser() != null) {
- final ArraySet<? extends PackageStateInternal> sharedUserPackages =
- setting.getSharedUser().getPackageStates();
+ if (setting.hasSharedUser()) {
+ final ArraySet<PackageStateInternal> sharedUserPackages =
+ mPmInternal.getSharedUserPackages(setting.getSharedUserAppId());
for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
if (sharedUserPackages.valueAt(i) == setting) {
continue;
@@ -1250,9 +1254,9 @@
synchronized (mCacheLock) {
removeAppIdFromVisibilityCache(setting.getAppId());
- if (mShouldFilterCache != null && setting.getSharedUser() != null) {
- final ArraySet<? extends PackageStateInternal> sharedUserPackages =
- setting.getSharedUser().getPackageStates();
+ if (mShouldFilterCache != null && setting.hasSharedUser()) {
+ final ArraySet<PackageStateInternal> sharedUserPackages =
+ mPmInternal.getSharedUserPackages(setting.getSharedUserAppId());
for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
PackageStateInternal siblingSetting =
sharedUserPackages.valueAt(i);
@@ -1367,13 +1371,14 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof");
}
if (callingSetting instanceof PackageStateInternal) {
- if (((PackageStateInternal) callingSetting).getSharedUser() == null) {
- callingPkgSetting = (PackageStateInternal) callingSetting;
- callingSharedPkgSettings = null;
- } else {
+ final PackageStateInternal packageState = (PackageStateInternal) callingSetting;
+ if (packageState.hasSharedUser()) {
callingPkgSetting = null;
- callingSharedPkgSettings = ((PackageStateInternal) callingSetting)
- .getSharedUser().getPackageStates();
+ callingSharedPkgSettings = mPmInternal.getSharedUserPackages(
+ packageState.getSharedUserAppId());
+ } else {
+ callingPkgSetting = packageState;
+ callingSharedPkgSettings = null;
}
} else {
callingPkgSetting = null;
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 0ae3418..0d9ccd2 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -640,4 +640,12 @@
@Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
@Nullable
Pair<PackageStateInternal, SharedUserApi> getPackageOrSharedUser(int appId);
+
+ @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+ @Nullable
+ SharedUserApi getSharedUser(int sharedUserAppIde);
+
+ @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+ @NonNull
+ ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId);
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 78aa45a..04f0aab 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -264,7 +264,7 @@
}
@Nullable
- public SharedUserSetting getSharedUser(String name) {
+ public SharedUserSetting getSharedUserFromId(String name) {
try {
return mSettings.getSharedUserLPw(name, 0, 0, false /*create*/);
} catch (PackageManagerException ignored) {
@@ -298,6 +298,31 @@
public Collection<SharedUserSetting> getAllSharedUsers() {
return mSettings.getAllSharedUsersLPw();
}
+
+ @Nullable
+ public SharedUserApi getSharedUserFromPackageName(String packageName) {
+ return mSettings.getSharedUserSettingLPr(packageName);
+ }
+
+ @Nullable
+ public SharedUserApi getSharedUserFromAppId(int sharedUserAppId) {
+ return (SharedUserSetting) mSettings.getSettingLPr(sharedUserAppId);
+ }
+
+ @NonNull
+ public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
+ final ArraySet<PackageStateInternal> res = new ArraySet<>();
+ final SharedUserSetting sharedUserSetting =
+ (SharedUserSetting) mSettings.getSettingLPr(sharedUserAppId);
+ if (sharedUserSetting != null) {
+ final ArraySet<? extends PackageStateInternal> sharedUserPackages =
+ sharedUserSetting.getPackageStates();
+ for (PackageStateInternal ps : sharedUserPackages) {
+ res.add(ps);
+ }
+ }
+ return res;
+ }
}
private static final Comparator<ProviderInfo> sProviderInitOrderSorter = (p1, p2) -> {
@@ -1570,7 +1595,8 @@
PackageInfo pi = new PackageInfo();
pi.packageName = ps.getPackageName();
pi.setLongVersionCode(ps.getVersionCode());
- pi.sharedUserId = (ps.getSharedUser() != null) ? ps.getSharedUser().getName() : null;
+ SharedUserApi sharedUser = mSettings.getSharedUserFromPackageName(pi.packageName);
+ pi.sharedUserId = (sharedUser != null) ? sharedUser.getName() : null;
pi.firstInstallTime = state.getFirstInstallTime();
pi.lastUpdateTime = ps.getLastUpdateTime();
@@ -4306,7 +4332,7 @@
if (shouldFilterApplication(sus, callingUid, callingUserId)) {
return null;
}
- return sus.name + ":" + sus.userId;
+ return sus.name + ":" + sus.mAppId;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplication(ps, callingUid, callingUserId)) {
@@ -4366,10 +4392,10 @@
if (getInstantAppPackageName(callingUid) != null) {
return Process.INVALID_UID;
}
- final SharedUserSetting suid = mSettings.getSharedUser(sharedUserName);
+ final SharedUserSetting suid = mSettings.getSharedUserFromId(sharedUserName);
if (suid != null && !shouldFilterApplication(suid, callingUid,
UserHandle.getUserId(callingUid))) {
- return suid.userId;
+ return suid.mAppId;
}
return Process.INVALID_UID;
}
@@ -5430,7 +5456,7 @@
public SparseArray<String> getAppsWithSharedUserIds() {
final SparseArray<String> sharedUserIds = new SparseArray<>();
for (SharedUserSetting setting : mSettings.getAllSharedUsers()) {
- sharedUserIds.put(UserHandle.getAppId(setting.userId), setting.name);
+ sharedUserIds.put(UserHandle.getAppId(setting.mAppId), setting.name);
}
return sharedUserIds;
}
@@ -5440,12 +5466,12 @@
public String[] getSharedUserPackagesForPackage(@NonNull String packageName,
@UserIdInt int userId) {
final PackageStateInternal packageSetting = mSettings.getPackage(packageName);
- if (packageSetting == null || packageSetting.getSharedUser() == null) {
+ if (packageSetting == null || mSettings.getSharedUserFromPackageName(packageName) == null) {
return EmptyArray.STRING;
}
ArraySet<? extends PackageStateInternal> packages =
- packageSetting.getSharedUser().getPackageStates();
+ mSettings.getSharedUserFromPackageName(packageName).getPackageStates();
final int numPackages = packages.size();
String[] res = new String[numPackages];
int i = 0;
@@ -5627,4 +5653,16 @@
private int getSupplementalProcessUid() {
return getPackage(mService.getSupplementalProcessPackageName()).getUid();
}
+
+ @Nullable
+ @Override
+ public SharedUserApi getSharedUser(int sharedUserAppId) {
+ return mSettings.getSharedUserFromAppId(sharedUserAppId);
+ }
+
+ @NonNull
+ @Override
+ public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
+ return mSettings.getSharedUserPackages(sharedUserAppId);
+ }
}
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index 40d4c03..583348b 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -852,4 +852,20 @@
return super.getPackageOrSharedUser(appId);
}
}
+
+ @Nullable
+ @Override
+ public SharedUserApi getSharedUser(int sharedUserAppId) {
+ synchronized (mLock) {
+ return super.getSharedUser(sharedUserAppId);
+ }
+ }
+
+ @NonNull
+ @Override
+ public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
+ synchronized (mLock) {
+ return super.getSharedUserPackages(sharedUserAppId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index 24c08d1..72e67da 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -1283,4 +1283,20 @@
return current.mComputer.getPackageOrSharedUser(appId);
}
}
+
+ @Nullable
+ @Override
+ public SharedUserApi getSharedUser(int sharedUserAppId) {
+ try (ThreadComputer current = snapshot()) {
+ return current.mComputer.getSharedUser(sharedUserAppId);
+ }
+ }
+
+ @NonNull
+ @Override
+ public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
+ try (ThreadComputer current = snapshot()) {
+ return current.mComputer.getSharedUserPackages(sharedUserAppId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 3fb4ab1..3220b31 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -467,13 +467,14 @@
private void clearPackageStateForUserLIF(PackageSetting ps, int userId,
PackageRemovedInfo outInfo, int flags) {
final AndroidPackage pkg;
+ final SharedUserSetting sus;
synchronized (mPm.mLock) {
pkg = mPm.mPackages.get(ps.getPackageName());
+ sus = mPm.mSettings.getSharedUserSettingLPr(ps);
}
mAppDataHelper.destroyAppProfilesLIF(pkg);
- final SharedUserSetting sus = ps.getSharedUser();
final List<AndroidPackage> sharedUserPkgs =
sus != null ? sus.getPackages() : Collections.emptyList();
final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index c418a10..684e6ba 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -86,7 +86,6 @@
import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
-import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -254,15 +253,21 @@
final String realPkgName = request.mRealPkgName;
final List<String> changedAbiCodePath = result.mChangedAbiCodePath;
final PackageSetting pkgSetting;
- if (request.mPkgSetting != null && request.mPkgSetting.getSharedUser() != null
- && request.mPkgSetting.getSharedUser() != result.mPkgSetting.getSharedUser()) {
- // shared user changed, remove from old shared user
- final SharedUserSetting sus = request.mPkgSetting.getSharedUser();
- sus.removePackage(request.mPkgSetting);
- // Prune unused SharedUserSetting
- if (mPm.mSettings.checkAndPruneSharedUserLPw(sus, false)) {
- // Set the app ID in removed info for UID_REMOVED broadcasts
- reconciledPkg.mInstallResult.mRemovedInfo.mRemovedAppId = sus.userId;
+ if (request.mPkgSetting != null) {
+ SharedUserSetting requestSharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(
+ request.mPkgSetting);
+ SharedUserSetting resultSharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(
+ result.mPkgSetting);
+ if (requestSharedUserSetting != null
+ && requestSharedUserSetting != resultSharedUserSetting) {
+ // shared user changed, remove from old shared user
+ requestSharedUserSetting.removePackage(request.mPkgSetting);
+ // Prune unused SharedUserSetting
+ if (mPm.mSettings.checkAndPruneSharedUserLPw(requestSharedUserSetting, false)) {
+ // Set the app ID in removed info for UID_REMOVED broadcasts
+ reconciledPkg.mInstallResult.mRemovedInfo.mRemovedAppId =
+ requestSharedUserSetting.mAppId;
+ }
}
}
if (result.mExistingSettingCopied) {
@@ -279,8 +284,9 @@
mPm.mSettings.removeRenamedPackageLPw(parsedPackage.getPackageName());
}
}
- if (pkgSetting.getSharedUser() != null) {
- pkgSetting.getSharedUser().addPackage(pkgSetting);
+ SharedUserSetting sharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(pkgSetting);
+ if (sharedUserSetting != null) {
+ sharedUserSetting.addPackage(pkgSetting);
}
if (reconciledPkg.mInstallArgs != null
&& reconciledPkg.mInstallArgs.mForceQueryableOverride) {
@@ -327,8 +333,8 @@
ksms.removeAppKeySetDataLPw(pkg.getPackageName());
}
if (reconciledPkg.mSharedUserSignaturesChanged) {
- pkgSetting.getSharedUser().signaturesChanged = Boolean.TRUE;
- pkgSetting.getSharedUser().signatures.mSigningDetails = reconciledPkg.mSigningDetails;
+ sharedUserSetting.signaturesChanged = Boolean.TRUE;
+ sharedUserSetting.signatures.mSigningDetails = reconciledPkg.mSigningDetails;
}
pkgSetting.setSigningDetails(reconciledPkg.mSigningDetails);
@@ -963,7 +969,7 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
reconciledPackages = ReconcilePackageUtils.reconcilePackages(
reconcileRequest, mSharedLibraries,
- mPm.mSettings.getKeySetManagerService());
+ mPm.mSettings.getKeySetManagerService(), mPm.mSettings);
} catch (ReconcileFailure e) {
for (InstallRequest request : requests) {
request.mInstallResult.setError("Reconciliation failed...", e);
@@ -1242,7 +1248,10 @@
// we'll check this again later when scanning, but we want to
// bail early here before tripping over redefined permissions.
final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
- if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+ final SharedUserSetting signatureCheckSus = mPm.mSettings.getSharedUserSettingLPr(
+ signatureCheckPs);
+ if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, signatureCheckSus,
+ scanFlags)) {
if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ parsedPackage.getPackageName() + " upgrade keys do not match the "
@@ -1257,7 +1266,9 @@
ReconcilePackageUtils.isRecoverSignatureUpdateNeeded(
mPm.getSettingsVersionForPackage(parsedPackage));
// We don't care about disabledPkgSetting on install for now.
- final boolean compatMatch = verifySignatures(signatureCheckPs, null,
+ final boolean compatMatch =
+ PackageManagerServiceUtils.verifySignatures(signatureCheckPs,
+ signatureCheckSus, null,
parsedPackage.getSigningDetails(), compareCompat, compareRecover,
isRollback);
// The new KeySets will be re-added later in the scanning process.
@@ -1496,6 +1507,7 @@
int targetParseFlags = parseFlags;
final PackageSetting ps;
final PackageSetting disabledPs;
+ final SharedUserSetting sharedUserSetting;
if (replace) {
final String pkgName11 = parsedPackage.getPackageName();
synchronized (mPm.mLock) {
@@ -1527,10 +1539,11 @@
ps = mPm.mSettings.getPackageLPr(pkgName11);
disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps);
+ sharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(ps);
// verify signatures are valid
final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
- if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
+ if (ksms.shouldCheckUpgradeKeySetLocked(ps, sharedUserSetting, scanFlags)) {
if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) {
throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"New package not signed by keys specified by upgrade-keysets: "
@@ -1743,16 +1756,18 @@
final PackageSetting sourcePackageSetting;
final KeySetManagerService ksms;
+ final SharedUserSetting sharedUserSetting;
synchronized (mPm.mLock) {
sourcePackageSetting = mPm.mSettings.getPackageLPr(sourcePackageName);
ksms = mPm.mSettings.getKeySetManagerService();
+ sharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(sourcePackageSetting);
}
final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
if (sourcePackageName.equals(parsedPackage.getPackageName())
&& (ksms.shouldCheckUpgradeKeySetLocked(
- sourcePackageSetting, scanFlags))) {
+ sourcePackageSetting, sharedUserSetting, scanFlags))) {
return ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
} else {
@@ -3574,7 +3589,8 @@
mPm.getSettingsVersionForPackage(parsedPackage)));
final Map<String, ReconciledPackage> reconcileResult =
ReconcilePackageUtils.reconcilePackages(reconcileRequest,
- mSharedLibraries, mPm.mSettings.getKeySetManagerService());
+ mSharedLibraries, mPm.mSettings.getKeySetManagerService(),
+ mPm.mSettings);
appIdCreated = optimisticallyRegisterAppId(scanResult);
commitReconciledScanResultLocked(reconcileResult.get(pkgName),
mPm.mUserManager.getUserIds());
@@ -3658,6 +3674,7 @@
final PackageSetting installedPkgSetting;
final PackageSetting originalPkgSetting;
final SharedUserSetting sharedUserSetting;
+ SharedUserSetting oldSharedUserSetting = null;
synchronized (mPm.mLock) {
platformPackage = mPm.getPlatformPackage();
@@ -3683,17 +3700,21 @@
&& (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
&& sharedUserSetting != null) {
Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
- + " (uid=" + sharedUserSetting.userId + "):"
+ + " (uid=" + sharedUserSetting.mAppId + "):"
+ " packages=" + sharedUserSetting.packages);
}
+ if (installedPkgSetting != null) {
+ oldSharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(installedPkgSetting);
+ }
}
final boolean isPlatformPackage = platformPackage != null
&& platformPackage.getPackageName().equals(parsedPackage.getPackageName());
- return new ScanRequest(parsedPackage, sharedUserSetting,
+ return new ScanRequest(parsedPackage, oldSharedUserSetting,
installedPkgSetting == null ? null : installedPkgSetting.getPkg() /* oldPkg */,
installedPkgSetting /* packageSetting */,
+ sharedUserSetting,
disabledPkgSetting /* disabledPackageSetting */,
originalPkgSetting /* originalPkgSetting */,
realPkgName, parseFlags, scanFlags, isPlatformPackage, user, cpuAbiOverride);
@@ -3703,8 +3724,8 @@
private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
final @ParsingPackageUtils.ParseFlags int parseFlags,
@PackageManagerService.ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
-
+ @Nullable UserHandle user, String cpuAbiOverride)
+ throws PackageManagerException {
final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage, parseFlags,
scanFlags, user, cpuAbiOverride);
final PackageSetting installedPkgSetting = initialScanRequest.mPkgSetting;
@@ -3725,8 +3746,9 @@
synchronized (mPm.mLock) {
assertPackageIsValid(parsedPackage, parseFlags, newScanFlags);
final ScanRequest request = new ScanRequest(parsedPackage,
- initialScanRequest.mSharedUserSetting,
- initialScanRequest.mOldPkg, installedPkgSetting, disabledPkgSetting,
+ initialScanRequest.mOldSharedUserSetting,
+ initialScanRequest.mOldPkg, installedPkgSetting,
+ initialScanRequest.mSharedUserSetting, disabledPkgSetting,
initialScanRequest.mOriginalPkgSetting, initialScanRequest.mRealPkgName,
parseFlags, scanFlags, initialScanRequest.mIsPlatformPackage, user,
cpuAbiOverride);
@@ -3775,8 +3797,9 @@
if (scanSystemPartition && isSystemPkgUpdated) {
// we're updating the disabled package, so, scan it as the package setting
final ScanRequest request = new ScanRequest(parsedPackage,
- initialScanRequest.mSharedUserSetting,
+ mPm.mSettings.getSharedUserSettingLPr(disabledPkgSetting),
null, disabledPkgSetting /* pkgSetting */,
+ initialScanRequest.mSharedUserSetting,
null /* disabledPkgSetting */, null /* originalPkgSetting */,
null, parseFlags, scanFlags,
initialScanRequest.mIsPlatformPackage, user, null);
@@ -3994,12 +4017,14 @@
if (!verifyPackageUpdateLPr(originalPs, pkg)) {
// the new package is incompatible with the original
continue;
- } else if (originalPs.getSharedUser() != null) {
- if (!originalPs.getSharedUser().name.equals(pkg.getSharedUserId())) {
+ } else if (mPm.mSettings.getSharedUserSettingLPr(originalPs) != null) {
+ final String sharedUserSettingsName =
+ mPm.mSettings.getSharedUserSettingLPr(originalPs).name;
+ if (!sharedUserSettingsName.equals(pkg.getSharedUserId())) {
// the shared user id is incompatible with the original
Slog.w(TAG, "Unable to migrate data from " + originalPs.getPackageName()
- + " to " + pkg.getPackageName() + ": old uid "
- + originalPs.getSharedUser().name
+ + " to " + pkg.getPackageName() + ": old shared user settings name "
+ + sharedUserSettingsName
+ " differs from " + pkg.getSharedUserId());
continue;
}
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index afca350..5c29833 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -32,6 +32,7 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.utils.WatchedArrayMap;
import org.xmlpull.v1.XmlPullParser;
@@ -354,9 +355,10 @@
return mKeySets.get(id) != null;
}
- public boolean shouldCheckUpgradeKeySetLocked(PackageStateInternal oldPs, int scanFlags) {
+ public boolean shouldCheckUpgradeKeySetLocked(PackageStateInternal oldPs,
+ SharedUserApi sharedUserSetting, int scanFlags) {
// Can't rotate keys during boot or if sharedUser.
- if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || (oldPs.getSharedUser() != null)
+ if (oldPs == null || (scanFlags & SCAN_INITIAL) != 0 || (sharedUserSetting != null)
|| !oldPs.getKeySetData().isUsingUpgradeKeySets()) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ee5c638..6709bf1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1515,7 +1515,8 @@
return;
}
AndroidPackage pkg = packageState.getPkg();
- SharedUserApi sharedUser = packageState.getSharedUser();
+ SharedUserApi sharedUser = m.mComputer.getSharedUser(
+ packageState.getSharedUserAppId());
String oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, packageState);
if (pkg == null) {
@@ -7867,6 +7868,18 @@
migrateAppsData);
}
+ @Override
+ @NonNull
+ public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
+ return PackageManagerService.this.mComputer.getSharedUserPackages(sharedUserAppId);
+ }
+
+ @Override
+ @Nullable
+ public SharedUserApi getSharedUserApi(int sharedUserAppId) {
+ return mComputer.getSharedUser(sharedUserAppId);
+ }
+
@NonNull
@Override
public PackageStateMutator.InitialState recordInitialState() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index d6340b5..0dae12f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -505,6 +505,7 @@
* @throws PackageManagerException if the signatures did not match.
*/
public static boolean verifySignatures(PackageSetting pkgSetting,
+ @Nullable SharedUserSetting sharedUserSetting,
PackageSetting disabledPkgSetting, SigningDetails parsedSignatures,
boolean compareCompat, boolean compareRecover, boolean isRollback)
throws PackageManagerException {
@@ -555,10 +556,8 @@
}
}
// Check for shared user signatures
- if (pkgSetting.getSharedUser() != null
- && pkgSetting.getSharedUser().signatures.mSigningDetails
- != SigningDetails.UNKNOWN) {
-
+ if (sharedUserSetting != null
+ && sharedUserSetting.getSigningDetails() != SigningDetails.UNKNOWN) {
// Already existing package. Make sure signatures match. In case of signing certificate
// rotation, the packages with newer certs need to be ok with being sharedUserId with
// the older ones. We check to see if either the new package is signed by an older cert
@@ -566,32 +565,32 @@
// with being sharedUser with the existing signing cert.
boolean match =
parsedSignatures.checkCapability(
- pkgSetting.getSharedUser().signatures.mSigningDetails,
+ sharedUserSetting.getSigningDetails(),
SigningDetails.CertCapabilities.SHARED_USER_ID)
- || pkgSetting.getSharedUser().signatures.mSigningDetails.checkCapability(
+ || sharedUserSetting.getSigningDetails().checkCapability(
parsedSignatures,
SigningDetails.CertCapabilities.SHARED_USER_ID);
// Special case: if the sharedUserId capability check failed it could be due to this
// being the only package in the sharedUserId so far and the lineage being updated to
// deny the sharedUserId capability of the previous key in the lineage.
- if (!match && pkgSetting.getSharedUser().packages.size() == 1
- && pkgSetting.getSharedUser().packages
+ if (!match && sharedUserSetting.packages.size() == 1
+ && sharedUserSetting.packages
.valueAt(0).getPackageName().equals(packageName)) {
match = true;
}
if (!match && compareCompat) {
match = matchSignaturesCompat(
- packageName, pkgSetting.getSharedUser().signatures, parsedSignatures);
+ packageName, sharedUserSetting.signatures, parsedSignatures);
}
if (!match && compareRecover) {
match =
matchSignaturesRecover(packageName,
- pkgSetting.getSharedUser().signatures.mSigningDetails,
+ sharedUserSetting.signatures.mSigningDetails,
parsedSignatures,
SigningDetails.CertCapabilities.SHARED_USER_ID)
|| matchSignaturesRecover(packageName,
parsedSignatures,
- pkgSetting.getSharedUser().signatures.mSigningDetails,
+ sharedUserSetting.signatures.mSigningDetails,
SigningDetails.CertCapabilities.SHARED_USER_ID);
compatMatch |= match;
}
@@ -599,14 +598,14 @@
throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + packageName
+ " has no signatures that match those in shared user "
- + pkgSetting.getSharedUser().name + "; ignoring!");
+ + sharedUserSetting.name + "; ignoring!");
}
// It is possible that this package contains a lineage that blocks sharedUserId access
// to an already installed package in the sharedUserId signed with a previous key.
// Iterate over all of the packages in the sharedUserId and ensure any that are signed
// with a key in this package's lineage have the SHARED_USER_ID capability granted.
if (parsedSignatures.hasPastSigningCertificates()) {
- for (PackageSetting shUidPkgSetting : pkgSetting.getSharedUser().packages) {
+ for (PackageSetting shUidPkgSetting : sharedUserSetting.packages) {
// if the current package in the sharedUserId is the package being updated then
// skip this check as the update may revoke the sharedUserId capability from
// the key with which this app was previously signed.
@@ -633,7 +632,7 @@
// If the lineage of this package diverges from the lineage of the sharedUserId then
// do not allow the installation to proceed.
if (!parsedSignatures.hasCommonAncestor(
- pkgSetting.getSharedUser().signatures.mSigningDetails)) {
+ sharedUserSetting.signatures.mSigningDetails)) {
throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + packageName + " has a signing lineage "
+ "that diverges from the lineage of the sharedUserId");
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index bd32d03..6efc55a 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -76,12 +76,9 @@
public class PackageSetting extends SettingBase implements PackageStateInternal {
/**
- * Temporary holding space for the shared user ID. While parsing package settings, the
- * shared users tag may come after the packages. In this case, we must delay linking the
- * shared user setting with the package setting. The shared user ID lets us link the
- * two objects.
+ * The shared user ID lets us link this object to {@link SharedUserSetting}.
*/
- private int sharedUserId;
+ private int mSharedUserAppId;
@Nullable
private Map<String, Set<String>> mimeGroups;
@@ -130,13 +127,6 @@
@Nullable
private AndroidPackage pkg;
- /**
- * WARNING. The object reference is important. We perform integer equality and NOT
- * object equality to check whether shared user settings are the same.
- */
- @Nullable
- private SharedUserSetting sharedUser;
-
/** @see AndroidPackage#getPath() */
@NonNull
private File mPath;
@@ -210,7 +200,7 @@
String legacyNativeLibraryPath, String primaryCpuAbi,
String secondaryCpuAbi, String cpuAbiOverride,
long longVersionCode, int pkgFlags, int pkgPrivateFlags,
- int sharedUserId,
+ int sharedUserAppId,
String[] usesSdkLibraries, long[] usesSdkLibrariesVersionsMajor,
String[] usesStaticLibraries, long[] usesStaticLibrariesVersions,
Map<String, Set<String>> mimeGroups,
@@ -231,7 +221,7 @@
this.versionCode = longVersionCode;
this.signatures = new PackageSignatures();
this.installSource = InstallSource.EMPTY;
- this.sharedUserId = sharedUserId;
+ this.mSharedUserAppId = sharedUserAppId;
mDomainSetId = domainSetId;
copyMimeGroups(mimeGroups);
mSnapshot = makeCache();
@@ -261,7 +251,6 @@
super(original);
copyPackageSetting(original);
if (sealedSnapshot) {
- sharedUser = sharedUser == null ? null : sharedUser.snapshot();
mSnapshot = new SnapshotCache.Sealed();
} else {
mSnapshot = makeCache();
@@ -313,10 +302,6 @@
proto.end(packageToken);
}
- public boolean isSharedUser() {
- return sharedUser != null;
- }
-
public PackageSetting setAppId(int appId) {
this.mAppId = appId;
onChanged();
@@ -489,11 +474,18 @@
return this;
}
- public int getSharedUserIdInt() {
- if (sharedUser != null) {
- return sharedUser.userId;
- }
- return sharedUserId;
+ public void setSharedUserAppId(int sharedUserAppId) {
+ mSharedUserAppId = sharedUserAppId;
+ }
+
+ @Override
+ public int getSharedUserAppId() {
+ return mSharedUserAppId;
+ }
+
+ @Override
+ public boolean hasSharedUser() {
+ return mSharedUserAppId > 0;
}
@Override
@@ -564,9 +556,7 @@
@Deprecated
@Override
public LegacyPermissionState getLegacyPermissionState() {
- return (sharedUser != null)
- ? sharedUser.getLegacyPermissionState()
- : super.getLegacyPermissionState();
+ return super.getLegacyPermissionState();
}
public PackageSetting setInstallPermissionsFixed(boolean installPermissionsFixed) {
@@ -620,14 +610,13 @@
public void copyPackageSetting(PackageSetting other) {
super.copySettingBase(other);
- sharedUserId = other.sharedUserId;
+ mSharedUserAppId = other.mSharedUserAppId;
mLoadingProgress = other.mLoadingProgress;
legacyNativeLibraryPath = other.legacyNativeLibraryPath;
mName = other.mName;
mRealName = other.mRealName;
mAppId = other.mAppId;
pkg = other.pkg;
- sharedUser = other.sharedUser;
mPath = other.mPath;
mPathString = other.mPathString;
mPrimaryCpuAbi = other.mPrimaryCpuAbi;
@@ -1168,12 +1157,6 @@
return getPkg();
}
- @Nullable
- @Override
- public int getSharedUserId() {
- return sharedUser == null ? -1 : sharedUser.userId;
- }
-
@NonNull
public SigningInfo getSigningInfo() {
return new SigningInfo(signatures.mSigningDetails);
@@ -1238,12 +1221,6 @@
return this;
}
- public PackageSetting setSharedUser(SharedUserSetting sharedUser) {
- this.sharedUser = sharedUser;
- onChanged();
- return this;
- }
-
public PackageSetting setCategoryOverride(int categoryHint) {
this.categoryOverride = categoryHint;
onChanged();
@@ -1377,15 +1354,6 @@
}
/**
- * WARNING. The object reference is important. We perform integer equality and NOT
- * object equality to check whether shared user settings are the same.
- */
- @DataClass.Generated.Member
- public @Nullable SharedUserSetting getSharedUser() {
- return sharedUser;
- }
-
- /**
* @see AndroidPackage#getPath()
*/
@DataClass.Generated.Member
@@ -1488,10 +1456,10 @@
}
@DataClass.Generated(
- time = 1643648635766L,
+ time = 1644270960923L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "private int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override int getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "private int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic void setSharedUserAppId(int)\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index f3d88ed..0b69cd3 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -22,19 +22,18 @@
import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
-import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Log;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.utils.WatchedLongSparseArray;
import java.util.List;
@@ -43,7 +42,7 @@
final class ReconcilePackageUtils {
public static Map<String, ReconciledPackage> reconcilePackages(
final ReconcileRequest request, SharedLibrariesImpl sharedLibraries,
- KeySetManagerService ksms)
+ KeySetManagerService ksms, Settings settings)
throws ReconcileFailure {
final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
@@ -121,7 +120,10 @@
boolean removeAppKeySetData = false;
boolean sharedUserSignaturesChanged = false;
SigningDetails signingDetails = null;
- if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+ SharedUserSetting sharedUserSetting = settings.getSharedUserSettingLPr(
+ signatureCheckPs);
+ if (ksms.shouldCheckUpgradeKeySetLocked(
+ signatureCheckPs, sharedUserSetting, scanFlags)) {
if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
// We just determined the app is signed correctly, so bring
// over the latest parsed certs.
@@ -139,6 +141,7 @@
}
signingDetails = parsedPackage.getSigningDetails();
} else {
+
try {
final Settings.VersionInfo versionInfo =
request.mVersionInfos.get(installPackageName);
@@ -146,9 +149,11 @@
final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
final boolean isRollback = installArgs != null
&& installArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
- final boolean compatMatch = verifySignatures(signatureCheckPs,
- disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
- compareRecover, isRollback);
+ final boolean compatMatch =
+ PackageManagerServiceUtils.verifySignatures(signatureCheckPs,
+ sharedUserSetting, disabledPkgSetting,
+ parsedPackage.getSigningDetails(), compareCompat,
+ compareRecover, isRollback);
// The new KeySets will be re-added later in the scanning process.
if (compatMatch) {
removeAppKeySetData = true;
@@ -161,21 +166,21 @@
// newer
// signing certificate than the existing one, and if so, copy over the new
// details
- if (signatureCheckPs.getSharedUser() != null) {
+ if (sharedUserSetting != null) {
// Attempt to merge the existing lineage for the shared SigningDetails with
// the lineage of the new package; if the shared SigningDetails are not
// returned this indicates the new package added new signers to the lineage
// and/or changed the capabilities of existing signers in the lineage.
SigningDetails sharedSigningDetails =
- signatureCheckPs.getSharedUser().signatures.mSigningDetails;
+ sharedUserSetting.signatures.mSigningDetails;
SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
signingDetails);
if (mergedDetails != sharedSigningDetails) {
- signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+ sharedUserSetting.signatures.mSigningDetails =
mergedDetails;
}
- if (signatureCheckPs.getSharedUser().signaturesChanged == null) {
- signatureCheckPs.getSharedUser().signaturesChanged = Boolean.FALSE;
+ if (sharedUserSetting.signaturesChanged == null) {
+ sharedUserSetting.signaturesChanged = Boolean.FALSE;
}
}
} catch (PackageManagerException e) {
@@ -192,10 +197,10 @@
// updating
// the signatures on the first package scanned for the shared user (i.e. if the
// signaturesChanged state hasn't been initialized yet in SharedUserSetting).
- if (signatureCheckPs.getSharedUser() != null) {
- final Signature[] sharedUserSignatures = signatureCheckPs.getSharedUser()
+ if (sharedUserSetting != null) {
+ final Signature[] sharedUserSignatures = sharedUserSetting
.signatures.mSigningDetails.getSignatures();
- if (signatureCheckPs.getSharedUser().signaturesChanged != null
+ if (sharedUserSetting.signaturesChanged != null
&& compareSignatures(sharedUserSignatures,
parsedPackage.getSigningDetails().getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
@@ -209,7 +214,7 @@
throw new ReconcileFailure(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
"Signature mismatch for shared user: "
- + scanResult.mPkgSetting.getSharedUser());
+ + sharedUserSetting);
} else {
// Treat mismatched signatures on system packages using a shared
// UID as
@@ -219,14 +224,14 @@
"Signature mismatch on system package "
+ parsedPackage.getPackageName()
+ " for shared user "
- + scanResult.mPkgSetting.getSharedUser());
+ + sharedUserSetting);
}
}
sharedUserSignaturesChanged = true;
- signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+ sharedUserSetting.signatures.mSigningDetails =
parsedPackage.getSigningDetails();
- signatureCheckPs.getSharedUser().signaturesChanged = Boolean.TRUE;
+ sharedUserSetting.signaturesChanged = Boolean.TRUE;
}
// File a report about this.
String msg = "System package " + parsedPackage.getPackageName()
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 7e898cb..079903e 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -279,7 +279,7 @@
if (!mPm.mSettings.isDisabledSystemPackageLPr(packageName)) {
// If we don't have a disabled system package to reinstall, the package is
// really gone and its permission state should be removed.
- final SharedUserSetting sus = deletedPs.getSharedUser();
+ SharedUserSetting sus = mPm.mSettings.getSharedUserSettingLPr(deletedPs);
List<AndroidPackage> sharedUserPkgs =
sus != null ? sus.getPackages() : Collections.emptyList();
mPermissionManager.onPackageUninstalled(packageName, deletedPs.getAppId(),
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 79ab563..99d9d58 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -125,6 +125,7 @@
final @ParsingPackageUtils.ParseFlags int parseFlags = request.mParseFlags;
final @PackageManagerService.ScanFlags int scanFlags = request.mScanFlags;
final String realPkgName = request.mRealPkgName;
+ final SharedUserSetting oldSharedUserSetting = request.mOldSharedUserSetting;
final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
final UserHandle user = request.mUser;
final boolean isPlatformPackage = request.mIsPlatformPackage;
@@ -165,18 +166,18 @@
int previousAppId = Process.INVALID_UID;
- if (pkgSetting != null && pkgSetting.getSharedUser() != sharedUserSetting) {
- if (pkgSetting.getSharedUser() != null && sharedUserSetting == null) {
+ if (pkgSetting != null && oldSharedUserSetting != sharedUserSetting) {
+ if (oldSharedUserSetting != null && sharedUserSetting == null) {
previousAppId = pkgSetting.getAppId();
// Log that something is leaving shareduid and keep going
Slog.i(TAG,
"Package " + parsedPackage.getPackageName() + " shared user changed from "
- + pkgSetting.getSharedUser().name + " to " + "<nothing>.");
+ + oldSharedUserSetting.name + " to " + "<nothing>.");
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Package " + parsedPackage.getPackageName() + " shared user changed from "
- + (pkgSetting.getSharedUser() != null
- ? pkgSetting.getSharedUser().name : "<nothing>")
+ + (oldSharedUserSetting != null
+ ? oldSharedUserSetting.name : "<nothing>")
+ " to "
+ (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+ "; replacing with new");
@@ -234,8 +235,8 @@
// TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
// secondaryCpuAbi are not known at this point so we always update them
// to null here, only to reset them at a later point.
- Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
- destCodeFile, parsedPackage.getNativeLibraryDir(),
+ Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, oldSharedUserSetting,
+ sharedUserSetting, destCodeFile, parsedPackage.getNativeLibraryDir(),
AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
@@ -390,16 +391,16 @@
+ " abiOverride=" + pkgSetting.getCpuAbiOverride());
}
- if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.getSharedUser() != null) {
+ if ((scanFlags & SCAN_BOOTING) == 0 && oldSharedUserSetting != null) {
// We don't do this here during boot because we can do it all
// at once after scanning all existing packages.
//
// We also do this *before* we perform dexopt on this package, so that
// we can avoid redundant dexopts, and also to make sure we've got the
// code and package path correct.
- changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.getSharedUser(),
+ changedAbiCodePath = applyAdjustedAbiToSharedUser(oldSharedUserSetting,
parsedPackage, packageAbiHelper.getAdjustedAbiForSharedUser(
- pkgSetting.getSharedUser().packages, parsedPackage));
+ oldSharedUserSetting.packages, parsedPackage));
}
parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
diff --git a/services/core/java/com/android/server/pm/ScanRequest.java b/services/core/java/com/android/server/pm/ScanRequest.java
index 34abdb1..98d11bd 100644
--- a/services/core/java/com/android/server/pm/ScanRequest.java
+++ b/services/core/java/com/android/server/pm/ScanRequest.java
@@ -32,14 +32,16 @@
@NonNull public final ParsedPackage mParsedPackage;
/** The package this package replaces */
@Nullable public final AndroidPackage mOldPkg;
- /** Shared user settings, if the package has a shared user */
- @Nullable public final SharedUserSetting mSharedUserSetting;
+ /** Shared user settings, if the old package has a shared user */
+ @Nullable public final SharedUserSetting mOldSharedUserSetting;
/**
* Package settings of the currently installed version.
* <p><em>IMPORTANT:</em> The contents of this object may be modified
* during scan.
*/
@Nullable public final PackageSetting mPkgSetting;
+ /** Shared user settings of the currently installed package */
+ @Nullable public final SharedUserSetting mSharedUserSetting;
/** A copy of the settings for the currently installed version */
@Nullable public final PackageSetting mOldPkgSetting;
/** Package settings for the disabled version on the /system partition */
@@ -59,9 +61,10 @@
ScanRequest(
@NonNull ParsedPackage parsedPackage,
- @Nullable SharedUserSetting sharedUserSetting,
+ @Nullable SharedUserSetting oldSharedUserSetting,
@Nullable AndroidPackage oldPkg,
@Nullable PackageSetting pkgSetting,
+ @Nullable SharedUserSetting sharedUserSetting,
@Nullable PackageSetting disabledPkgSetting,
@Nullable PackageSetting originalPkgSetting,
@Nullable String realPkgName,
@@ -73,6 +76,7 @@
mParsedPackage = parsedPackage;
mOldPkg = oldPkg;
mPkgSetting = pkgSetting;
+ mOldSharedUserSetting = oldSharedUserSetting;
mSharedUserSetting = sharedUserSetting;
mOldPkgSetting = pkgSetting == null ? null : new PackageSetting(pkgSetting);
mDisabledPkgSetting = disabledPkgSetting;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 0575b8c..53d0a73 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -780,13 +780,13 @@
SharedUserSetting s = mSharedUsers.get(name);
if (s == null && create) {
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
- s.userId = acquireAndRegisterNewAppIdLPw(s);
- if (s.userId < 0) {
+ s.mAppId = acquireAndRegisterNewAppIdLPw(s);
+ if (s.mAppId < 0) {
// < 0 means we couldn't assign a userid; throw exception
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
"Creating shared user " + name + " failed");
}
- Log.i(PackageManagerService.TAG, "New shared user " + name + ": id=" + s.userId);
+ Log.i(PackageManagerService.TAG, "New shared user " + name + ": id=" + s.mAppId);
mSharedUsers.put(name, s);
}
return s;
@@ -818,8 +818,9 @@
disabled = p;
}
mDisabledSysPackages.put(name, disabled);
- if (disabled.getSharedUser() != null) {
- disabled.getSharedUser().mDisabledPackages.add(disabled);
+ SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(disabled);
+ if (sharedUserSetting != null) {
+ sharedUserSetting.mDisabledPackages.add(disabled);
}
return true;
}
@@ -832,8 +833,9 @@
Log.w(PackageManagerService.TAG, "Package " + name + " is not disabled");
return null;
}
- if (p.getSharedUser() != null) {
- p.getSharedUser().mDisabledPackages.remove(p);
+ SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p);
+ if (sharedUserSetting != null) {
+ sharedUserSetting.mDisabledPackages.remove(p);
}
p.getPkgState().setUpdatedSystemApp(false);
PackageSetting ret = addPackageLPw(name, p.getRealName(), p.getPath(),
@@ -856,9 +858,12 @@
void removeDisabledSystemPackageLPw(String name) {
final PackageSetting p = mDisabledSysPackages.remove(name);
- if (p != null && p.getSharedUser() != null) {
- p.getSharedUser().mDisabledPackages.remove(p);
- checkAndPruneSharedUserLPw(p.getSharedUser(), false);
+ if (p != null) {
+ SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p);
+ if (sharedUserSetting != null) {
+ sharedUserSetting.mDisabledPackages.remove(p);
+ checkAndPruneSharedUserLPw(sharedUserSetting, false);
+ }
}
}
@@ -893,7 +898,7 @@
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
- if (s.userId == uid) {
+ if (s.mAppId == uid) {
return s;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
@@ -901,7 +906,7 @@
return null;
}
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
- s.userId = uid;
+ s.mAppId = uid;
if (registerExistingAppIdLPw(uid, s, name)) {
mSharedUsers.put(name, s);
return s;
@@ -974,7 +979,9 @@
usesStaticLibraries, usesStaticLibrariesVersions,
createMimeGroups(mimeGroupNames), domainSetId);
pkgSetting.setLastModifiedTime(codePath.lastModified());
- pkgSetting.setSharedUser(sharedUser);
+ if (sharedUser != null) {
+ pkgSetting.setSharedUserAppId(sharedUser.mAppId);
+ }
// If this is not a system app, it starts out stopped.
if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
if (DEBUG_STOPPED) {
@@ -1025,7 +1032,7 @@
}
}
if (sharedUser != null) {
- pkgSetting.setAppId(sharedUser.userId);
+ pkgSetting.setAppId(sharedUser.mAppId);
} else {
// Clone the setting here for disabled system packages
if (disabledPkg != null) {
@@ -1069,7 +1076,9 @@
* WARNING: The provided PackageSetting object may be mutated.
*/
static void updatePackageSetting(@NonNull PackageSetting pkgSetting,
- @Nullable PackageSetting disabledPkg, @Nullable SharedUserSetting sharedUser,
+ @Nullable PackageSetting disabledPkg,
+ @Nullable SharedUserSetting existingSharedUserSetting,
+ @Nullable SharedUserSetting sharedUser,
@NonNull File codePath, @Nullable String legacyNativeLibraryPath,
@Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags,
int pkgPrivateFlags, @NonNull UserManagerService userManager,
@@ -1078,16 +1087,18 @@
@Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId)
throws PackageManagerException {
final String pkgName = pkgSetting.getPackageName();
- if (!Objects.equals(pkgSetting.getSharedUser(), sharedUser) && sharedUser != null) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + pkgName + " shared user changed from "
- + (pkgSetting.getSharedUser() != null
- ? pkgSetting.getSharedUser().name : "<nothing>")
- + " to " + sharedUser.name);
- throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED,
- "Updating application package " + pkgName + " failed");
+ if (sharedUser != null) {
+ if (!Objects.equals(existingSharedUserSetting, sharedUser)) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Package " + pkgName + " shared user changed from "
+ + (existingSharedUserSetting != null
+ ? existingSharedUserSetting.name : "<nothing>")
+ + " to " + sharedUser.name);
+ throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED,
+ "Updating application package " + pkgName + " failed");
+ }
+ pkgSetting.setSharedUserAppId(sharedUser.mAppId);
}
- pkgSetting.setSharedUser(sharedUser);
if (!pkgSetting.getPath().equals(codePath)) {
final boolean isSystem = pkgSetting.isSystem();
@@ -1228,11 +1239,13 @@
}
// If this app defines a shared user id initialize
// the shared user signatures as well.
- if (p.getSharedUser() != null
- && p.getSharedUser().signatures.mSigningDetails.getSignatures() == null) {
- p.getSharedUser().signatures.mSigningDetails = pkg.getSigningDetails();
+ SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p);
+ if (sharedUserSetting != null) {
+ if (sharedUserSetting.signatures.mSigningDetails.getSignatures() == null) {
+ sharedUserSetting.signatures.mSigningDetails = pkg.getSigningDetails();
+ }
}
- addPackageSettingLPw(p, p.getSharedUser());
+ addPackageSettingLPw(p, sharedUserSetting);
}
// Utility method that adds a PackageSetting to mPackages and
@@ -1241,23 +1254,24 @@
private void addPackageSettingLPw(PackageSetting p, SharedUserSetting sharedUser) {
mPackages.put(p.getPackageName(), p);
if (sharedUser != null) {
- if (p.getSharedUser() != null && p.getSharedUser() != sharedUser) {
+ SharedUserSetting existingSharedUserSetting = getSharedUserSettingLPr(p);
+ if (existingSharedUserSetting != null && existingSharedUserSetting != sharedUser) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Package " + p.getPackageName() + " was user "
- + p.getSharedUser() + " but is now " + sharedUser
+ + existingSharedUserSetting + " but is now " + sharedUser
+ "; I am not changing its files so it will probably fail!");
- p.getSharedUser().removePackage(p);
- } else if (p.getAppId() != sharedUser.userId) {
+ sharedUser.removePackage(p);
+ } else if (p.getAppId() != sharedUser.mAppId) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Package " + p.getPackageName() + " was user id " + p.getAppId()
- + " but is now user " + sharedUser
- + " with id " + sharedUser.userId
- + "; I am not changing its files so it will probably fail!");
+ "Package " + p.getPackageName() + " was user id " + p.getAppId()
+ + " but is now user " + sharedUser
+ + " with id " + sharedUser.mAppId
+ + "; I am not changing its files so it will probably fail!");
}
sharedUser.addPackage(p);
- p.setSharedUser(sharedUser);
- p.setAppId(sharedUser.userId);
+ p.setSharedUserAppId(sharedUser.mAppId);
+ p.setAppId(sharedUser.mAppId);
}
// If the we know about this user id, we have to update it as it
@@ -1277,7 +1291,7 @@
boolean checkAndPruneSharedUserLPw(SharedUserSetting s, boolean skipCheck) {
if (skipCheck || (s.packages.isEmpty() && s.mDisabledPackages.isEmpty())) {
if (mSharedUsers.remove(s.name) != null) {
- removeAppIdLPw(s.userId);
+ removeAppIdLPw(s.mAppId);
return true;
}
}
@@ -1288,10 +1302,11 @@
final PackageSetting p = mPackages.remove(name);
if (p != null) {
removeInstallerPackageStatus(name);
- if (p.getSharedUser() != null) {
- p.getSharedUser().removePackage(p);
- if (checkAndPruneSharedUserLPw(p.getSharedUser(), false)) {
- return p.getSharedUser().userId;
+ SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p);
+ if (sharedUserSetting != null) {
+ sharedUserSetting.removePackage(p);
+ if (checkAndPruneSharedUserLPw(sharedUserSetting, false)) {
+ return sharedUserSetting.mAppId;
}
} else {
removeAppIdLPw(p.getAppId());
@@ -2465,7 +2480,7 @@
for (final SharedUserSetting usr : mSharedUsers.values()) {
serializer.startTag(null, "shared-user");
serializer.attribute(null, ATTR_NAME, usr.name);
- serializer.attributeInt(null, "userId", usr.userId);
+ serializer.attributeInt(null, "userId", usr.mAppId);
usr.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage());
serializer.endTag(null, "shared-user");
}
@@ -2784,7 +2799,7 @@
serializer.attribute(null, "cpuAbiOverride", pkg.getCpuAbiOverride());
}
- if (pkg.getSharedUser() == null) {
+ if (!pkg.hasSharedUser()) {
serializer.attributeInt(null, "userId", pkg.getAppId());
} else {
serializer.attributeInt(null, "sharedUserId", pkg.getAppId());
@@ -2827,7 +2842,7 @@
serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime());
serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime());
serializer.attributeLong(null, "version", pkg.getVersionCode());
- if (pkg.getSharedUser() == null) {
+ if (!pkg.hasSharedUser()) {
serializer.attributeInt(null, "userId", pkg.getAppId());
} else {
serializer.attributeInt(null, "sharedUserId", pkg.getAppId());
@@ -3099,21 +3114,22 @@
for (int i = 0; i < N; i++) {
final PackageSetting p = mPendingPackages.get(i);
- final int sharedUserId = p.getSharedUserIdInt();
- final Object idObj = getSettingLPr(sharedUserId);
+ final int sharedUserAppId = p.getSharedUserAppId();
+ if (sharedUserAppId <= 0) {
+ continue;
+ }
+ final Object idObj = getSettingLPr(sharedUserAppId);
if (idObj instanceof SharedUserSetting) {
final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
- p.setSharedUser(sharedUser);
- p.setAppId(sharedUser.userId);
addPackageSettingLPw(p, sharedUser);
} else if (idObj != null) {
String msg = "Bad package setting: package " + p.getPackageName()
- + " has shared uid " + sharedUserId + " that is not a shared uid\n";
+ + " has shared uid " + sharedUserAppId + " that is not a shared uid\n";
mReadMessages.append(msg);
PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
} else {
String msg = "Bad package setting: package " + p.getPackageName()
- + " has shared uid " + sharedUserId + " that is not defined\n";
+ + " has shared uid " + sharedUserAppId + " that is not defined\n";
mReadMessages.append(msg);
PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
}
@@ -3146,8 +3162,9 @@
for (PackageSetting disabledPs : mDisabledSysPackages.values()) {
final Object id = getSettingLPr(disabledPs.getAppId());
if (id instanceof SharedUserSetting) {
- disabledPs.setSharedUser((SharedUserSetting) id);
- disabledPs.getSharedUser().mDisabledPackages.add(disabledPs);
+ SharedUserSetting sharedUserSetting = (SharedUserSetting) id;
+ sharedUserSetting.mDisabledPackages.add(disabledPs);
+ disabledPs.setSharedUserAppId(sharedUserSetting.mAppId);
}
}
@@ -3563,7 +3580,9 @@
ps.setLastUpdateTime(parser.getAttributeLongHex(null, "ut", 0));
ps.setAppId(parser.getAttributeInt(null, "userId", 0));
if (ps.getAppId() <= 0) {
- ps.setAppId(parser.getAttributeInt(null, "sharedUserId", 0));
+ final int sharedUserAppId = parser.getAttributeInt(null, "sharedUserId", 0);
+ ps.setAppId(sharedUserAppId);
+ ps.setSharedUserAppId(sharedUserAppId);
}
final float loadingProgress =
parser.getAttributeFloat(null, "loadingProgress", 0);
@@ -3578,7 +3597,13 @@
}
if (parser.getName().equals(TAG_PERMISSIONS)) {
- readInstallPermissionsLPr(parser, ps.getLegacyPermissionState(), users);
+ final LegacyPermissionState legacyState;
+ if (ps.hasSharedUser()) {
+ legacyState = getSettingLPr(ps.getSharedUserAppId()).getLegacyPermissionState();
+ } else {
+ legacyState = ps.getLegacyPermissionState();
+ }
+ readInstallPermissionsLPr(parser, legacyState, users);
} else if (parser.getName().equals(TAG_USES_STATIC_LIB)) {
readUsesStaticLibLPw(parser, ps);
} else if (parser.getName().equals(TAG_USES_SDK_LIB)) {
@@ -3603,7 +3628,7 @@
String name = null;
String realName = null;
int userId = 0;
- int sharedUserId = 0;
+ int sharedUserAppId = 0;
String codePathStr = null;
String legacyCpuAbiString = null;
String legacyNativeLibraryPathStr = null;
@@ -3635,7 +3660,7 @@
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
userId = parser.getAttributeInt(null, "userId", 0);
- sharedUserId = parser.getAttributeInt(null, "sharedUserId", 0);
+ sharedUserAppId = parser.getAttributeInt(null, "sharedUserId", 0);
codePathStr = parser.getAttributeValue(null, "codePath");
legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi");
@@ -3729,7 +3754,7 @@
lastUpdateTime = parser.getAttributeLongHex(null, "ut", 0);
if (PackageManagerService.DEBUG_SETTINGS)
Log.v(PackageManagerService.TAG, "Reading package: " + name + " userId=" + userId
- + " sharedUserId=" + sharedUserId);
+ + " sharedUserId=" + sharedUserAppId);
if (realName != null) {
realName = realName.intern();
}
@@ -3759,12 +3784,12 @@
packageSetting.setLastModifiedTime(timeStamp);
packageSetting.setLastUpdateTime(lastUpdateTime);
}
- } else if (sharedUserId != 0) {
- if (sharedUserId > 0) {
+ } else if (sharedUserAppId != 0) {
+ if (sharedUserAppId > 0) {
packageSetting = new PackageSetting(name.intern(), realName,
new File(codePathStr), legacyNativeLibraryPathStr,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
- versionCode, pkgFlags, pkgPrivateFlags, sharedUserId,
+ versionCode, pkgFlags, pkgPrivateFlags, sharedUserAppId,
null /* usesSdkLibraries */,
null /* usesSdkLibrariesVersions */,
null /* usesStaticLibraries */,
@@ -3775,11 +3800,11 @@
mPendingPackages.add(packageSetting);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name
- + ": sharedUserId=" + sharedUserId + " pkg=" + packageSetting);
+ + ": sharedUserId=" + sharedUserAppId + " pkg=" + packageSetting);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: package " + name
- + " has bad sharedId " + sharedUserId + " at "
+ + " has bad sharedId " + sharedUserAppId + " at "
+ parser.getPositionDescription());
}
} else {
@@ -3849,8 +3874,14 @@
packageSetting.getSignatures()
.readXml(parser,mPastSignatures.untrackedStorage());
} else if (tagName.equals(TAG_PERMISSIONS)) {
- readInstallPermissionsLPr(parser,
- packageSetting.getLegacyPermissionState(), users);
+ final LegacyPermissionState legacyState;
+ if (packageSetting.hasSharedUser()) {
+ legacyState = getSettingLPr(
+ packageSetting.getSharedUserAppId()).getLegacyPermissionState();
+ } else {
+ legacyState = packageSetting.getLegacyPermissionState();
+ }
+ readInstallPermissionsLPr(parser, legacyState, users);
packageSetting.setInstallPermissionsFixed(true);
} else if (tagName.equals("proper-signing-keyset")) {
long id = parser.getAttributeLong(null, "identifier");
@@ -4305,6 +4336,19 @@
return pkg.getCurrentEnabledStateLPr(classNameStr, userId);
}
+ SharedUserSetting getSharedUserSettingLPr(String packageName) {
+ final PackageSetting ps = mPackages.get(packageName);
+ return getSharedUserSettingLPr(ps);
+ }
+
+ @Nullable
+ SharedUserSetting getSharedUserSettingLPr(PackageSetting ps) {
+ if (ps == null || !ps.hasSharedUser()) {
+ return null;
+ }
+ return (SharedUserSetting) getSettingLPr(ps.getSharedUserAppId());
+ }
+
/**
* Returns all users on the device, including pre-created and dying users.
*
@@ -4523,8 +4567,9 @@
pw.print(prefix); pw.print(" userId="); pw.println(ps.getAppId());
- if (ps.getSharedUser() != null) {
- pw.print(prefix); pw.print(" sharedUser="); pw.println(ps.getSharedUser());
+ SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(ps);
+ if (sharedUserSetting != null) {
+ pw.print(prefix); pw.print(" sharedUser="); pw.println(sharedUserSetting);
}
pw.print(prefix); pw.print(" pkg="); pw.println(pkg);
pw.print(prefix); pw.print(" codePath="); pw.println(ps.getPathString());
@@ -4808,7 +4853,7 @@
}
}
- if (ps.getSharedUser() == null || permissionNames != null || dumpAll) {
+ if (!ps.hasSharedUser() || permissionNames != null || dumpAll) {
dumpInstallPermissionsLPr(pw, prefix + " ", permissionNames, permissionsState, users);
}
@@ -4928,7 +4973,7 @@
pw.println(lastDisabledAppCaller);
}
- if (ps.getSharedUser() == null) {
+ if (!ps.hasSharedUser()) {
dumpGidsLPr(pw, prefix + " ", mPermissionDataProvider.getGidsForUid(
UserHandle.getUid(user.id, ps.getAppId())));
dumpRuntimePermissionsLPr(pw, prefix + " ", permissionNames, permissionsState
@@ -4981,7 +5026,7 @@
}
if (!checkin && packageName != null) {
- dumpState.setSharedUser(ps.getSharedUser());
+ dumpState.setSharedUser(getSharedUserSettingLPr(ps));
}
if (!checkin && !printedSomething) {
@@ -5064,7 +5109,7 @@
continue;
}
final LegacyPermissionState permissionsState =
- mPermissionDataProvider.getLegacyPermissionState(su.userId);
+ mPermissionDataProvider.getLegacyPermissionState(su.mAppId);
if (permissionNames != null
&& !permissionsState.hasPermissionState(permissionNames)) {
continue;
@@ -5084,7 +5129,7 @@
pw.println("):");
String prefix = " ";
- pw.print(prefix); pw.print("userId="); pw.println(su.userId);
+ pw.print(prefix); pw.print("userId="); pw.println(su.mAppId);
pw.print(prefix); pw.println("Packages");
final int numPackages = su.packages.size();
@@ -5108,7 +5153,7 @@
for (UserInfo user : users) {
final int userId = user.id;
final int[] gids = mPermissionDataProvider.getGidsForUid(UserHandle.getUid(
- userId, su.userId));
+ userId, su.mAppId));
final Collection<PermissionState> permissions =
permissionsState.getPermissionStates(userId);
if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) {
@@ -5119,7 +5164,7 @@
}
}
} else {
- pw.print("suid,"); pw.print(su.userId); pw.print(","); pw.println(su.name);
+ pw.print("suid,"); pw.print(su.mAppId); pw.print(","); pw.println(su.name);
}
}
}
@@ -5521,7 +5566,7 @@
for (int i = 0; i < packagesSize; i++) {
String packageName = packageStates.keyAt(i);
PackageStateInternal packageState = packageStates.valueAt(i);
- if (packageState.getSharedUser() == null) {
+ if (!packageState.hasSharedUser()) {
List<RuntimePermissionsState.PermissionState> permissions =
getPermissionsFromPermissionsState(
packageState.getLegacyPermissionState(), userId);
@@ -5627,7 +5672,7 @@
packageSetting.getLegacyPermissionState(),
userId);
packageSetting.setInstallPermissionsFixed(true);
- } else if (packageSetting.getSharedUser() == null && !isUpgradeToR) {
+ } else if (!packageSetting.hasSharedUser() && !isUpgradeToR) {
Slogf.w(TAG, "Missing permission state for package %s on user %d",
packageName, userId);
packageSetting.getLegacyPermissionState().setMissing(true, userId);
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 5ef1471..d0e1e6c 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -19,9 +19,6 @@
import android.annotation.NonNull;
import android.content.pm.ApplicationInfo;
import android.content.pm.SigningDetails;
-import com.android.server.pm.pkg.component.ComponentMutateUtils;
-import com.android.server.pm.pkg.component.ParsedProcess;
-import com.android.server.pm.pkg.component.ParsedProcessImpl;
import android.service.pm.PackageServiceDumpProto;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -29,8 +26,12 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ParsedProcess;
+import com.android.server.pm.pkg.component.ParsedProcessImpl;
import com.android.server.utils.SnapshotCache;
import libcore.util.EmptyArray;
@@ -46,7 +47,7 @@
public final class SharedUserSetting extends SettingBase implements SharedUserApi {
final String name;
- int userId;
+ int mAppId;
/** @see SharedUserApi#getUidFlags() **/
int uidFlags;
@@ -98,7 +99,7 @@
private SharedUserSetting(SharedUserSetting orig) {
super(orig);
name = orig.name;
- userId = orig.userId;
+ mAppId = orig.mAppId;
uidFlags = orig.uidFlags;
uidPrivateFlags = orig.uidPrivateFlags;
packages = new ArraySet<>(orig.packages);
@@ -133,12 +134,12 @@
@Override
public String toString() {
return "SharedUserSetting{" + Integer.toHexString(System.identityHashCode(this)) + " "
- + name + "/" + userId + "}";
+ + name + "/" + mAppId + "}";
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
- proto.write(PackageServiceDumpProto.SharedUserProto.UID, userId);
+ proto.write(PackageServiceDumpProto.SharedUserProto.UID, mAppId);
proto.write(PackageServiceDumpProto.SharedUserProto.NAME, name);
proto.end(token);
}
@@ -286,7 +287,7 @@
/** Updates all fields in this shared user setting from another. */
public SharedUserSetting updateFrom(SharedUserSetting sharedUser) {
super.copySettingBase(sharedUser);
- this.userId = sharedUser.userId;
+ this.mAppId = sharedUser.mAppId;
this.uidFlags = sharedUser.uidFlags;
this.uidPrivateFlags = sharedUser.uidPrivateFlags;
this.seInfoTargetSdkVersion = sharedUser.seInfoTargetSdkVersion;
@@ -315,8 +316,8 @@
}
@Override
- public int getUserId() {
- return userId;
+ public int getAppId() {
+ return mAppId;
}
@Override
@@ -363,4 +364,10 @@
public ArrayMap<String, ParsedProcess> getProcesses() {
return processes;
}
+
+ @NonNull
+ @Override
+ public LegacyPermissionState getSharedUserLegacyPermissionState() {
+ return super.getLegacyPermissionState();
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e63d721..5c4d011 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1517,7 +1517,6 @@
return userTypeDetails.getBadgeNoBackground();
}
- @Override
public boolean isProfile(@UserIdInt int userId) {
checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
synchronized (mUsersLock) {
@@ -1526,21 +1525,19 @@
}
}
+ /**
+ * Returns the user type (if it is a profile), empty string (if it isn't a profile),
+ * or null (if the user doesn't exist).
+ */
@Override
- public boolean isManagedProfile(@UserIdInt int userId) {
- checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isManagedProfile");
+ public @Nullable String getProfileType(@UserIdInt int userId) {
+ checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getProfileType");
synchronized (mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
- return userInfo != null && userInfo.isManagedProfile();
- }
- }
-
- @Override
- public boolean isCloneProfile(@UserIdInt int userId) {
- checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isCloneProfile");
- synchronized (mUsersLock) {
- UserInfo userInfo = getUserInfoLU(userId);
- return userInfo != null && userInfo.isCloneProfile();
+ if (userInfo != null) {
+ return userInfo.isProfile() ? userInfo.userType : "";
+ }
+ return null;
}
}
@@ -5163,6 +5160,8 @@
nextId = scanNextAvailableIdLocked();
}
}
+ // If we got here, we probably recycled user ids, so invalidate any caches.
+ UserManager.invalidateStaticUserProperties();
if (nextId < 0) {
throw new IllegalStateException("No user id available!");
}
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 6e6585e..1e3b67c 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -332,13 +332,13 @@
}
String typeName = parser.getAttributeValue(null, "name");
- if (typeName == null) {
+ if (typeName == null || typeName.equals("")) {
Slog.w(LOG_TAG, "Skipping user type with no name in "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
- typeName.intern();
+ typeName = typeName.intern();
UserTypeDetails.Builder builder;
if (typeName.startsWith("android.")) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index e9074c4..ed47bfb7 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -42,6 +42,7 @@
import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE;
import static android.content.pm.PackageManager.MASK_PERMISSION_FLAGS_ALL;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.os.Process.INVALID_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED;
import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED;
@@ -130,6 +131,7 @@
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ParsedPermission;
@@ -2542,16 +2544,18 @@
if (uidState.isMissing()) {
Collection<String> uidRequestedPermissions;
int targetSdkVersion;
- if (ps.getSharedUser() == null) {
+ if (!ps.hasSharedUser()) {
uidRequestedPermissions = pkg.getRequestedPermissions();
targetSdkVersion = pkg.getTargetSdkVersion();
} else {
uidRequestedPermissions = new ArraySet<>();
targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
- List<AndroidPackage> packages = ps.getSharedUser().getPackages();
+ final ArraySet<PackageStateInternal> packages =
+ mPackageManagerInt.getSharedUserPackages(ps.getSharedUserAppId());
int packagesSize = packages.size();
for (int i = 0; i < packagesSize; i++) {
- AndroidPackage sharedUserPackage = packages.get(i);
+ AndroidPackageApi sharedUserPackage =
+ packages.valueAt(i).getAndroidPackage();
uidRequestedPermissions.addAll(
sharedUserPackage.getRequestedPermissions());
targetSdkVersion = Math.min(targetSdkVersion,
@@ -2593,7 +2597,7 @@
if (replace) {
userState.setInstallPermissionsFixed(ps.getPackageName(), false);
- if (ps.getSharedUser() == null) {
+ if (!ps.hasSharedUser()) {
origState = new UidPermissionState(uidState);
uidState.reset();
} else {
@@ -2603,7 +2607,8 @@
// changed runtime permissions here are promotion of an install to
// runtime and revocation of a runtime from a shared user.
if (revokeUnusedSharedUserPermissionsLocked(
- ps.getSharedUser().getPackages(), uidState)) {
+ mPackageManagerInt.getSharedUserPackages(ps.getSharedUserAppId()),
+ uidState)) {
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
runtimePermissionsRevoked = true;
}
@@ -3170,44 +3175,12 @@
inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms, newPerm, ps,
pkg);
}
- } else if (NOTIFICATION_PERMISSIONS.contains(newPerm)) {
- //&& (origPs.getPermissionState(newPerm) == null) {
- // TODO(b/205888750): add back line about origPs once all TODO sections below are
- // propagated through droidfood
- Permission bp = mRegistry.getPermission(newPerm);
- if (bp == null) {
- throw new IllegalStateException("Unknown new permission " + newPerm);
- }
- if (!isUserSetOrPregrantedOrFixed(ps.getPermissionFlags(newPerm))) {
- updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
- int setFlag = ps.isPermissionGranted(newPerm)
- ? 0 : FLAG_PERMISSION_REVIEW_REQUIRED;
- ps.updatePermissionFlags(bp, FLAG_PERMISSION_REVIEW_REQUIRED, setFlag);
- // TODO(b/205888750): remove if/else block once propagated through droidfood
- if (ps.isPermissionGranted(newPerm)
- && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M) {
- ps.revokePermission(bp);
- } else if (!ps.isPermissionGranted(newPerm)
- && pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
- ps.grantPermission(bp);
- }
- } else {
- // TODO(b/205888750): remove once propagated through droidfood
- ps.updatePermissionFlags(bp, FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
- | FLAG_PERMISSION_REVIEW_REQUIRED, 0);
- }
}
}
return updatedUserIds;
}
- private boolean isUserSetOrPregrantedOrFixed(int flags) {
- return (flags & (FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED
- | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED
- | FLAG_PERMISSION_GRANTED_BY_DEFAULT | FLAG_PERMISSION_GRANTED_BY_ROLE)) != 0;
- }
-
@NonNull
@Override
public List<SplitPermissionInfoParcelable> getSplitPermissions() {
@@ -3849,13 +3822,14 @@
@GuardedBy("mLock")
private boolean revokeUnusedSharedUserPermissionsLocked(
- List<AndroidPackage> pkgList, UidPermissionState uidState) {
+ ArraySet<PackageStateInternal> pkgList, UidPermissionState uidState) {
// Collect all used permissions in the UID
final ArraySet<String> usedPermissions = new ArraySet<>();
if (pkgList == null || pkgList.size() == 0) {
return false;
}
- for (AndroidPackage pkg : pkgList) {
+ for (PackageStateInternal pkgState : pkgList) {
+ final AndroidPackageApi pkg = pkgState.getAndroidPackage();
if (pkg.getRequestedPermissions().isEmpty()) {
continue;
}
@@ -4489,8 +4463,13 @@
final int[] userIds = getAllUserIds();
mPackageManagerInt.forEachPackageState(ps -> {
final int appId = ps.getAppId();
- final LegacyPermissionState legacyState = ps.getLegacyPermissionState();
-
+ final LegacyPermissionState legacyState;
+ if (ps.hasSharedUser()) {
+ legacyState = mPackageManagerInt.getSharedUserApi(
+ ps.getSharedUserAppId()).getSharedUserLegacyPermissionState();
+ } else {
+ legacyState = ps.getLegacyPermissionState();
+ }
synchronized (mLock) {
for (final int userId : userIds) {
final UserPermissionState userState = mState.getOrCreateUserState(userId);
@@ -4530,7 +4509,13 @@
}
mPackageManagerInt.forEachPackageSetting(ps -> {
ps.setInstallPermissionsFixed(false);
- final LegacyPermissionState legacyState = ps.getLegacyPermissionState();
+ final LegacyPermissionState legacyState;
+ if (ps.hasSharedUser()) {
+ legacyState = mPackageManagerInt.getSharedUserApi(
+ ps.getSharedUserAppId()).getSharedUserLegacyPermissionState();
+ } else {
+ legacyState = ps.getLegacyPermissionState();
+ }
legacyState.reset();
final int appId = ps.getAppId();
@@ -4876,7 +4861,7 @@
final PackageStateInternal ps =
mPackageManagerInt.getPackageStateInternal(pkg.getPackageName());
- if (ps.getSharedUser() != null) {
+ if (ps.hasSharedUser()) {
// The package is joining a shared user group. This can only happen when a system
// app left shared UID with an update, and then the update is uninstalled.
// If no apps remain in its original shared UID group, clone the current
@@ -4967,7 +4952,7 @@
private void onPackageInstalledInternal(@NonNull AndroidPackage pkg, int previousAppId,
@NonNull PermissionManagerServiceInternal.PackageInstalledParams params,
@UserIdInt int[] userIds) {
- if (previousAppId != Process.INVALID_UID) {
+ if (previousAppId != INVALID_UID) {
handleAppIdMigration(pkg, previousAppId);
}
updatePermissions(pkg.getPackageName(), pkg);
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index 9fa9644..7726d7f 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -159,12 +159,18 @@
String getSecondaryCpuAbi();
/**
- * Retrieves the shared user ID. Note that the actual shared user data is not available here and
- * must be queried separately.
- *
- * @return the shared user this package is a part of, or -1 if it's not part of a shared user.
+ * Whether the package shares the same user ID as other packages
*/
- int getSharedUserId();
+ boolean hasSharedUser();
+
+ /**
+ * Retrieves the shared user app ID. Note that the actual shared user data is not available here
+ * and must be queried separately.
+ *
+ * @return the app ID of the shared user that this package is a part of, or -1 if it's not part
+ * of a shared user.
+ */
+ int getSharedUserAppId();
@NonNull
SigningInfo getSigningInfo();
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index 9395ca5..9a21e24 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -126,8 +126,8 @@
private final String mPrimaryCpuAbi;
@Nullable
private final String mSecondaryCpuAbi;
- @Nullable
- private final int mSharedUserId;
+ private final boolean mHasSharedUser;
+ private final int mSharedUserAppId;
@NonNull
private final String[] mUsesSdkLibraries;
@NonNull
@@ -172,7 +172,8 @@
mPath = pkgState.getPath();
mPrimaryCpuAbi = pkgState.getPrimaryCpuAbi();
mSecondaryCpuAbi = pkgState.getSecondaryCpuAbi();
- mSharedUserId = pkgState.getSharedUserId();
+ mHasSharedUser = pkgState.hasSharedUser();
+ mSharedUserAppId = pkgState.getSharedUserAppId();
mUsesSdkLibraries = pkgState.getUsesSdkLibraries();
mUsesSdkLibrariesVersionsMajor = pkgState.getUsesSdkLibrariesVersionsMajor();
mUsesStaticLibraries = pkgState.getUsesStaticLibraries();
@@ -271,6 +272,15 @@
return mLongVersionCode;
}
+ @Override
+ public boolean hasSharedUser() {
+ return mHasSharedUser;
+ }
+
+ @Override
+ public int getSharedUserAppId() {
+ return mSharedUserAppId;
+ }
/**
* @hide
*/
@@ -516,7 +526,7 @@
}
@DataClass.Generated(
- time = 1640209608883L,
+ time = 1644270981508L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final long mFirstInstallTime\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@@ -615,8 +625,8 @@
}
@DataClass.Generated.Member
- public @Nullable int getSharedUserId() {
- return mSharedUserId;
+ public boolean isHasSharedUser() {
+ return mHasSharedUser;
}
@DataClass.Generated.Member
@@ -671,10 +681,10 @@
}
@DataClass.Generated(
- time = 1640209608912L,
+ time = 1644270981543L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.Integer mSharedUserId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesSdkLibraries\nprivate final @android.annotation.NonNull long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.pkg.PackageStateInternal)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\npublic @java.lang.Override long getVersionCode()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final boolean mHasSharedUser\nprivate final int mSharedUserAppId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesSdkLibraries\nprivate final @android.annotation.NonNull long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.pkg.PackageStateInternal)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\npublic @java.lang.Override long getVersionCode()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override int getSharedUserAppId()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index fb2fe1f..68a00a9 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -50,9 +50,6 @@
@NonNull
InstallSource getInstallSource();
- @Nullable
- SharedUserApi getSharedUser();
-
// TODO: Remove this in favor of boolean APIs
int getFlags();
int getPrivateFlags();
diff --git a/services/core/java/com/android/server/pm/pkg/SharedUserApi.java b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
index 43eac53..94a87f3 100644
--- a/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
+++ b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
@@ -23,6 +23,7 @@
import android.util.ArraySet;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.pkg.component.ParsedProcess;
import java.util.List;
@@ -33,7 +34,7 @@
String getName();
@UserIdInt
- int getUserId();
+ int getAppId();
// flags that are associated with this uid, regardless of any package flags
int getUidFlags();
@@ -65,4 +66,7 @@
ArrayMap<String, ParsedProcess> getProcesses();
boolean isPrivileged();
+
+ @NonNull
+ LegacyPermissionState getSharedUserLegacyPermissionState();
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6e4651c..c0abbf6 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -298,6 +298,7 @@
// must match: config_doubleTapOnHomeBehavior in config.xml
static final int DOUBLE_TAP_HOME_NOTHING = 0;
static final int DOUBLE_TAP_HOME_RECENT_SYSTEM_UI = 1;
+ static final int DOUBLE_TAP_HOME_PIP_MENU = 2;
static final int SHORT_PRESS_WINDOW_NOTHING = 0;
static final int SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE = 1;
@@ -1743,11 +1744,15 @@
// Delay handling home if a double-tap is possible.
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
- mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
- mHomeDoubleTapPending = true;
- mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
- ViewConfiguration.getDoubleTapTimeout());
- return -1;
+ // For the picture-in-picture menu, only add the delay if a pip is there.
+ if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_PIP_MENU
+ || mPictureInPictureVisible) {
+ mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
+ mHomeDoubleTapPending = true;
+ mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
+ ViewConfiguration.getDoubleTapTimeout());
+ return -1;
+ }
}
// Post to main thread to avoid blocking input pipeline.
@@ -1780,7 +1785,7 @@
if (mHomeDoubleTapPending) {
mHomeDoubleTapPending = false;
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
- handleDoubleTapOnHome();
+ mHandler.post(this::handleDoubleTapOnHome);
// TODO(multi-display): Remove display id check once we support recents on
// multi-display
} else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI
@@ -1798,13 +1803,29 @@
}
private void handleDoubleTapOnHome() {
- if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
- mHomeConsumed = true;
- toggleRecentApps();
+ if (mHomeConsumed) {
+ return;
+ }
+ switch (mDoubleTapOnHomeBehavior) {
+ case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI:
+ mHomeConsumed = true;
+ toggleRecentApps();
+ break;
+ case DOUBLE_TAP_HOME_PIP_MENU:
+ mHomeConsumed = true;
+ showPictureInPictureMenuInternal();
+ break;
+ default:
+ Log.w(TAG, "No action or undefined behavior for double tap home: "
+ + mDoubleTapOnHomeBehavior);
+ break;
}
}
private void handleLongPressOnHome(int deviceId, long eventTime) {
+ if (mHomeConsumed) {
+ return;
+ }
if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_NOTHING) {
return;
}
@@ -2362,8 +2383,8 @@
mDoubleTapOnHomeBehavior = res.getInteger(
com.android.internal.R.integer.config_doubleTapOnHomeBehavior);
if (mDoubleTapOnHomeBehavior < DOUBLE_TAP_HOME_NOTHING ||
- mDoubleTapOnHomeBehavior > DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
- mDoubleTapOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
+ mDoubleTapOnHomeBehavior > DOUBLE_TAP_HOME_PIP_MENU) {
+ mDoubleTapOnHomeBehavior = DOUBLE_TAP_HOME_NOTHING;
}
mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_NOTHING;
@@ -5741,6 +5762,8 @@
return "DOUBLE_TAP_HOME_NOTHING";
case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI:
return "DOUBLE_TAP_HOME_RECENT_SYSTEM_UI";
+ case DOUBLE_TAP_HOME_PIP_MENU:
+ return "DOUBLE_TAP_HOME_PIP_MENU";
default:
return Integer.toString(behavior);
}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 040fffa8..19c8cd2 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -160,7 +160,7 @@
private SensorPrivacyManagerInternalImpl mSensorPrivacyManagerInternal;
- private EmergencyCallHelper mEmergencyCallHelper;
+ private CallStateHelper mCallStateHelper;
private KeyguardManager mKeyguardManager;
private int mCurrentUser = USER_NULL;
@@ -191,7 +191,7 @@
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
- mEmergencyCallHelper = new EmergencyCallHelper();
+ mCallStateHelper = new CallStateHelper();
} else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
mCameraPrivacyLightController = new CameraPrivacyLightController(mContext);
}
@@ -702,7 +702,7 @@
}
private boolean canChangeIndividualSensorPrivacy(@UserIdInt int userId, int sensor) {
- if (sensor == MICROPHONE && mEmergencyCallHelper.isInEmergencyCall()) {
+ if (sensor == MICROPHONE && mCallStateHelper.isInEmergencyCall()) {
// During emergency call the microphone toggle managed automatically
Log.i(TAG, "Can't change mic toggle during an emergency call");
return false;
@@ -1523,16 +1523,16 @@
}
}
- private class EmergencyCallHelper {
+ private class CallStateHelper {
private OutgoingEmergencyStateCallback mEmergencyStateCallback;
private CallStateCallback mCallStateCallback;
private boolean mIsInEmergencyCall;
private boolean mMicUnmutedForEmergencyCall;
- private Object mEmergencyStateLock = new Object();
+ private Object mCallStateLock = new Object();
- EmergencyCallHelper() {
+ CallStateHelper() {
mEmergencyStateCallback = new OutgoingEmergencyStateCallback();
mCallStateCallback = new CallStateCallback();
@@ -1543,7 +1543,7 @@
}
boolean isInEmergencyCall() {
- synchronized (mEmergencyStateLock) {
+ synchronized (mCallStateLock) {
return mIsInEmergencyCall;
}
}
@@ -1563,12 +1563,14 @@
public void onCallStateChanged(int state) {
if (state == TelephonyManager.CALL_STATE_IDLE) {
onCallOver();
+ } else {
+ onCall();
}
}
}
private void onEmergencyCall() {
- synchronized (mEmergencyStateLock) {
+ synchronized (mCallStateLock) {
if (!mIsInEmergencyCall) {
mIsInEmergencyCall = true;
if (mSensorPrivacyServiceImpl
@@ -1583,8 +1585,19 @@
}
}
+ private void onCall() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mCallStateLock) {
+ mSensorPrivacyServiceImpl.showSensorUseDialog(MICROPHONE);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private void onCallOver() {
- synchronized (mEmergencyStateLock) {
+ synchronized (mCallStateLock) {
if (mIsInEmergencyCall) {
mIsInEmergencyCall = false;
if (mMicUnmutedForEmergencyCall) {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 6aafd4a..b4c54f9 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -69,6 +69,7 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.Xml;
+import android.view.Display;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
@@ -76,7 +77,9 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -143,6 +146,7 @@
private final LockPatternUtils mLockPatternUtils;
private final UserManager mUserManager;
private final ActivityManager mActivityManager;
+ private VirtualDeviceManagerInternal mVirtualDeviceManager;
@GuardedBy("mUserIsTrusted")
private final SparseBooleanArray mUserIsTrusted = new SparseBooleanArray();
@@ -1293,9 +1297,57 @@
mHandler.obtainMessage(MSG_UNREGISTER_LISTENER, trustListener).sendToTarget();
}
+ /**
+ * @param uid: uid of the calling app (obtained via getCallingUid())
+ * @param displayId: the id of a Display
+ * @return Returns true if both of the following conditions hold -
+ * 1) the uid belongs to an app instead of a system core component; and
+ * 2) either the uid is running on a virtual device or the displayId
+ * is owned by a virtual device
+ */
+ private boolean isAppOrDisplayOnAnyVirtualDevice(int uid, int displayId) {
+ if (UserHandle.isCore(uid)) {
+ return false;
+ }
+
+ if (mVirtualDeviceManager == null) {
+ mVirtualDeviceManager = LocalServices.getService(
+ VirtualDeviceManagerInternal.class);
+ if (mVirtualDeviceManager == null) {
+ // VirtualDeviceManager service may not have been published
+ return false;
+ }
+ }
+
+ switch (displayId) {
+ case Display.INVALID_DISPLAY:
+ // There is no Display object associated with the Context of the calling app.
+ if (mVirtualDeviceManager.isAppRunningOnAnyVirtualDevice(uid)) {
+ return true;
+ }
+ break;
+ case Display.DEFAULT_DISPLAY:
+ // The DEFAULT_DISPLAY is by definition not virtual.
+ break;
+ default:
+ // Other display IDs can belong to logical displays created for other purposes.
+ if (mVirtualDeviceManager.isDisplayOwnedByAnyVirtualDevice(displayId)) {
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
@Override
- public boolean isDeviceLocked(int userId) throws RemoteException {
- userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
+ public boolean isDeviceLocked(int userId, int displayId) throws RemoteException {
+ int uid = getCallingUid();
+ if (isAppOrDisplayOnAnyVirtualDevice(uid, displayId)) {
+ // Virtual displays are considered insecure because they may be used for streaming
+ // to other devices.
+ return false;
+ }
+ userId = ActivityManager.handleIncomingUser(getCallingPid(), uid, userId,
false /* allowAll */, true /* requireFull */, "isDeviceLocked", null);
final long token = Binder.clearCallingIdentity();
@@ -1310,8 +1362,15 @@
}
@Override
- public boolean isDeviceSecure(int userId) throws RemoteException {
- userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
+ public boolean isDeviceSecure(int userId, int displayId) throws RemoteException {
+ int uid = getCallingUid();
+ if (isAppOrDisplayOnAnyVirtualDevice(uid, displayId)) {
+ // Virtual displays are considered insecure because they may be used for streaming
+ // to other devices.
+ return false;
+ }
+
+ userId = ActivityManager.handleIncomingUser(getCallingPid(), uid, userId,
false /* allowAll */, true /* requireFull */, "isDeviceSecure", null);
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 580ab179..9f8d097 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1695,6 +1695,12 @@
}
@Override
+ public String getVoiceInteractorPackageName(IBinder callingVoiceInteractor) {
+ return LocalServices.getService(VoiceInteractionManagerInternal.class)
+ .getVoiceInteractorPackageName(callingVoiceInteractor);
+ }
+
+ @Override
public int startAssistantActivity(String callingPackage, @NonNull String callingFeatureId,
int callingPid, int callingUid, Intent intent, String resolvedType, Bundle bOptions,
int userId) {
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index f61cc94..a83a033 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -65,7 +65,16 @@
@Override
public void binderDied() {
synchronized (mGlobalLock) {
- mOrganizersByFeatureIds.remove(mFeature).destroy();
+ IDisplayAreaOrganizer featureOrganizer = getOrganizerByFeature(mFeature);
+ if (featureOrganizer != null) {
+ IBinder organizerBinder = featureOrganizer.asBinder();
+ if (!organizerBinder.equals(mOrganizer.asBinder()) &&
+ organizerBinder.isBinderAlive()) {
+ Slog.d(TAG, "Dead organizer replaced for feature=" + mFeature);
+ return;
+ }
+ mOrganizersByFeatureIds.remove(mFeature).destroy();
+ }
}
}
}
@@ -172,7 +181,7 @@
organizer.asBinder(), uid);
mOrganizersByFeatureIds.entrySet().removeIf((entry) -> {
final boolean matches = entry.getValue().mOrganizer.asBinder()
- == organizer.asBinder();
+ .equals(organizer.asBinder());
if (matches) {
entry.getValue().destroy();
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 818e4f6..e845034 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3127,12 +3127,16 @@
mDisplayPolicy.switchUser();
}
- @Override
- void removeIfPossible() {
- if (isAnimating(TRANSITION | PARENTS)
+ private boolean shouldDeferRemoval() {
+ return isAnimating(TRANSITION | PARENTS)
// isAnimating is a legacy transition query and will be removed, so also add a
// check for whether this is in a shell-transition when not using legacy.
- || mTransitionController.inTransition()) {
+ || mTransitionController.isTransitionOnDisplay(this);
+ }
+
+ @Override
+ void removeIfPossible() {
+ if (shouldDeferRemoval()) {
mDeferredRemoval = true;
return;
}
@@ -3161,6 +3165,8 @@
mInputMonitor.onDisplayRemoved();
mWmService.mDisplayNotificationController.dispatchDisplayRemoved(this);
mWmService.mAccessibilityController.onDisplayRemoved(mDisplayId);
+ mRootWindowContainer.mTaskSupervisor
+ .getKeyguardController().onDisplayRemoved(mDisplayId);
} finally {
mDisplayReady = false;
}
@@ -3174,7 +3180,8 @@
/** Returns true if a removal action is still being deferred. */
@Override
boolean handleCompleteDeferredRemoval() {
- final boolean stillDeferringRemoval = super.handleCompleteDeferredRemoval();
+ final boolean stillDeferringRemoval =
+ super.handleCompleteDeferredRemoval() || shouldDeferRemoval();
if (!stillDeferringRemoval && mDeferredRemoval) {
removeImmediately();
@@ -5915,8 +5922,6 @@
forAllRootTasks(t -> {t.removeIfPossible("releaseSelfIfNeeded");});
} else if (getTopRootTask() == null) {
removeIfPossible();
- mRootWindowContainer.mTaskSupervisor
- .getKeyguardController().onDisplayRemoved(mDisplayId);
}
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 8b8fd2c..9f2188b 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -158,8 +158,8 @@
/** The final animation targets derived from participants after promotion. */
private ArrayList<WindowContainer> mTargets;
- /** The main display running this transition. */
- private DisplayContent mTargetDisplay;
+ /** The displays that this transition is running on. */
+ private final ArrayList<DisplayContent> mTargetDisplays = new ArrayList<>();
/**
* Set of participating windowtokens (activity/wallpaper) which are visible at the end of
@@ -209,6 +209,10 @@
return mTransientLaunches != null && mTransientLaunches.contains(activity);
}
+ boolean isOnDisplay(@NonNull DisplayContent dc) {
+ return mTargetDisplays.contains(dc);
+ }
+
void setSeamlessRotation(@NonNull WindowContainer wc) {
final ChangeInfo info = mChanges.get(wc);
if (info == null) return;
@@ -280,6 +284,9 @@
mChanges.put(wc, info);
}
mParticipants.add(wc);
+ if (wc.getDisplayContent() != null && !mTargetDisplays.contains(wc.getDisplayContent())) {
+ mTargetDisplays.add(wc.getDisplayContent());
+ }
if (info.mShowWallpaper) {
// Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
final WindowState wallpaper =
@@ -516,15 +523,23 @@
dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
}
- final AsyncRotationController asyncRotationController =
- mTargetDisplay.getAsyncRotationController();
- if (asyncRotationController != null) {
- asyncRotationController.onTransitionFinished();
- }
- // Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
- // so re-compute in case the IME target is changed after transition.
- if (mTransientLaunches != null) {
- mTargetDisplay.computeImeTarget(true /* updateImeTarget */);
+ for (int i = 0; i < mTargetDisplays.size(); ++i) {
+ final DisplayContent dc = mTargetDisplays.get(i);
+ final AsyncRotationController asyncRotationController = dc.getAsyncRotationController();
+ if (asyncRotationController != null) {
+ asyncRotationController.onTransitionFinished();
+ }
+ if (mTransientLaunches != null) {
+ // Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
+ // so re-compute in case the IME target is changed after transition.
+ for (int t = 0; t < mTransientLaunches.size(); ++t) {
+ if (mTransientLaunches.valueAt(t).getDisplayContent() == dc) {
+ dc.computeImeTarget(true /* updateImeTarget */);
+ break;
+ }
+ }
+ }
+ dc.handleCompleteDeferredRemoval();
}
}
@@ -555,19 +570,13 @@
Slog.e(TAG, "Unexpected Sync ID " + syncId + ". Expected " + mSyncId);
return;
}
- boolean hasWallpaper = false;
- DisplayContent dc = null;
- for (int i = mParticipants.size() - 1; i >= 0; --i) {
- final WindowContainer<?> wc = mParticipants.valueAt(i);
- if (dc == null && wc.mDisplayContent != null) {
- dc = wc.mDisplayContent;
- }
- if (!hasWallpaper && isWallpaper(wc)) {
- hasWallpaper = true;
- }
+ if (mTargetDisplays.isEmpty()) {
+ mTargetDisplays.add(mController.mAtm.mRootWindowContainer.getDefaultDisplay());
}
- if (dc == null) dc = mController.mAtm.mRootWindowContainer.getDefaultDisplay();
- mTargetDisplay = dc;
+ // While there can be multiple DC's involved. For now, we just use the first one as
+ // the "primary" one for most things. Eventually, this will need to change, but, for the
+ // time being, we don't have full cross-display transitions so it isn't a problem.
+ final DisplayContent dc = mTargetDisplays.get(0);
if (mState == STATE_ABORT) {
mController.abort(this);
@@ -577,8 +586,11 @@
return;
}
// Ensure that wallpaper visibility is updated with the latest wallpaper target.
- if (hasWallpaper) {
- dc.mWallpaperController.adjustWallpaperWindows();
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ final WindowContainer<?> wc = mParticipants.valueAt(i);
+ if (isWallpaper(wc) && wc.getDisplayContent() != null) {
+ wc.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
+ }
}
mState = STATE_PLAYING;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 3d9d824..c267cba 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -256,6 +256,17 @@
return false;
}
+ /** @return {@code true} if wc is in a participant subtree */
+ boolean isTransitionOnDisplay(@NonNull DisplayContent dc) {
+ if (mCollectingTransition != null && mCollectingTransition.isOnDisplay(dc)) {
+ return true;
+ }
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ if (mPlayingTransitions.get(i).isOnDisplay(dc)) return true;
+ }
+ return false;
+ }
+
/**
* @return {@code true} if {@param ar} is part of a transient-launch activity in an active
* transition.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8536b08..4d1bc22 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1364,6 +1364,7 @@
float lightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
float ambientShadowAlpha = a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0);
float spotShadowAlpha = a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0);
+ a.recycle();
float[] ambientColor = {0.f, 0.f, 0.f, ambientShadowAlpha};
float[] spotColor = {0.f, 0.f, 0.f, spotShadowAlpha};
SurfaceControl.setGlobalShadowSettings(ambientColor, spotColor, lightY, lightZ,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f8ace0c..091d879 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7331,7 +7331,7 @@
}
@Override
- public void reportPasswordChanged(@UserIdInt int userId) {
+ public void reportPasswordChanged(PasswordMetrics metrics, @UserIdInt int userId) {
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
@@ -7363,6 +7363,10 @@
affectedUserIds.addAll(removeCaApprovalsIfNeeded(userId));
saveSettingsForUsersLocked(affectedUserIds);
}
+ if (mInjector.securityLogIsLoggingEnabled()) {
+ SecurityLog.writeEvent(SecurityLog.TAG_PASSWORD_CHANGED,
+ /* complexity */ metrics.determineComplexity(), /*user*/ userId);
+ }
}
/**
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 717168f..f72b23c 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -43,6 +43,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.ParcelUuid;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -64,6 +65,14 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.UUID;
+
+// NOTE about locking order:
+// if there is a path that syncs on BOTH mDevicesByInfo AND mDeviceConnections,
+// this order must be observed
+// 1. synchronized (mDevicesByInfo)
+// 2. synchronized (mDeviceConnections)
+//TODO Introduce a single lock object to lock the whole state and avoid the requirement above.
public class MidiService extends IMidiManager.Stub {
@@ -122,6 +131,9 @@
// UID of BluetoothMidiService
private int mBluetoothServiceUid;
+ private static final UUID MIDI_SERVICE = UUID.fromString(
+ "03B80E5A-EDE8-4B33-A751-6CE34EC4C700");
+
// PackageMonitor for listening to package changes
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
@@ -434,14 +446,19 @@
public void onServiceConnected(ComponentName name, IBinder service) {
IMidiDeviceServer server = null;
if (mBluetoothDevice != null) {
- IBluetoothMidiService mBluetoothMidiService = IBluetoothMidiService.Stub.asInterface(service);
- try {
- // We need to explicitly add the device in a separate method
- // because onBind() is only called once.
- IBinder deviceBinder = mBluetoothMidiService.addBluetoothDevice(mBluetoothDevice);
- server = IMidiDeviceServer.Stub.asInterface(deviceBinder);
- } catch(RemoteException e) {
- Log.e(TAG, "Could not call addBluetoothDevice()", e);
+ IBluetoothMidiService mBluetoothMidiService =
+ IBluetoothMidiService.Stub.asInterface(service);
+ if (mBluetoothMidiService != null) {
+ try {
+ // We need to explicitly add the device in a separate method
+ // because onBind() is only called once.
+ IBinder deviceBinder =
+ mBluetoothMidiService.addBluetoothDevice(
+ mBluetoothDevice);
+ server = IMidiDeviceServer.Stub.asInterface(deviceBinder);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not call addBluetoothDevice()", e);
+ }
}
} else {
server = IMidiDeviceServer.Stub.asInterface(service);
@@ -482,19 +499,19 @@
}
public void removeDeviceConnection(DeviceConnection connection) {
- synchronized (mDeviceConnections) {
- mDeviceConnections.remove(connection);
+ synchronized (mDevicesByInfo) {
+ synchronized (mDeviceConnections) {
+ mDeviceConnections.remove(connection);
- if (mDeviceConnections.size() == 0 && mServiceConnection != null) {
- mContext.unbindService(mServiceConnection);
- mServiceConnection = null;
- if (mBluetoothDevice != null) {
- // Bluetooth devices are ephemeral - remove when no clients exist
- synchronized (mDevicesByInfo) {
+ if (mDeviceConnections.size() == 0 && mServiceConnection != null) {
+ mContext.unbindService(mServiceConnection);
+ mServiceConnection = null;
+ if (mBluetoothDevice != null) {
+ // Bluetooth devices are ephemeral - remove when no clients exist
closeLocked();
+ } else {
+ setDeviceServer(null);
}
- } else {
- setDeviceServer(null);
}
}
}
@@ -589,6 +606,20 @@
}
}
+ // Note, this isn't useful at connect-time because the service UUIDs haven't
+ // been gathered yet.
+ private boolean isBLEMIDIDevice(BluetoothDevice btDevice) {
+ ParcelUuid[] uuids = btDevice.getUuids();
+ if (uuids != null) {
+ for (ParcelUuid uuid : uuids) {
+ if (uuid.getUuid().equals(MIDI_SERVICE)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private final BroadcastReceiver mBleMidiReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -603,6 +634,8 @@
Log.d(TAG, "ACTION_ACL_CONNECTED");
BluetoothDevice btDevice =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ // We can't determine here if this is a BLD-MIDI device, so go ahead and try
+ // to open as a MIDI device, further down it will get figured out.
openBluetoothDevice(btDevice);
}
break;
@@ -611,7 +644,11 @@
Log.d(TAG, "ACTION_ACL_DISCONNECTED");
BluetoothDevice btDevice =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- closeBluetoothDevice(btDevice);
+ // We DO know at this point if we are disconnecting a MIDI device, so
+ // don't bother if we are not.
+ if (isBLEMIDIDevice(btDevice)) {
+ closeBluetoothDevice(btDevice);
+ }
}
break;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index 816dbdb..936940f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -17,6 +17,8 @@
package com.android.server.am;
import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
@@ -52,7 +54,12 @@
import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND;
import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND_SERVICE;
import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATT_DIMENS;
+import static com.android.server.am.AppPermissionTracker.AppPermissionPolicy;
import static com.android.server.am.AppRestrictionController.STOCK_PM_FLAGS;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_FGS_LOCATION;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_FGS_MEDIA_PLAYBACK;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_MEDIA_SESSION;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_PERMISSION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -103,6 +110,7 @@
import android.os.SystemClock;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
+import android.permission.PermissionManager;
import android.provider.DeviceConfig;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -127,6 +135,7 @@
import com.android.server.am.AppRestrictionController.UidBatteryUsageProvider;
import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent;
import com.android.server.apphibernation.AppHibernationManagerInternal;
+import com.android.server.notification.NotificationManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.usage.AppStandbyInternal;
@@ -151,6 +160,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
/**
* Tests for {@link AppRestrictionController}.
@@ -215,6 +225,8 @@
@Mock private PackageManager mPackageManager;
@Mock private PackageManagerInternal mPackageManagerInternal;
@Mock private NotificationManager mNotificationManager;
+ @Mock private NotificationManagerInternal mNotificationManagerInternal;
+ @Mock private PermissionManager mPermissionManager;
@Mock private PermissionManagerServiceInternal mPermissionManagerServiceInternal;
@Mock private MediaSessionManager mMediaSessionManager;
@Mock private RoleManager mRoleManager;
@@ -250,6 +262,7 @@
private AppBindServiceEventsTracker mAppBindServiceEventsTracker;
private AppFGSTracker mAppFGSTracker;
private AppMediaSessionTracker mAppMediaSessionTracker;
+ private AppPermissionTracker mAppPermissionTracker;
@Before
public void setUp() throws Exception {
@@ -289,12 +302,16 @@
doReturn(AppOpsManager.MODE_IGNORED)
.when(mAppOpsManager)
.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, uid, packageName);
- doReturn(PERMISSION_DENIED)
- .when(mPermissionManagerServiceInternal)
- .checkUidPermission(uid, ACCESS_BACKGROUND_LOCATION);
- doReturn(PERMISSION_DENIED)
- .when(mPermissionManagerServiceInternal)
- .checkPermission(packageName, ACCESS_BACKGROUND_LOCATION, userId);
+ final String[] permissions = new String[] {ACCESS_BACKGROUND_LOCATION,
+ ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION};
+ for (String permission : permissions) {
+ doReturn(PERMISSION_DENIED)
+ .when(mPermissionManagerServiceInternal)
+ .checkUidPermission(uid, permission);
+ doReturn(PERMISSION_DENIED)
+ .when(mPermissionManagerServiceInternal)
+ .checkPermission(packageName, permission, userId);
+ }
}
doReturn(appStandbyInfoList).when(mAppStandbyInternal).getAppStandbyBuckets(userId);
}
@@ -536,11 +553,14 @@
final float bgRestrictedThreshold = 4.0f;
final float bgRestrictedThresholdMah =
BATTERY_FULL_CHARGE_MAH * bgRestrictedThreshold / 100.0f;
+ final int testPid = 1234;
+ final int notificationId = 1000;
DeviceConfigSession<Boolean> bgCurrentDrainMonitor = null;
DeviceConfigSession<Long> bgCurrentDrainWindow = null;
DeviceConfigSession<Float> bgCurrentDrainRestrictedBucketThreshold = null;
DeviceConfigSession<Float> bgCurrentDrainBgRestrictedThreshold = null;
+ DeviceConfigSession<Boolean> bgPromptFgsWithNotiToBgRestricted = null;
mBgRestrictionController.addAppBackgroundRestrictionListener(listener);
@@ -587,10 +607,24 @@
isLowRamDeviceStatic() ? 1 : 0]);
bgCurrentDrainBgRestrictedThreshold.set(bgRestrictedThreshold);
+ bgPromptFgsWithNotiToBgRestricted = new DeviceConfigSession<>(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ ConstantsObserver.KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_TO_BG_RESTRICTED,
+ DeviceConfig::getBoolean,
+ mContext.getResources().getBoolean(
+ R.bool.config_bg_prompt_fgs_with_noti_to_bg_restricted));
+ bgPromptFgsWithNotiToBgRestricted.set(true);
+
mCurrentTimeMillis = 10_000L;
doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
doReturn(statsList).when(mBatteryStatsInternal).getBatteryUsageStats(anyObject());
+ doReturn(true).when(mNotificationManagerInternal).isNotificationShown(
+ testPkgName, null, notificationId, testUser);
+ mAppFGSTracker.onForegroundServiceStateChanged(testPkgName, testUid,
+ testPid, true);
+ mAppFGSTracker.onForegroundServiceNotificationUpdated(
+ testPkgName, testUid, notificationId);
runTestBgCurrentDrainMonitorOnce(listener, stats, uids,
new double[]{restrictBucketThresholdMah - 1, 0},
@@ -721,6 +755,85 @@
Thread.sleep(windowMs);
clearInvocations(mInjector.getAppStandbyInternal());
clearInvocations(mBgRestrictionController);
+
+ // We're not going to prompt the user if the abusive app has a FGS with notification.
+ bgPromptFgsWithNotiToBgRestricted.set(false);
+
+ runTestBgCurrentDrainMonitorOnce(listener, stats, uids,
+ new double[]{bgRestrictedThresholdMah + 1, 0},
+ new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+ () -> {
+ doReturn(mCurrentTimeMillis).when(stats).getStatsStartTimestamp();
+ doReturn(mCurrentTimeMillis + windowMs)
+ .when(stats).getStatsEndTimestamp();
+ mCurrentTimeMillis += windowMs + 1;
+ // We won't change restriction level automatically because it needs
+ // user consent.
+ try {
+ listener.verify(timeout, testUid, testPkgName,
+ RESTRICTION_LEVEL_BACKGROUND_RESTRICTED);
+ fail("There shouldn't be level change event like this");
+ } catch (Exception e) {
+ // Expected.
+ }
+ verify(mInjector.getAppStandbyInternal(), never()).setAppStandbyBucket(
+ eq(testPkgName),
+ eq(STANDBY_BUCKET_RARE),
+ eq(testUser),
+ anyInt(), anyInt());
+ // We should have requested to goto background restricted level.
+ verify(mBgRestrictionController, times(1)).handleRequestBgRestricted(
+ eq(testPkgName),
+ eq(testUid));
+ // However, we won't have the prompt to user posted because the policy
+ // is not to show that for FGS with notification.
+ checkNotificationShown(new String[] {testPkgName}, never(), false);
+ });
+
+ // Pretend we have the notification dismissed.
+ mAppFGSTracker.onForegroundServiceNotificationUpdated(
+ testPkgName, testUid, -notificationId);
+ clearInvocations(mInjector.getAppStandbyInternal());
+ clearInvocations(mBgRestrictionController);
+
+ runTestBgCurrentDrainMonitorOnce(listener, stats, uids,
+ new double[]{bgRestrictedThresholdMah + 1, 0},
+ new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+ () -> {
+ doReturn(mCurrentTimeMillis).when(stats).getStatsStartTimestamp();
+ doReturn(mCurrentTimeMillis + windowMs)
+ .when(stats).getStatsEndTimestamp();
+ mCurrentTimeMillis += windowMs + 1;
+ // We won't change restriction level automatically because it needs
+ // user consent.
+ try {
+ listener.verify(timeout, testUid, testPkgName,
+ RESTRICTION_LEVEL_BACKGROUND_RESTRICTED);
+ fail("There shouldn't be level change event like this");
+ } catch (Exception e) {
+ // Expected.
+ }
+ verify(mInjector.getAppStandbyInternal(), never()).setAppStandbyBucket(
+ eq(testPkgName),
+ eq(STANDBY_BUCKET_RARE),
+ eq(testUser),
+ anyInt(), anyInt());
+ // We should have requested to goto background restricted level.
+ verify(mBgRestrictionController, times(1)).handleRequestBgRestricted(
+ eq(testPkgName),
+ eq(testUid));
+ // Verify we have the notification posted now because its FGS is invisible.
+ checkNotificationShown(new String[] {testPkgName}, atLeast(1), true);
+ });
+
+ // Pretend notification is back on.
+ mAppFGSTracker.onForegroundServiceNotificationUpdated(
+ testPkgName, testUid, notificationId);
+ // Now we'll prompt the user even it has a FGS with notification.
+ bgPromptFgsWithNotiToBgRestricted.set(true);
+ clearInvocations(mInjector.getAppStandbyInternal());
+ clearInvocations(mBgRestrictionController);
+
runTestBgCurrentDrainMonitorOnce(listener, stats, uids,
new double[]{bgRestrictedThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros,
@@ -785,6 +898,7 @@
closeIfNotNull(bgCurrentDrainWindow);
closeIfNotNull(bgCurrentDrainRestrictedBucketThreshold);
closeIfNotNull(bgCurrentDrainBgRestrictedThreshold);
+ closeIfNotNull(bgPromptFgsWithNotiToBgRestricted);
}
}
@@ -1179,7 +1293,8 @@
.checkPermission(packageName, perm, UserHandle.getUserId(uid));
doReturn(PERMISSION_GRANTED)
.when(mPermissionManagerServiceInternal)
- .checkUidPermission(uid, ACCESS_BACKGROUND_LOCATION);
+ .checkUidPermission(uid, perm);
+ mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
}
if (mediaControllers != null) {
@@ -1204,7 +1319,8 @@
.checkPermission(packageName, perm, UserHandle.getUserId(uid));
doReturn(PERMISSION_DENIED)
.when(mPermissionManagerServiceInternal)
- .checkUidPermission(uid, ACCESS_BACKGROUND_LOCATION);
+ .checkUidPermission(uid, perm);
+ mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
}
if (topStateThread != null) {
topStateThread.join();
@@ -1272,6 +1388,10 @@
DeviceConfigSession<Long> bgLocationMinDurationThreshold = null;
DeviceConfigSession<Boolean> bgCurrentDrainEventDurationBasedThresholdEnabled = null;
DeviceConfigSession<Boolean> bgBatteryExemptionEnabled = null;
+ DeviceConfigSession<Integer> bgBatteryExemptionTypes = null;
+ DeviceConfigSession<Boolean> bgPermissionMonitorEnabled = null;
+ DeviceConfigSession<String> bgPermissionsInMonitor = null;
+ DeviceConfigSession<Boolean> bgCurrentDrainHighThresholdByBgLocation = null;
mBgRestrictionController.addAppBackgroundRestrictionListener(listener);
@@ -1370,6 +1490,38 @@
AppBatteryExemptionPolicy.DEFAULT_BG_BATTERY_EXEMPTION_ENABLED);
bgBatteryExemptionEnabled.set(false);
+ bgBatteryExemptionTypes = new DeviceConfigSession<>(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES,
+ DeviceConfig::getInt,
+ mContext.getResources().getInteger(
+ R.integer.config_bg_current_drain_exempted_types));
+ bgBatteryExemptionTypes.set(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_FGS_MEDIA_PLAYBACK
+ | STATE_TYPE_FGS_LOCATION | STATE_TYPE_PERMISSION);
+
+ bgPermissionMonitorEnabled = new DeviceConfigSession<>(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ AppPermissionPolicy.KEY_BG_PERMISSION_MONITOR_ENABLED,
+ DeviceConfig::getBoolean,
+ AppPermissionPolicy.DEFAULT_BG_PERMISSION_MONITOR_ENABLED);
+ bgPermissionMonitorEnabled.set(true);
+
+ bgPermissionsInMonitor = new DeviceConfigSession<>(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ AppPermissionPolicy.KEY_BG_PERMISSION_MONITOR_ENABLED,
+ DeviceConfig::getString,
+ Arrays.stream(AppPermissionPolicy.DEFAULT_BG_PERMISSIONS_IN_MONITOR)
+ .collect(Collectors.joining(",")));
+ bgPermissionsInMonitor.set(ACCESS_FINE_LOCATION);
+
+ bgCurrentDrainHighThresholdByBgLocation = new DeviceConfigSession<>(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION,
+ DeviceConfig::getBoolean,
+ mContext.getResources().getBoolean(
+ R.bool.config_bg_current_drain_high_threshold_by_bg_location));
+ bgCurrentDrainHighThresholdByBgLocation.set(true);
+
mCurrentTimeMillis = 10_000L;
doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
@@ -1519,15 +1671,44 @@
setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
mAppBatteryPolicy.reset();
- // Run with bg location permission, with higher current drain.
+ // Turn off the higher threshold for bg location access.
+ bgCurrentDrainHighThresholdByBgLocation.set(false);
+
+ // Run with bg location permission, with moderate current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false,
ACCESS_BACKGROUND_LOCATION, null, null, listener, stats, uids,
- new double[]{restrictBucketHighThresholdMah - 1, 0},
+ new double[]{restrictBucketThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
null, windowMs, null, null, null);
+ // Run with bg location permission, with a bit higher current drain.
+ runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
+ FOREGROUND_SERVICE_TYPE_NONE, 0, false,
+ ACCESS_BACKGROUND_LOCATION, null, null, listener, stats, uids,
+ new double[]{restrictBucketThresholdMah + 1, 0},
+ new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+ false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
+ null, windowMs, null, null, null);
+
+ // Start over.
+ resetBgRestrictionController();
+ setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
+ mAppBatteryPolicy.reset();
+
+ // Turn on the higher threshold for bg location access.
+ bgCurrentDrainHighThresholdByBgLocation.set(true);
+
+ // Run with bg location permission, with higher current drain.
+ runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
+ FOREGROUND_SERVICE_TYPE_NONE, 0, false,
+ ACCESS_BACKGROUND_LOCATION , null, null, listener, stats, uids,
+ new double[]{restrictBucketHighThresholdMah - 1, 0},
+ new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+ true , RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
+ null, windowMs, null, null, null);
+
// Run with bg location permission, with even higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false,
@@ -1610,6 +1791,36 @@
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
null, windowMs, initialBg, initialFgs, initialFg);
+ // Set the policy to exempt media session and permission.
+ bgBatteryExemptionTypes.set(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_PERMISSION);
+ // Start over.
+ resetBgRestrictionController();
+ setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
+ mAppBatteryPolicy.reset();
+
+ // Run with coarse location permission, with high current drain.
+ runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
+ FOREGROUND_SERVICE_TYPE_NONE, 0, false,
+ ACCESS_COARSE_LOCATION, null, null, listener, stats, uids,
+ new double[]{restrictBucketThresholdMah + 1, 0},
+ new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+ false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
+ null, windowMs, initialBg, initialFgs, initialFg);
+
+ // Start over.
+ resetBgRestrictionController();
+ setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
+ mAppBatteryPolicy.reset();
+
+ // Run with fine location permission, with high current drain.
+ runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
+ FOREGROUND_SERVICE_TYPE_NONE, 0, false,
+ ACCESS_FINE_LOCATION, null, null, listener, stats, uids,
+ new double[]{restrictBucketThresholdMah + 1, 0},
+ new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+ true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
+ null, windowMs, initialBg, initialFgs, initialFg);
+
// Start over.
resetBgRestrictionController();
setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
@@ -1639,6 +1850,10 @@
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
null, windowMs, initialBg, initialFgs, initialFg);
+ // Set the policy to exempt all.
+ bgBatteryExemptionTypes.set(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_FGS_MEDIA_PLAYBACK
+ | STATE_TYPE_FGS_LOCATION | STATE_TYPE_PERMISSION);
+
// Start over.
resetBgRestrictionController();
setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
@@ -1673,6 +1888,10 @@
closeIfNotNull(bgLocationMinDurationThreshold);
closeIfNotNull(bgCurrentDrainEventDurationBasedThresholdEnabled);
closeIfNotNull(bgBatteryExemptionEnabled);
+ closeIfNotNull(bgBatteryExemptionTypes);
+ closeIfNotNull(bgPermissionMonitorEnabled);
+ closeIfNotNull(bgPermissionsInMonitor);
+ closeIfNotNull(bgCurrentDrainHighThresholdByBgLocation);
}
}
@@ -1693,6 +1912,15 @@
mAppBatteryExemptionTracker.reset();
mAppBatteryPolicy.reset();
}
+ if (perm != null) {
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManagerServiceInternal)
+ .checkPermission(packageName, perm, UserHandle.getUserId(uid));
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManagerServiceInternal)
+ .checkUidPermission(uid, perm);
+ mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
+ }
runExemptionTestOnce(
packageName, uid, pid, serviceType, sleepMs, stopAfterSleep,
perm, mediaControllers, topStateChanges, resetFGSTracker, false,
@@ -1742,6 +1970,15 @@
);
}
);
+ if (perm != null) {
+ doReturn(PERMISSION_DENIED)
+ .when(mPermissionManagerServiceInternal)
+ .checkPermission(packageName, perm, UserHandle.getUserId(uid));
+ doReturn(PERMISSION_DENIED)
+ .when(mPermissionManagerServiceInternal)
+ .checkUidPermission(uid, perm);
+ mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
+ }
}
@Test
@@ -2307,6 +2544,11 @@
BackgroundRestrictionTest.class),
BackgroundRestrictionTest.this);
controller.addAppStateTracker(mAppBindServiceEventsTracker);
+ mAppPermissionTracker = new AppPermissionTracker(mContext, controller,
+ TestAppPermissionTrackerInjector.class.getDeclaredConstructor(
+ BackgroundRestrictionTest.class),
+ BackgroundRestrictionTest.this);
+ controller.addAppStateTracker(mAppPermissionTracker);
} catch (NoSuchMethodException e) {
// Won't happen.
}
@@ -2401,6 +2643,11 @@
AppBatteryExemptionTracker getAppBatteryExemptionTracker() {
return mAppBatteryExemptionTracker;
}
+
+ @Override
+ AppPermissionTracker getAppPermissionTracker() {
+ return mAppPermissionTracker;
+ }
}
private class TestBaseTrackerInjector<T extends BaseAppStatePolicy>
@@ -2461,6 +2708,19 @@
}
@Override
+ NotificationManagerInternal getNotificationManagerInternal() {
+ return BackgroundRestrictionTest.this.mNotificationManagerInternal;
+ }
+
+ PackageManagerInternal getPackageManagerInternal() {
+ return BackgroundRestrictionTest.this.mPackageManagerInternal;
+ }
+
+ PermissionManager getPermissionManager() {
+ return BackgroundRestrictionTest.this.mPermissionManager;
+ }
+
+ @Override
long getServiceStartForegroundTimeout() {
return 1_000; // ms
}
@@ -2490,6 +2750,10 @@
extends TestBaseTrackerInjector<AppMediaSessionPolicy> {
}
+ private class TestAppPermissionTrackerInjector
+ extends TestBaseTrackerInjector<AppPermissionPolicy> {
+ }
+
private class TestAppBroadcastEventsTrackerInjector
extends TestBaseTrackerInjector<AppBroadcastEventsPolicy> {
@Override
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 7d42a52..0319d8d 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
@@ -43,6 +43,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.when;
import android.app.job.JobInfo;
@@ -137,7 +138,7 @@
.setOverrideDeadline(12)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
- when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+ when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0))).thenReturn(TEST_PACKAGE);
assertEffectiveBucketForMediaExemption(createJobStatus(triggerContentJob), false);
}
@@ -146,7 +147,7 @@
final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
.addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
.build();
- when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+ when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0))).thenReturn(TEST_PACKAGE);
assertEffectiveBucketForMediaExemption(createJobStatus(triggerContentJob), false);
}
@@ -155,7 +156,7 @@
final JobInfo networkJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
- when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+ when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0))).thenReturn(TEST_PACKAGE);
assertEffectiveBucketForMediaExemption(createJobStatus(networkJob), false);
}
@@ -165,7 +166,8 @@
.addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
- when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn("not.test.package");
+ when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0)))
+ .thenReturn("not.test.package");
assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), false);
}
@@ -178,13 +180,25 @@
.addTriggerContentUri(new JobInfo.TriggerContentUri(nonEligibleUri, 0))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
- when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+ when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0))).thenReturn(TEST_PACKAGE);
assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), false);
}
@Test
+ public void testMediaBackupExemption_lowPriorityJobs() {
+ when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0))).thenReturn(TEST_PACKAGE);
+ final JobInfo.Builder jobBuilder = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+ assertEffectiveBucketForMediaExemption(
+ createJobStatus(jobBuilder.setPriority(JobInfo.PRIORITY_LOW).build()), false);
+ assertEffectiveBucketForMediaExemption(
+ createJobStatus(jobBuilder.setPriority(JobInfo.PRIORITY_MIN).build()), false);
+ }
+
+ @Test
public void testMediaBackupExemptionGranted() {
- when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+ when(mJobSchedulerInternal.getCloudMediaProviderPackage(eq(0))).thenReturn(TEST_PACKAGE);
final JobInfo imageUriJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
.addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 40bd800..a0ac506 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1794,6 +1794,98 @@
}
}
+ /**
+ * Test getTimeUntilQuotaConsumedLocked when allowed time equals the bucket window size.
+ */
+ @Test
+ public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (24 * HOUR_IN_MILLIS),
+ mQcConstants.MAX_EXECUTION_TIME_MS - 10 * MINUTE_IN_MILLIS, 5),
+ false);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
+ false);
+
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+ 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 10 * MINUTE_IN_MILLIS);
+ // window size = allowed time, so jobs can essentially run non-stop until they reach the
+ // max execution time.
+ setStandbyBucket(EXEMPTED_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(0,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 10 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ }
+ }
+
+ /**
+ * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
+ * window and the session is rolling out of the window.
+ */
+ @Test
+ public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (24 * HOUR_IN_MILLIS),
+ 10 * MINUTE_IN_MILLIS, 5), false);
+ setStandbyBucket(RARE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(0,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(10 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ }
+
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (8 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ setStandbyBucket(FREQUENT_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(0,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(10 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ }
+
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (2 * HOUR_IN_MILLIS),
+ 10 * MINUTE_IN_MILLIS, 5), false);
+ setStandbyBucket(WORKING_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(0,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(10 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ }
+
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
+ false);
+ // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
+ // max execution time.
+ setStandbyBucket(ACTIVE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(0,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ }
+ }
+
@Test
public void testIsWithinQuotaLocked_NeverApp() {
synchronized (mQuotaController.mLock) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
index b89f36f..f35986b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -230,7 +230,7 @@
val pair = createBasicAndroidPackage(STATIC_LIB_PACKAGE_NAME + "_" + 10, 10L,
staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = 10L)
val parsedPackage = pair.second as ParsedPackage
- val scanRequest = ScanRequest(parsedPackage, null, null, null,
+ val scanRequest = ScanRequest(parsedPackage, null, null, null, null,
null, null, null, 0, 0, false, null, null)
val scanResult = ScanResult(scanRequest, true, null, null, false, 0, null, null, null)
@@ -329,7 +329,7 @@
val packageSetting = mRule.system()
.createBasicSettingBuilder(pair.first.parentFile, parsedPackage.hideAsFinal())
.setPkgFlags(ApplicationInfo.FLAG_SYSTEM).build()
- val scanRequest = ScanRequest(parsedPackage, null, null, null,
+ val scanRequest = ScanRequest(parsedPackage, null, null, null, null,
null, null, null, 0, 0, false, null, null)
val scanResult = ScanResult(scanRequest, true, packageSetting, null,
false, 0, null, null, listOf(testInfo))
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index 6a7d298..68994e6 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -1,6 +1,6 @@
per-file *Alarm* = file:/apex/jobscheduler/OWNERS
per-file *AppOp* = file:/core/java/android/permission/OWNERS
-per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
+per-file *Bluetooth* = file:platform/packages/modules/Bluetooth:master:/framework/java/android/bluetooth/OWNERS
per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
per-file BatteryServiceTest.java = file:platform/hardware/interfaces:/health/aidl/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index e9b5b62..18e0f29 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -16,8 +16,23 @@
package com.android.server.am;
-import static org.junit.Assert.assertNull;
+import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
+import static android.content.Intent.ACTION_BOOT_COMPLETED;
+import static android.content.Intent.ACTION_LOCKED_BOOT_COMPLETED;
+import static android.content.Intent.ACTION_TIME_CHANGED;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_ALL;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY;
+import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
+import static com.android.server.am.BroadcastDispatcher.DeferredBootCompletedBroadcastPerUser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+
+import android.app.ActivityManagerInternal;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -25,10 +40,14 @@
import android.os.Process;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
import androidx.test.filters.SmallTest;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Collections;
@@ -44,6 +63,24 @@
@Presubmit
public class BroadcastRecordTest {
+ private static final int USER0 = UserHandle.USER_SYSTEM;
+ private static final int USER1 = USER0 + 1;
+ private static final int[] USER_LIST = new int[] {USER0, USER1};
+ private static final String PACKAGE1 = "pkg1";
+ private static final String PACKAGE2 = "pkg2";
+ private static final String PACKAGE3 = "pkg3";
+ private static final String PACKAGE4 = "pkg4";
+ private static final String[] PACKAGE_LIST = new String[] {PACKAGE1, PACKAGE2, PACKAGE3,
+ PACKAGE4};
+
+ @Mock
+ ActivityManagerInternal mActivityManagerInternal;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
@Test
public void testCleanupDisabledPackageReceivers() {
final int user0 = UserHandle.USER_SYSTEM;
@@ -61,39 +98,232 @@
// With given package:
// Send to all users, cleanup a package of all users.
- final BroadcastRecord recordAllAll = createBroadcastRecord(receiversM, UserHandle.USER_ALL);
+ final BroadcastRecord recordAllAll = createBroadcastRecord(receiversM, UserHandle.USER_ALL,
+ new Intent());
cleanupDisabledPackageReceivers(recordAllAll, pkgToCleanup, UserHandle.USER_ALL);
assertNull(verifyRemaining(recordAllAll, excludeReceivers(receiversM, pkgToCleanup, -1)));
// Send to all users, cleanup a package of one user.
- final BroadcastRecord recordAllOne = createBroadcastRecord(receiversM, UserHandle.USER_ALL);
+ final BroadcastRecord recordAllOne = createBroadcastRecord(receiversM, UserHandle.USER_ALL,
+ new Intent());
cleanupDisabledPackageReceivers(recordAllOne, pkgToCleanup, user0);
assertNull(verifyRemaining(recordAllOne,
excludeReceivers(receiversM, pkgToCleanup, user0)));
// Send to one user, cleanup a package of all users.
- final BroadcastRecord recordOneAll = createBroadcastRecord(receiversU0, user0);
+ final BroadcastRecord recordOneAll = createBroadcastRecord(receiversU0, user0,
+ new Intent());
cleanupDisabledPackageReceivers(recordOneAll, pkgToCleanup, UserHandle.USER_ALL);
assertNull(verifyRemaining(recordOneAll, excludeReceivers(receiversU0, pkgToCleanup, -1)));
// Send to one user, cleanup a package one user.
- final BroadcastRecord recordOneOne = createBroadcastRecord(receiversU0, user0);
+ final BroadcastRecord recordOneOne = createBroadcastRecord(receiversU0, user0,
+ new Intent());
cleanupDisabledPackageReceivers(recordOneOne, pkgToCleanup, user0);
assertNull(verifyRemaining(recordOneOne, excludeReceivers(receiversU0, pkgToCleanup, -1)));
// Without given package (e.g. stop user):
// Send to all users, cleanup one user.
- final BroadcastRecord recordAllM = createBroadcastRecord(receiversM, UserHandle.USER_ALL);
+ final BroadcastRecord recordAllM = createBroadcastRecord(receiversM, UserHandle.USER_ALL,
+ new Intent());
cleanupDisabledPackageReceivers(recordAllM, null /* packageName */, user1);
assertNull(verifyRemaining(recordAllM,
excludeReceivers(receiversM, null /* packageName */, user1)));
// Send to one user, cleanup one user.
- final BroadcastRecord recordU0 = createBroadcastRecord(receiversU0, user0);
+ final BroadcastRecord recordU0 = createBroadcastRecord(receiversU0, user0, new Intent());
cleanupDisabledPackageReceivers(recordU0, null /* packageName */, user0);
assertNull(verifyRemaining(recordU0, Collections.emptyList()));
}
+ // Test defer BOOT_COMPLETED and LOCKED_BOOT_COMPLETED broaddcasts.
+ @Test
+ public void testDeferBootCompletedBroadcast() {
+ testDeferBootCompletedBroadcast_defer_none(ACTION_BOOT_COMPLETED);
+ testDeferBootCompletedBroadcast_defer_all(ACTION_BOOT_COMPLETED);
+ testDeferBootCompletedBroadcast_defer_background_restricted_only(ACTION_BOOT_COMPLETED);
+ testDeferBootCompletedBroadcast_defer_none(ACTION_LOCKED_BOOT_COMPLETED);
+ testDeferBootCompletedBroadcast_defer_all(ACTION_LOCKED_BOOT_COMPLETED);
+ testDeferBootCompletedBroadcast_defer_background_restricted_only(
+ ACTION_LOCKED_BOOT_COMPLETED);
+ }
+
+ // non-BOOT_COMPLETED broadcast does not get deferred.
+ @Test
+ public void testNoDeferOtherBroadcast() {
+ // no split for non-BOOT_COMPLETED broadcasts.
+ final BroadcastRecord br = createBootCompletedBroadcastRecord(ACTION_TIME_CHANGED);
+ final int origReceiversSize = br.receivers.size();
+
+ SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
+ mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_ALL);
+ // No receivers get deferred.
+ assertEquals(0, deferred.size());
+ assertEquals(origReceiversSize, br.receivers.size());
+ }
+
+ private BroadcastRecord createBootCompletedBroadcastRecord(String action) {
+ final List<ResolveInfo> receivers = createReceiverInfos(PACKAGE_LIST, USER_LIST);
+ final BroadcastRecord br = createBroadcastRecord(receivers, UserHandle.USER_ALL,
+ new Intent(action));
+ assertEquals(PACKAGE_LIST.length * USER_LIST.length, br.receivers.size());
+ return br;
+ }
+
+ // Test type DEFER_BOOT_COMPLETED_BROADCAST_NONE, this type does not defer any receiver.
+ private void testDeferBootCompletedBroadcast_defer_none(String action) {
+ final BroadcastRecord br = createBootCompletedBroadcastRecord(action);
+ final int origReceiversSize = br.receivers.size();
+
+ SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
+ mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_NONE);
+ // No receivers get deferred.
+ assertEquals(0, deferred.size());
+ assertEquals(origReceiversSize, br.receivers.size());
+ }
+
+ // Test type DEFER_BOOT_COMPLETED_BROADCAST_ALL, this type defer all receivers.
+ private void testDeferBootCompletedBroadcast_defer_all(String action) {
+ final BroadcastRecord br = createBootCompletedBroadcastRecord(action);
+
+ SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
+ mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_ALL);
+ // original BroadcastRecord receivers list is empty now.
+ assertTrue(br.receivers.isEmpty());
+
+ assertEquals(PACKAGE_LIST.length * USER_LIST.length, deferred.size());
+ for (int i = 0; i < PACKAGE_LIST.length; i++) {
+ for (final int userId : USER_LIST) {
+ final int uid = UserHandle.getUid(userId, getAppId(i));
+ assertTrue(deferred.contains(uid));
+ assertEquals(1, deferred.get(uid).receivers.size());
+ final ResolveInfo info = (ResolveInfo) deferred.get(uid).receivers.get(0);
+ assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
+ assertEquals(uid, info.activityInfo.applicationInfo.uid);
+ }
+ }
+ }
+
+ // Test type DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY,
+ // This type defers receiver whose app package is background restricted.
+ private void testDeferBootCompletedBroadcast_defer_background_restricted_only(String action) {
+ final BroadcastRecord br = createBootCompletedBroadcastRecord(action);
+ final int origReceiversSize = br.receivers.size();
+
+ // First half packages in PACKAGE_LIST, return BACKGROUND_RESTRICTED.
+ for (int i = 0; i < PACKAGE_LIST.length / 2; i++) {
+ for (int u = 0; u < USER_LIST.length; u++) {
+ final int uid = UserHandle.getUid(USER_LIST[u], getAppId(i));
+ doReturn(RESTRICTION_LEVEL_BACKGROUND_RESTRICTED).when(mActivityManagerInternal)
+ .getRestrictionLevel(eq(uid));
+ }
+ }
+
+ // the second half packages in PACKAGE_LIST, return not BACKGROUND_RESTRICTED.
+ for (int i = PACKAGE_LIST.length / 2; i < PACKAGE_LIST.length; i++) {
+ for (int u = 0; u < USER_LIST.length; u++) {
+ final int uid = UserHandle.getUid(USER_LIST[u], getAppId(i));
+ doReturn(RESTRICTION_LEVEL_BACKGROUND_RESTRICTED - 10).when(
+ mActivityManagerInternal).getRestrictionLevel(eq(uid));
+ }
+ }
+
+ SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
+ mActivityManagerInternal,
+ DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY);
+ // original BroadcastRecord receivers list is half now.
+ assertEquals(origReceiversSize / 2, br.receivers.size());
+ assertEquals(origReceiversSize / 2, deferred.size());
+
+ for (int i = 0; i < PACKAGE_LIST.length / 2; i++) {
+ for (int u = 0; u < USER_LIST.length; u++) {
+ final int uid = UserHandle.getUid(USER_LIST[u], getAppId(i));
+ assertTrue(deferred.contains(uid));
+ assertEquals(1, deferred.get(uid).receivers.size());
+ final ResolveInfo info = (ResolveInfo) deferred.get(uid).receivers.get(0);
+ assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
+ assertEquals(uid, info.activityInfo.applicationInfo.uid);
+ }
+ }
+
+ for (int i = PACKAGE_LIST.length / 2; i < PACKAGE_LIST.length; i++) {
+ for (int u = 0; u < USER_LIST.length; u++) {
+ final int uid = UserHandle.getUid(USER_LIST[u], getAppId(i));
+ boolean found = false;
+ for (int r = 0; r < br.receivers.size(); r++) {
+ final ResolveInfo info = (ResolveInfo) br.receivers.get(r);
+ if (uid == info.activityInfo.applicationInfo.uid) {
+ found = true;
+ break;
+ }
+ }
+ assertTrue(found);
+ }
+ }
+ }
+
+ /**
+ * Test the class {@link BroadcastDispatcher#DeferredBootCompletedBroadcastPerUser}
+ */
+ @Test
+ public void testDeferBootCompletedBroadcast_dispatcher() {
+ testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_LOCKED_BOOT_COMPLETED, false);
+ testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_BOOT_COMPLETED, false);
+ testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_LOCKED_BOOT_COMPLETED, true);
+ testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_BOOT_COMPLETED, true);
+ }
+
+ private void testDeferBootCompletedBroadcast_dispatcher_internal(String action,
+ boolean isAllUidReady) {
+ final List<ResolveInfo> receivers = createReceiverInfos(PACKAGE_LIST, new int[] {USER0});
+ final BroadcastRecord br = createBroadcastRecord(receivers, USER0, new Intent(action));
+ assertEquals(PACKAGE_LIST.length, br.receivers.size());
+
+ SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
+ mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_ALL);
+ // original BroadcastRecord receivers list is empty now.
+ assertTrue(br.receivers.isEmpty());
+ assertEquals(PACKAGE_LIST.length, deferred.size());
+
+ DeferredBootCompletedBroadcastPerUser deferredPerUser =
+ new DeferredBootCompletedBroadcastPerUser(USER0);
+ deferredPerUser.enqueueBootCompletedBroadcasts(action, deferred);
+
+ if (action.equals(ACTION_LOCKED_BOOT_COMPLETED)) {
+ assertEquals(PACKAGE_LIST.length,
+ deferredPerUser.mDeferredLockedBootCompletedBroadcasts.size());
+ assertTrue(deferredPerUser.mLockedBootCompletedBroadcastReceived);
+ for (int i = 0; i < PACKAGE_LIST.length; i++) {
+ final int uid = UserHandle.getUid(USER0, getAppId(i));
+ if (!isAllUidReady) {
+ deferredPerUser.updateUidReady(uid);
+ }
+ BroadcastRecord d = deferredPerUser.dequeueDeferredBootCompletedBroadcast(
+ isAllUidReady);
+ final ResolveInfo info = (ResolveInfo) d.receivers.get(0);
+ assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
+ assertEquals(uid, info.activityInfo.applicationInfo.uid);
+ }
+ assertEquals(0, deferredPerUser.mUidReadyForLockedBootCompletedBroadcast.size());
+ } else if (action.equals(ACTION_BOOT_COMPLETED)) {
+ assertEquals(PACKAGE_LIST.length,
+ deferredPerUser.mDeferredBootCompletedBroadcasts.size());
+ assertTrue(deferredPerUser.mBootCompletedBroadcastReceived);
+ for (int i = 0; i < PACKAGE_LIST.length; i++) {
+ final int uid = UserHandle.getUid(USER0, getAppId(i));
+ if (!isAllUidReady) {
+ deferredPerUser.updateUidReady(uid);
+ }
+ BroadcastRecord d = deferredPerUser.dequeueDeferredBootCompletedBroadcast(
+ isAllUidReady);
+ final ResolveInfo info = (ResolveInfo) d.receivers.get(0);
+ assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
+ assertEquals(uid, info.activityInfo.applicationInfo.uid);
+ }
+ assertEquals(0, deferredPerUser.mUidReadyForBootCompletedBroadcast.size());
+ }
+ }
+
private static void cleanupDisabledPackageReceivers(BroadcastRecord record,
String packageName, int userId) {
record.cleanupDisabledPackageReceiversLocked(packageName, null /* filterByClasses */,
@@ -148,7 +378,7 @@
for (int i = 0; i < packages.length; i++) {
for (final int userId : userIds) {
receivers.add(createResolveInfo(packages[i],
- UserHandle.getUid(userId, Process.FIRST_APPLICATION_UID + i)));
+ UserHandle.getUid(userId, getAppId(i))));
}
}
return receivers;
@@ -172,10 +402,11 @@
return excludedList;
}
- private static BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId) {
+ private static BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
+ Intent intent) {
return new BroadcastRecord(
null /* queue */,
- new Intent(),
+ intent,
null /* callerApp */,
null /* callerPackage */,
null /* callerFeatureId */,
@@ -200,4 +431,8 @@
null /* activityStartsToken */,
false /* timeoutExempt */ );
}
+
+ private static int getAppId(int i) {
+ return Process.FIRST_APPLICATION_UID + i;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 69d8e89..b94b690 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -235,7 +235,8 @@
@Test
public void testAuthenticate_credentialAllowedButNotSetup_returnsNoDeviceCredential()
throws Exception {
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(false);
mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
@@ -254,7 +255,8 @@
// When no biometrics are enrolled, but credentials are set up, status bar should be
// invoked right away with showAuthenticationDialog
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(true);
mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
@@ -532,7 +534,8 @@
public void testAuthenticate_noBiometrics_credentialAllowed() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false);
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(true);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
true /* requireConfirmation */,
Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK);
@@ -601,7 +604,8 @@
public void testAuthenticate_no_Biometrics_noCredential() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false);
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(false);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
true /* requireConfirmation */,
@@ -872,7 +876,8 @@
@Test
public void testBiometricOrCredentialAuth_whenBiometricLockout_showsCredential()
throws Exception {
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(true);
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
.thenReturn(LockoutTracker.LOCKOUT_PERMANENT);
@@ -1168,13 +1173,15 @@
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK);
// When only biometric is requested, and sensor is not strong enough
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(false);
int authenticators = Authenticators.BIOMETRIC_STRONG;
assertEquals(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
invokeCanAuthenticate(mBiometricService, authenticators));
// When credential and biometric are requested, and sensor is not strong enough
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(true);
authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
invokeCanAuthenticate(mBiometricService, authenticators));
@@ -1186,12 +1193,14 @@
mBiometricService.onStart();
// Credential requested but not set up
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(false);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(false);
assertEquals(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED,
invokeCanAuthenticate(mBiometricService, Authenticators.DEVICE_CREDENTIAL));
// Credential requested and set up
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(true);
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
invokeCanAuthenticate(mBiometricService, Authenticators.DEVICE_CREDENTIAL));
}
@@ -1199,7 +1208,8 @@
@Test
public void testCanAuthenticate_whenNoBiometricsEnrolled() throws Exception {
// With credential set up, test the following.
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(true);
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
false /* enrolled */);
@@ -1218,7 +1228,8 @@
public void testCanAuthenticate_whenBiometricsNotEnabledForApps() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false);
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(true);
// When only biometric is requested
int authenticators = Authenticators.BIOMETRIC_STRONG;
@@ -1247,7 +1258,8 @@
invokeCanAuthenticate(mBiometricService, authenticators));
// When credential and biometric are requested, and credential is set up
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(true);
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
invokeCanAuthenticate(mBiometricService, authenticators));
}
@@ -1394,7 +1406,8 @@
// Requesting strong and credential, when credential is setup
resetReceivers();
authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(true);
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
invokeCanAuthenticate(mBiometricService, authenticators));
requestId = invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
@@ -1527,7 +1540,8 @@
public void testWorkAuthentication_fingerprintFailsIfDisabledByDevicePolicyManager()
throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
- when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt()))
+ .thenReturn(true);
when(mDevicePolicyManager
.getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
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 48e2122..fe110e5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -55,6 +55,7 @@
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_PROFILE_OFF_DEADLINE;
import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_TURN_PROFILE_ON_NOTIFICATION;
@@ -997,7 +998,8 @@
addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
// Change the parent user's password.
- dpm.reportPasswordChanged(UserHandle.USER_SYSTEM);
+ dpm.reportPasswordChanged(new PasswordMetrics(CREDENTIAL_TYPE_PASSWORD),
+ UserHandle.USER_SYSTEM);
// The managed profile owner should receive this broadcast.
final Intent intent = new Intent(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED);
@@ -1036,7 +1038,8 @@
addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
// Change the profile's password.
- dpm.reportPasswordChanged(MANAGED_PROFILE_USER_ID);
+ dpm.reportPasswordChanged(new PasswordMetrics(CREDENTIAL_TYPE_PASSWORD),
+ MANAGED_PROFILE_USER_ID);
// Both the device owner and the managed profile owner should receive this broadcast.
final Intent intent = new Intent(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED);
@@ -5531,7 +5534,7 @@
reset(mContext.spiedContext);
// This should be ignored, as there is no lock screen.
- dpm.reportPasswordChanged(userHandle);
+ dpm.reportPasswordChanged(new PasswordMetrics(CREDENTIAL_TYPE_NONE), userHandle);
// No broadcast should be sent.
verify(mContext.spiedContext, times(0)).sendBroadcastAsUser(
@@ -5904,7 +5907,7 @@
when(getServices().lockSettingsInternal.getUserPasswordMetrics(userHandle))
.thenReturn(passwordMetrics);
- dpm.reportPasswordChanged(userHandle);
+ dpm.reportPasswordChanged(passwordMetrics, userHandle);
verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntentAction(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 58e1c43..eae9ee7f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -257,7 +257,7 @@
flushHandlerTasks();
final PasswordMetrics metric = PasswordMetrics.computeForCredential(pattern);
assertEquals(metric, mService.getUserPasswordMetrics(PRIMARY_USER_ID));
- verify(mDevicePolicyManager).reportPasswordChanged(PRIMARY_USER_ID);
+ verify(mDevicePolicyManager).reportPasswordChanged(metric, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
pattern, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 13a8f69..b72b8d2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -30,15 +30,10 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedActivityImpl;
-import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
-import com.android.server.pm.pkg.component.ParsedProviderImpl;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
@@ -53,6 +48,13 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedActivityImpl;
+import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
+import com.android.server.pm.pkg.component.ParsedProviderImpl;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.utils.WatchableTester;
import org.junit.Before;
@@ -101,6 +103,8 @@
AppsFilter.StateProvider mStateProvider;
@Mock
Executor mMockExecutor;
+ @Mock
+ PackageManagerInternal mMockPmInternal;
private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>();
@@ -222,7 +226,7 @@
public void testSystemReadyPropogates() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
appsFilter.onSystemReady();
@@ -234,7 +238,7 @@
public void testQueriesAction_FilterMatches() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -257,7 +261,7 @@
public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
final Signature frameworkSignature = Mockito.mock(Signature.class);
@@ -306,7 +310,7 @@
public void testQueriesProvider_FilterMatches() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -331,7 +335,7 @@
public void testOnUserUpdated_FilterMatches() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -389,7 +393,7 @@
public void testQueriesDifferentProvider_Filters() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -414,7 +418,7 @@
public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -433,7 +437,7 @@
public void testQueriesAction_NoMatchingAction_Filters() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -450,7 +454,7 @@
public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -471,7 +475,7 @@
public void testNoQueries_Filters() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -488,7 +492,7 @@
public void testNoUsesLibrary_Filters() throws Exception {
final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -514,7 +518,7 @@
public void testUsesLibrary_DoesntFilter() throws Exception {
final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -541,7 +545,7 @@
public void testUsesOptionalLibrary_DoesntFilter() throws Exception {
final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -568,7 +572,7 @@
public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception {
final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -600,7 +604,7 @@
public void testForceQueryable_SystemDoesntFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -619,7 +623,7 @@
public void testForceQueryable_NonSystemFilters() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -636,7 +640,7 @@
public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"},
- false, null, mMockExecutor);
+ false, null, mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -655,7 +659,7 @@
public void testSystemSignedTarget_DoesntFilter() throws CertificateException {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
appsFilter.onSystemReady();
final Signature frameworkSignature = Mockito.mock(Signature.class);
@@ -684,7 +688,7 @@
public void testForceQueryableByDevice_NonSystemCaller_Filters() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"},
- false, null, mMockExecutor);
+ false, null, mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -702,7 +706,8 @@
public void testSystemQueryable_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{},
- true /* system force queryable */, null, mMockExecutor);
+ true /* system force queryable */, null, mMockExecutor,
+ mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -720,7 +725,7 @@
public void testQueriesPackage_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -739,7 +744,7 @@
.thenReturn(false);
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -756,7 +761,7 @@
public void testSystemUid_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -772,7 +777,7 @@
public void testSystemUidSecondaryUser_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -789,7 +794,7 @@
public void testNonSystemUid_NoCallingSetting_Filters() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -804,7 +809,7 @@
public void testNoTargetPackage_filters() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -862,7 +867,7 @@
return Collections.emptyMap();
}
},
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -918,7 +923,16 @@
.setOverlayTargetOverlayableName("overlayableName");
ParsingPackage actorOne = pkg("com.some.package.actor.one");
ParsingPackage actorTwo = pkg("com.some.package.actor.two");
-
+ ArraySet<PackageStateInternal> actorSharedSettingPackages = new ArraySet<>();
+ PackageSetting ps1 = getPackageSettingFromParsingPackage(actorOne, DUMMY_ACTOR_APPID,
+ null /*settingBuilder*/);
+ PackageSetting ps2 = getPackageSettingFromParsingPackage(actorTwo, DUMMY_ACTOR_APPID,
+ null /*settingBuilder*/);
+ actorSharedSettingPackages.add(ps1);
+ actorSharedSettingPackages.add(ps2);
+ when(mMockPmInternal.getSharedUserPackages(any(Integer.class))).thenReturn(
+ actorSharedSettingPackages
+ );
final AppsFilter appsFilter = new AppsFilter(
mStateProvider,
mFeatureConfigMock,
@@ -949,20 +963,18 @@
return Collections.emptyMap();
}
},
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_APPID);
SharedUserSetting actorSharedSetting = new SharedUserSetting("actorSharedUser",
targetSetting.getFlags(), targetSetting.getPrivateFlags());
+ actorSharedSetting.mAppId = 100; /* mimic a valid sharedUserSetting.mAppId */
PackageSetting overlaySetting =
simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_APPID);
- simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_APPID,
- null /*settingBuilder*/, actorSharedSetting);
- simulateAddPackage(appsFilter, actorTwo, DUMMY_ACTOR_APPID,
- null /*settingBuilder*/, actorSharedSetting);
-
+ simulateAddPackage(ps1, appsFilter, actorSharedSetting);
+ simulateAddPackage(ps2, appsFilter, actorSharedSetting);
// actorTwo can see both target and overlay
assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSharedSetting,
@@ -975,7 +987,7 @@
public void testInitiatingApp_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -993,7 +1005,7 @@
public void testUninstalledInitiatingApp_Filters() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -1011,7 +1023,7 @@
public void testOriginatingApp_Filters() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -1036,7 +1048,7 @@
public void testInstallingApp_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -1061,7 +1073,7 @@
public void testInstrumentation_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -1090,7 +1102,7 @@
public void testWhoCanSee() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -1163,7 +1175,7 @@
public void testOnChangeReport() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
simulateAddBasicAndroid(appsFilter);
@@ -1236,7 +1248,7 @@
public void testOnChangeReportedFilter() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
- mMockExecutor);
+ mMockExecutor, mMockPmInternal);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange filter");
@@ -1292,8 +1304,15 @@
private PackageSetting simulateAddPackage(AppsFilter filter,
ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action,
@Nullable SharedUserSetting sharedUserSetting) {
- AndroidPackage newPkg = ((ParsedPackage) newPkgBuilder.hideAsParsed()).hideAsFinal();
+ final PackageSetting setting =
+ getPackageSettingFromParsingPackage(newPkgBuilder, appId, action);
+ simulateAddPackage(setting, filter, sharedUserSetting);
+ return setting;
+ }
+ private PackageSetting getPackageSettingFromParsingPackage(ParsingPackage newPkgBuilder,
+ int appId, @Nullable WithSettingBuilder action) {
+ AndroidPackage newPkg = ((ParsedPackage) newPkgBuilder.hideAsParsed()).hideAsFinal();
final PackageSettingBuilder settingBuilder = new PackageSettingBuilder()
.setPackage(newPkg)
.setAppId(appId)
@@ -1302,13 +1321,17 @@
.setPVersionCode(1L);
final PackageSetting setting =
(action == null ? settingBuilder : action.withBuilder(settingBuilder)).build();
- mExisting.put(newPkg.getPackageName(), setting);
+ return setting;
+ }
+
+ private void simulateAddPackage(PackageSetting setting, AppsFilter filter,
+ @Nullable SharedUserSetting sharedUserSetting) {
+ mExisting.put(setting.getPackageName(), setting);
if (sharedUserSetting != null) {
sharedUserSetting.addPackage(setting);
- setting.setSharedUser(sharedUserSetting);
+ setting.setSharedUserAppId(sharedUserSetting.mAppId);
}
filter.addPackage(setting);
- return setting;
}
private WithSettingBuilder withInstallSource(String initiatingPackageName,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index ed4dee1..345f22f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -722,6 +722,7 @@
Settings.updatePackageSetting(
testPkgSetting01,
null /*disabledPkg*/,
+ null /*existingSharedUserSetting*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
null /*legacyNativeLibraryPath*/,
@@ -757,6 +758,7 @@
Settings.updatePackageSetting(
testPkgSetting01,
null /*disabledPkg*/,
+ null /*existingSharedUserSetting*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
null /*legacyNativeLibraryPath*/,
@@ -792,6 +794,7 @@
Settings.updatePackageSetting(
testPkgSetting01,
null /*disabledPkg*/,
+ null /*existingSharedUserSetting*/,
testUserSetting01 /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
null /*legacyNativeLibraryPath*/,
@@ -1148,8 +1151,6 @@
assertThat(origPkgSetting.getRealName(), is(testPkgSetting.getRealName()));
assertSame(origPkgSetting.getSecondaryCpuAbi(), testPkgSetting.getSecondaryCpuAbi());
assertThat(origPkgSetting.getSecondaryCpuAbi(), is(testPkgSetting.getSecondaryCpuAbi()));
- assertSame(origPkgSetting.getSharedUser(), testPkgSetting.getSharedUser());
- assertThat(origPkgSetting.getSharedUser(), is(testPkgSetting.getSharedUser()));
assertSame(origPkgSetting.getSignatures(), testPkgSetting.getSignatures());
assertThat(origPkgSetting.getSignatures(), is(testPkgSetting.getSignatures()));
assertThat(origPkgSetting.getLastModifiedTime(), is(testPkgSetting.getLastModifiedTime()));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
index 54bfe01..9962a3c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
@@ -26,6 +26,7 @@
class ScanRequestBuilder {
private final ParsedPackage mPkg;
private AndroidPackage mOldPkg;
+ private SharedUserSetting mOldSharedUserSetting;
private SharedUserSetting mSharedUserSetting;
private PackageSetting mPkgSetting;
private PackageSetting mDisabledPkgSetting;
@@ -52,6 +53,11 @@
return this;
}
+ public ScanRequestBuilder setOldSharedUserSetting(SharedUserSetting oldSharedUserSetting) {
+ this.mOldSharedUserSetting = oldSharedUserSetting;
+ return this;
+ }
+
public ScanRequestBuilder setPkgSetting(PackageSetting pkgSetting) {
this.mPkgSetting = pkgSetting;
return this;
@@ -110,8 +116,8 @@
ScanRequest build() {
return new ScanRequest(
- mPkg, mSharedUserSetting, mOldPkg, mPkgSetting, mDisabledPkgSetting,
- mOriginalPkgSetting, mRealPkgName, mParseFlags, mScanFlags, mIsPlatformPackage,
- mUser, mCpuAbiOverride);
+ mPkg, mOldSharedUserSetting, mOldPkg, mPkgSetting, mSharedUserSetting,
+ mDisabledPkgSetting, mOriginalPkgSetting, mRealPkgName, mParseFlags, mScanFlags,
+ mIsPlatformPackage, mUser, mCpuAbiOverride);
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index a3440b4..b71323b4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.notification;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
@@ -252,6 +253,39 @@
}
@Test
+ public void testSetNotificationPermission_pkgPerm_notUserSet_grantedByDefaultPermNotSet()
+ throws Exception {
+ when(mPermManager.getPermissionFlags(anyString(),
+ eq(Manifest.permission.POST_NOTIFICATIONS),
+ anyInt())).thenReturn(FLAG_PERMISSION_GRANTED_BY_DEFAULT);
+ PermissionHelper.PackagePermission pkgPerm = new PermissionHelper.PackagePermission(
+ "pkg", 10, true, false);
+
+ mPermissionHelper.setNotificationPermission(pkgPerm);
+ verify(mPermManager, never()).revokeRuntimePermission(
+ anyString(), anyString(), anyInt(), anyString());
+ verify(mPermManager, never()).updatePermissionFlags(
+ anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt());
+ }
+
+ @Test
+ public void testSetNotificationPermission_pkgPerm_userSet_grantedByDefaultPermSet()
+ throws Exception {
+ when(mPermManager.getPermissionFlags(anyString(),
+ eq(Manifest.permission.POST_NOTIFICATIONS),
+ anyInt())).thenReturn(FLAG_PERMISSION_GRANTED_BY_DEFAULT);
+ PermissionHelper.PackagePermission pkgPerm = new PermissionHelper.PackagePermission(
+ "pkg", 10, true, true);
+
+ mPermissionHelper.setNotificationPermission(pkgPerm);
+ verify(mPermManager).grantRuntimePermission(
+ "pkg", Manifest.permission.POST_NOTIFICATIONS, 10);
+ verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_REVIEW_REQUIRED,
+ FLAG_PERMISSION_USER_SET, true, 10);
+ }
+
+ @Test
public void testSetNotificationPermission_revokeUserSet() throws Exception {
mPermissionHelper.setNotificationPermission("pkg", 10, false, true);
@@ -283,6 +317,32 @@
}
@Test
+ public void testSetNotificationPermission_SystemFixedPermNotSet() throws Exception {
+ when(mPermManager.getPermissionFlags(anyString(),
+ eq(Manifest.permission.POST_NOTIFICATIONS),
+ anyInt())).thenReturn(FLAG_PERMISSION_SYSTEM_FIXED);
+
+ mPermissionHelper.setNotificationPermission("pkg", 10, false, true);
+ verify(mPermManager, never()).revokeRuntimePermission(
+ anyString(), anyString(), anyInt(), anyString());
+ verify(mPermManager, never()).updatePermissionFlags(
+ anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt());
+ }
+
+ @Test
+ public void testSetNotificationPermission_PolicyFixedPermNotSet() throws Exception {
+ when(mPermManager.getPermissionFlags(anyString(),
+ eq(Manifest.permission.POST_NOTIFICATIONS),
+ anyInt())).thenReturn(FLAG_PERMISSION_POLICY_FIXED);
+
+ mPermissionHelper.setNotificationPermission("pkg", 10, false, true);
+ verify(mPermManager, never()).revokeRuntimePermission(
+ anyString(), anyString(), anyInt(), anyString());
+ verify(mPermManager, never()).updatePermissionFlags(
+ anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt());
+ }
+
+ @Test
public void testIsPermissionFixed() throws Exception {
when(mPermManager.getPermissionFlags(anyString(),
eq(Manifest.permission.POST_NOTIFICATIONS),
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index fc4ab22..8f1eed8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -789,6 +789,73 @@
}
@Test
+ public void testReadXml_oldXml_migration_NoUid() throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+
+ when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
+ String xml = "<ranking version=\"2\">\n"
+ + "<package name=\"something\" show_badge=\"true\">\n"
+ + "<channel id=\"idn\" name=\"name\" importance=\"2\"/>\n"
+ + "</package>\n"
+ + "</ranking>\n";
+ NotificationChannel idn = new NotificationChannel("idn", "name", IMPORTANCE_LOW);
+ idn.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+ idn.setShowBadge(false);
+
+ loadByteArrayXml(xml.getBytes(), true, USER_SYSTEM);
+ verify(mPermissionHelper, never()).setNotificationPermission(any());
+
+ when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(1234);
+ final ApplicationInfo app = new ApplicationInfo();
+ app.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
+ when(mPm.getApplicationInfoAsUser(eq("something"), anyInt(), anyInt())).thenReturn(app);
+
+ mHelper.onPackagesChanged(false, 0, new String[] {"something"}, new int[] {1234});
+
+
+ verify(mPermissionHelper, times(1)).setNotificationPermission(any());
+ }
+
+ @Test
+ public void testReadXml_newXml_noMigration_NoUid() throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+
+ when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
+ String xml = "<ranking version=\"3\">\n"
+ + "<package name=\"something\" show_badge=\"true\">\n"
+ + "<channel id=\"idn\" name=\"name\" importance=\"2\"/>\n"
+ + "</package>\n"
+ + "</ranking>\n";
+ NotificationChannel idn = new NotificationChannel("idn", "name", IMPORTANCE_LOW);
+ idn.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+ idn.setShowBadge(false);
+
+ loadByteArrayXml(xml.getBytes(), true, USER_SYSTEM);
+
+ when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(1234);
+ final ApplicationInfo app = new ApplicationInfo();
+ app.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
+ when(mPm.getApplicationInfoAsUser(eq("something"), anyInt(), anyInt())).thenReturn(app);
+
+ mHelper.onPackagesChanged(false, 0, new String[] {"something"}, new int[] {1234});
+
+
+ verify(mPermissionHelper, never()).setNotificationPermission(any());
+ }
+
+ @Test
public void testChannelXmlForNonBackup_postMigration() throws Exception {
when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 1285a84..8cbbe94 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -263,6 +263,25 @@
}
@Override
+ public String getVoiceInteractorPackageName(IBinder callingVoiceInteractor) {
+ VoiceInteractionManagerServiceImpl impl =
+ VoiceInteractionManagerService.this.mServiceStub.mImpl;
+ if (impl == null) {
+ return null;
+ }
+ VoiceInteractionSessionConnection session =
+ impl.mActiveSession;
+ if (session == null) {
+ return null;
+ }
+ IVoiceInteractor voiceInteractor = session.mInteractor;
+ if (voiceInteractor == null || voiceInteractor.asBinder() != callingVoiceInteractor) {
+ return null;
+ }
+ return session.mSessionComponentName.getPackageName();
+ }
+
+ @Override
public HotwordDetectionServiceIdentity getHotwordDetectionServiceIdentity() {
// IMPORTANT: This is called when performing permission checks; do not lock!
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 03be19f..2c39863 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3796,7 +3796,7 @@
/**
* SMDP+ server address for downloading opportunistic eSIM profile.
* FQDN (Fully Qualified Domain Name) of the SM-DP+ (e.g., smdp.gsma.com) restricted to the
- * Alphanumeric mode character set defined in table 5 of ISO/IEC 18004 [15] excluding '$'.
+ * Alphanumeric mode character set defined in table 5 of ISO/IEC 18004 excluding '$'.
*/
public static final String KEY_SMDP_SERVER_ADDRESS_STRING =
"smdp_server_address_string";
@@ -3840,6 +3840,17 @@
"opportunistic_carrier_ids_int_array";
/**
+ * Boolean configuration to control auto provisioning eSIM download in
+ * OpportunisticNetworkService using only WiFi or both WiFi/Data.
+ * True will download esim only via WiFi.
+ * False will use both WiFi and Data connection.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_ESIM_DOWNLOAD_VIA_WIFI_ONLY_BOOL =
+ "opportunistic_esim_download_via_wifi_only_bool";
+
+ /**
* Controls RSRP threshold at which OpportunisticNetworkService will decide whether
* the opportunistic network is good enough for internet data.
*/
@@ -8826,6 +8837,7 @@
sDefaults.putInt(KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT, 5);
sDefaults.putInt(KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT, 60);
sDefaults.putIntArray(KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY, new int[] {0});
+ sDefaults.putBoolean(KEY_OPPORTUNISTIC_ESIM_DOWNLOAD_VIA_WIFI_ONLY_BOOL, false);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.bp b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.bp
new file mode 100644
index 0000000..4fff969
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "frameworks_base_license",
+ ],
+}
+
+android_test {
+ name: "SmartCamera",
+ optimize: {
+ enabled: false,
+ },
+ // comment it out for now since we need use some hidden APIs
+ sdk_version: "current",
+ static_libs: ["android-ex-camera2"],
+ srcs: [
+ "src/**/*.java",
+ ],
+ jni_libs: ["libsmartcamera_jni"],
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk
deleted file mode 100644
index 6003628..0000000
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# 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.
-
-ifneq ($(TARGET_BUILD_JAVA_SUPPORT_LEVEL),)
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-# comment it out for now since we need use some hidden APIs
-LOCAL_SDK_VERSION := current
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-ex-camera2
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-renderscript-files-under, src)
-
-LOCAL_PACKAGE_NAME := SmartCamera
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../NOTICE
-LOCAL_JNI_SHARED_LIBRARIES := libsmartcamera_jni
-
-include $(BUILD_PACKAGE)
-
-# Include packages in subdirectories
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
-endif
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.bp b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.bp
new file mode 100644
index 0000000..5edb1de
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "frameworks_base_license",
+ ],
+}
+
+android_test {
+ name: "SmartCamera-tests",
+ platform_apis: true,
+ srcs: ["src/**/*.java"],
+ libs: ["android.test.base"],
+ static_libs: [
+ "guava",
+ "junit",
+ ],
+ optimize: {
+ enabled: false,
+ },
+ instrumentation_for: "SmartCamera",
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
deleted file mode 100644
index c23d593..0000000
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-# LOCAL_SDK_VERSION := current
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_PACKAGE_NAME := SmartCamera-tests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_SRC_FILES += $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := guava junit
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_INSTRUMENTATION_FOR := SmartCamera
-
-include $(BUILD_PACKAGE)
diff --git a/tests/CanvasCompare/Android.bp b/tests/CanvasCompare/Android.bp
new file mode 100644
index 0000000..9883115
--- /dev/null
+++ b/tests/CanvasCompare/Android.bp
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "frameworks_base_license",
+ ],
+}
+
+android_test {
+ name: "CanvasCompare",
+ srcs: [
+ "src/**/*.java",
+ ":CanvasCompare-rscript{CanvasCompare.srcjar}",
+ ],
+ resource_zips: [
+ ":CanvasCompare-rscript{CanvasCompare.res.zip}",
+ ],
+ platform_apis: true,
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: ["junit"],
+}
+
+genrule {
+ name: "CanvasCompare-rscript",
+ srcs: [
+ "src/**/*.rscript",
+ ":rs_script_api",
+ ":rs_clang_headers",
+ ],
+ tools: [
+ "llvm-rs-cc",
+ "soong_zip",
+ ],
+ out: [
+ "CanvasCompare.srcjar",
+ "CanvasCompare.res.zip",
+ ],
+ cmd: "for f in $(locations src/**/*.rscript); do " +
+ " $(location llvm-rs-cc) -o $(genDir)/res/raw -p $(genDir)/src " +
+ " -I $$(dirname $$(echo $(locations :rs_script_api) | awk '{ print $$1 }')) " +
+ " -I $$(dirname $$(echo $(locations :rs_clang_headers) | awk '{ print $$1 }')) $${f}; " +
+ "done && " +
+ "$(location soong_zip) -srcjar -o $(location CanvasCompare.srcjar) -C $(genDir)/src -D $(genDir)/src &&" +
+ "$(location soong_zip) -o $(location CanvasCompare.res.zip) -C $(genDir)/res -D $(genDir)/res",
+}
diff --git a/tests/CanvasCompare/Android.mk b/tests/CanvasCompare/Android.mk
deleted file mode 100644
index b82ae65..0000000
--- a/tests/CanvasCompare/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-LOCAL_PACKAGE_NAME := CanvasCompare
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-
-include $(BUILD_PACKAGE)