diff options
244 files changed, 6095 insertions, 2321 deletions
diff --git a/Android.bp b/Android.bp index 68efd59d10f7..b6483a0e3dc4 100644 --- a/Android.bp +++ b/Android.bp @@ -327,7 +327,6 @@ java_defaults { "rs/java", "sax/java", "telecomm/java", - "wifi/aidl-export", ], }, } @@ -1030,22 +1029,6 @@ aidl_interface { }, } - -subdirs = [ - "cmds/*", - "core/*", - "libs/*", - "media/*", - "proto", - "tools/*", - "native/android", - "native/graphics/jni", -] - -optional_subdirs = [ - "core/tests/utiltests/jni", -] - // TODO(b/77285514): remove this once the last few hidl interfaces have been // updated to use hwbinder.stubs. java_library { @@ -1105,13 +1088,6 @@ python_binary_host { } filegroup { - name: "framework-annotation-nonnull-srcs", - srcs: [ - "core/java/android/annotation/NonNull.java", - ], -} - -filegroup { name: "framework-media-annotation-srcs", srcs: [ ":framework-annotations", diff --git a/StubLibraries.bp b/StubLibraries.bp index 4616ced1226c..7f23df74d2da 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -49,7 +49,6 @@ stubs_defaults { ":opt-net-voip-srcs", ":core-current-stubs-source", ":core_public_api_files", - ":ike-api-srcs", ], // TODO(b/147699819): remove below aidl includes. aidl: { diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java index aae33d7b0a89..18b1108aab3a 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java @@ -858,12 +858,9 @@ public class BlobStoreManagerService extends SystemService { writeBlobsInfoAsync(); // Cleanup any stale sessions. - final ArrayList<Integer> indicesToRemove = new ArrayList<>(); for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) { final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i); - indicesToRemove.clear(); - for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) { - final BlobStoreSession blobStoreSession = userSessions.valueAt(j); + userSessions.removeIf((sessionId, blobStoreSession) -> { boolean shouldRemove = false; // Cleanup sessions which haven't been modified in a while. @@ -880,13 +877,10 @@ public class BlobStoreManagerService extends SystemService { if (shouldRemove) { blobStoreSession.getSessionFile().delete(); mActiveBlobIds.remove(blobStoreSession.getSessionId()); - indicesToRemove.add(j); deletedBlobIds.add(blobStoreSession.getSessionId()); } - } - for (int j = 0; j < indicesToRemove.size(); ++j) { - userSessions.removeAt(indicesToRemove.get(j)); - } + return shouldRemove; + }); } if (LOGV) { Slog.v(TAG, "Completed idle maintenance; deleted " diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java index b96161aba758..4c98b5fd3b56 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java @@ -77,11 +77,11 @@ public class JobParameters implements Parcelable { /** * @hide - * @deprecated use {@link #getReasonCodeDescription(int)} */ - @Deprecated - public static String getReasonName(int reason) { - switch (reason) { + // TODO(142420609): make it @SystemApi for mainline + @NonNull + public static String getReasonCodeDescription(int reasonCode) { + switch (reasonCode) { case REASON_CANCELED: return "canceled"; case REASON_CONSTRAINTS_NOT_SATISFIED: return "constraints"; case REASON_PREEMPT: return "preempt"; @@ -89,7 +89,7 @@ public class JobParameters implements Parcelable { case REASON_DEVICE_IDLE: return "device_idle"; case REASON_DEVICE_THERMAL: return "thermal"; case REASON_RESTRAINED: return "restrained"; - default: return "unknown:" + reason; + default: return "unknown:" + reasonCode; } } @@ -100,13 +100,6 @@ public class JobParameters implements Parcelable { return JOB_STOP_REASON_CODES; } - /** @hide */ - // @SystemApi TODO make it a system api for mainline - @NonNull - public static String getReasonCodeDescription(int reasonCode) { - return getReasonName(reasonCode); - } - @UnsupportedAppUsage private final int jobId; private final PersistableBundle extras; diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java index e28e5bd6c53d..d05034797f3d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java @@ -359,7 +359,8 @@ public final class JobPackageTracker { } pw.print(pe.stopReasons.valueAt(k)); pw.print("x "); - pw.print(JobParameters.getReasonName(pe.stopReasons.keyAt(k))); + pw.print(JobParameters + .getReasonCodeDescription(pe.stopReasons.keyAt(k))); } pw.println(); } @@ -606,8 +607,9 @@ public final class JobPackageTracker { if (reason != null) { pw.print(mEventReasons[index]); } else { - pw.print(JobParameters.getReasonName((mEventCmds[index] & EVENT_STOP_REASON_MASK) - >> EVENT_STOP_REASON_SHIFT)); + pw.print(JobParameters.getReasonCodeDescription( + (mEventCmds[index] & EVENT_STOP_REASON_MASK) + >> EVENT_STOP_REASON_SHIFT)); } } pw.println(); 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 ff7944d07310..c1e529f3f966 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1963,7 +1963,7 @@ public class JobSchedulerService extends com.android.server.SystemService if (restriction != null) { final int reason = restriction.getReason(); serviceContext.cancelExecutingJobLocked(reason, - "restricted due to " + JobParameters.getReasonName(reason)); + "restricted due to " + JobParameters.getReasonCodeDescription(reason)); } } } @@ -3110,7 +3110,7 @@ public class JobSchedulerService extends com.android.server.SystemService final JobRestriction restriction = mJobRestrictions.get(i); if (restriction.isJobRestricted(job)) { final int reason = restriction.getReason(); - pw.write(" " + JobParameters.getReasonName(reason) + "[" + reason + "]"); + pw.print(" " + JobParameters.getReasonCodeDescription(reason)); } } } else { 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 789f20b44ce0..b63cc1918a7a 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 @@ -39,6 +39,7 @@ import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.job.GrantedUriPermissions; @@ -97,6 +98,15 @@ public final class JobStatus { | CONSTRAINT_IDLE; /** + * Standard media URIs that contain the media files that might be important to the user. + * @see #mHasMediaBackupExemption + */ + private static final Uri[] MEDIA_URIS_FOR_STANDBY_EXEMPTION = { + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + MediaStore.Video.Media.EXTERNAL_CONTENT_URI, + }; + + /** * The constraints that we want to log to statsd. * * Constraints that can be inferred from other atoms have been excluded to avoid logging too @@ -441,13 +451,13 @@ public final class JobStatus { if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) { requiredConstraints |= CONSTRAINT_DEADLINE; } - boolean mediaOnly = false; + boolean exemptedMediaUrisOnly = false; if (job.getTriggerContentUris() != null) { requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER; - mediaOnly = true; + exemptedMediaUrisOnly = true; for (JobInfo.TriggerContentUri uri : job.getTriggerContentUris()) { - if (!MediaStore.AUTHORITY.equals(uri.getUri().getAuthority())) { - mediaOnly = false; + if (!ArrayUtils.contains(MEDIA_URIS_FOR_STANDBY_EXEMPTION, uri.getUri())) { + exemptedMediaUrisOnly = false; break; } } @@ -475,8 +485,8 @@ public final class JobStatus { job.getRequiredNetwork().networkCapabilities.setSingleUid(this.sourceUid); } final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class); - mHasMediaBackupExemption = !job.hasLateConstraint() && mediaOnly && requiresNetwork - && this.sourcePackageName.equals(jsi.getMediaBackupPackage()); + mHasMediaBackupExemption = !job.hasLateConstraint() && exemptedMediaUrisOnly + && requiresNetwork && this.sourcePackageName.equals(jsi.getMediaBackupPackage()); } /** Copy constructor: used specifically when cloning JobStatus objects for persistence, diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index bf61eb4db29a..a4ab31d7b49a 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -65,6 +65,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.content.pm.CrossProfileAppsInternal; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; @@ -283,6 +284,12 @@ public class AppStandbyController implements AppStandbyInternal { * start is the first usage of the app */ long mInitialForegroundServiceStartTimeoutMillis; + /** + * User usage that would elevate an app's standby bucket will also elevate the standby bucket of + * cross profile connected apps. Explicit standby bucket setting via + * {@link #setAppStandbyBucket(String, int, int, int, int)} will not be propagated. + */ + boolean mLinkCrossProfileApps; private volatile boolean mAppIdleEnabled; private boolean mIsCharging; @@ -445,10 +452,12 @@ public class AppStandbyController implements AppStandbyInternal { continue; } if (!packageName.equals(providerPkgName)) { + final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, + userId); synchronized (mAppIdleLock) { - reportNoninteractiveUsageLocked(packageName, userId, STANDBY_BUCKET_ACTIVE, - REASON_SUB_USAGE_SYNC_ADAPTER, elapsedRealtime, - mSyncAdapterTimeoutMillis); + reportNoninteractiveUsageCrossUserLocked(packageName, userId, + STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER, + elapsedRealtime, mSyncAdapterTimeoutMillis, linkedProfiles); } } } catch (PackageManager.NameNotFoundException e) { @@ -477,10 +486,10 @@ public class AppStandbyController implements AppStandbyInternal { } final long elapsedRealtime = mInjector.elapsedRealtime(); - + final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId); synchronized (mAppIdleLock) { - reportNoninteractiveUsageLocked(packageName, userId, bucketToPromote, - usageReason, elapsedRealtime, durationMillis); + reportNoninteractiveUsageCrossUserLocked(packageName, userId, bucketToPromote, + usageReason, elapsedRealtime, durationMillis, linkedProfiles); } } @@ -492,10 +501,11 @@ public class AppStandbyController implements AppStandbyInternal { final int currentBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime); if (currentBucket == STANDBY_BUCKET_NEVER) { + final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId); // Bring the app out of the never bucket - reportNoninteractiveUsageLocked(packageName, userId, STANDBY_BUCKET_WORKING_SET, - REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED, elapsedRealtime, - mUnexemptedSyncScheduledTimeoutMillis); + reportNoninteractiveUsageCrossUserLocked(packageName, userId, + STANDBY_BUCKET_WORKING_SET, REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED, + elapsedRealtime, mUnexemptedSyncScheduledTimeoutMillis, linkedProfiles); } } } @@ -504,14 +514,39 @@ public class AppStandbyController implements AppStandbyInternal { if (!mAppIdleEnabled) return; final long elapsedRealtime = mInjector.elapsedRealtime(); - + final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId); synchronized (mAppIdleLock) { - reportNoninteractiveUsageLocked(packageName, userId, STANDBY_BUCKET_ACTIVE, + reportNoninteractiveUsageCrossUserLocked(packageName, userId, STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_EXEMPTED_SYNC_START, elapsedRealtime, - mExemptedSyncStartTimeoutMillis); + mExemptedSyncStartTimeoutMillis, linkedProfiles); + } + } + + /** + * Helper method to report indirect user usage of an app and handle reporting the usage + * against cross profile connected apps. <br> + * Use {@link #reportNoninteractiveUsageLocked(String, int, int, int, long, long)} if + * cross profile connected apps do not need to be handled. + */ + private void reportNoninteractiveUsageCrossUserLocked(String packageName, int userId, + int bucket, int subReason, long elapsedRealtime, long nextCheckDelay, + List<UserHandle> otherProfiles) { + reportNoninteractiveUsageLocked(packageName, userId, bucket, subReason, elapsedRealtime, + nextCheckDelay); + final int size = otherProfiles.size(); + for (int profileIndex = 0; profileIndex < size; profileIndex++) { + final int otherUserId = otherProfiles.get(profileIndex).getIdentifier(); + reportNoninteractiveUsageLocked(packageName, otherUserId, bucket, subReason, + elapsedRealtime, nextCheckDelay); } } + /** + * Helper method to report indirect user usage of an app. <br> + * Use + * {@link #reportNoninteractiveUsageCrossUserLocked(String, int, int, int, long, long, List)} + * if cross profile connected apps need to be handled. + */ private void reportNoninteractiveUsageLocked(String packageName, int userId, int bucket, int subReason, long elapsedRealtime, long nextCheckDelay) { final AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId, bucket, @@ -766,8 +801,16 @@ public class AppStandbyController implements AppStandbyInternal { || eventType == UsageEvents.Event.SLICE_PINNED || eventType == UsageEvents.Event.SLICE_PINNED_PRIV || eventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) { + final String pkg = event.getPackageName(); + final List<UserHandle> linkedProfiles = getCrossProfileTargets(pkg, userId); synchronized (mAppIdleLock) { - reportEventLocked(event.getPackageName(), eventType, elapsedRealtime, userId); + reportEventLocked(pkg, eventType, elapsedRealtime, userId); + + final int size = linkedProfiles.size(); + for (int profileIndex = 0; profileIndex < size; profileIndex++) { + final int linkedUserId = linkedProfiles.get(profileIndex).getIdentifier(); + reportEventLocked(pkg, eventType, elapsedRealtime, linkedUserId); + } } } } @@ -826,6 +869,16 @@ public class AppStandbyController implements AppStandbyInternal { } } + /** + * Note: don't call this with the lock held since it makes calls to other system services. + */ + private @NonNull List<UserHandle> getCrossProfileTargets(String pkg, int userId) { + synchronized (mAppIdleLock) { + if (!mLinkCrossProfileApps) return Collections.emptyList(); + } + return mInjector.getValidCrossProfileTargets(pkg, userId); + } + private int usageEventToSubReason(int eventType) { switch (eventType) { case UsageEvents.Event.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND; @@ -1589,6 +1642,7 @@ public class AppStandbyController implements AppStandbyInternal { private PackageManagerInternal mPackageManagerInternal; private DisplayManager mDisplayManager; private PowerManager mPowerManager; + private CrossProfileAppsInternal mCrossProfileAppsInternal; int mBootPhase; /** * The minimum amount of time required since the last user interaction before an app can be @@ -1620,6 +1674,8 @@ public class AppStandbyController implements AppStandbyInternal { Context.DISPLAY_SERVICE); mPowerManager = mContext.getSystemService(PowerManager.class); mBatteryManager = mContext.getSystemService(BatteryManager.class); + mCrossProfileAppsInternal = LocalServices.getService( + CrossProfileAppsInternal.class); final ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); @@ -1727,6 +1783,17 @@ public class AppStandbyController implements AppStandbyInternal { public boolean isDeviceIdleMode() { return mPowerManager.isDeviceIdleMode(); } + + public List<UserHandle> getValidCrossProfileTargets(String pkg, int userId) { + final int uid = mPackageManagerInternal.getPackageUidInternal(pkg, 0, userId); + if (uid < 0 + || !mPackageManagerInternal.getPackage(uid).isCrossProfile() + || !mCrossProfileAppsInternal + .verifyUidHasInteractAcrossProfilePermission(pkg, uid)) { + return Collections.emptyList(); + } + return mCrossProfileAppsInternal.getTargetUserProfiles(pkg, userId); + } } class AppStandbyHandler extends Handler { @@ -1857,6 +1924,8 @@ public class AppStandbyController implements AppStandbyInternal { "initial_foreground_service_start_duration"; private static final String KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS = "auto_restricted_bucket_delay_ms"; + private static final String KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = + "cross_profile_apps_share_standby_buckets"; public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR; public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR; public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR; @@ -1868,6 +1937,7 @@ public class AppStandbyController implements AppStandbyInternal { public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE; public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE; public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS = ONE_DAY; + public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -1973,6 +2043,10 @@ public class AppStandbyController implements AppStandbyInternal { mParser.getDurationMillis(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS, COMPRESS_TIME ? ONE_MINUTE : DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS)); + + mLinkCrossProfileApps = mParser.getBoolean( + KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS, + DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS); } // Check if app_idle_enabled has changed. Do this after getting the rest of the settings diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index 7d18578aff66..1ae0c22541c2 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -375,13 +375,13 @@ public final class MediaParser { } /** - * Thrown if all extractors implementations provided to {@link #create} failed to sniff the - * input content. + * Thrown if all parser implementations provided to {@link #create} failed to sniff the input + * content. */ public static final class UnrecognizedInputFormatException extends IOException { /** - * Creates a new instance which signals that the extractors with the given names failed to + * Creates a new instance which signals that the parsers with the given names failed to * parse the input. */ @NonNull @@ -389,7 +389,7 @@ public final class MediaParser { private static UnrecognizedInputFormatException createForExtractors( @NonNull String... extractorNames) { StringBuilder builder = new StringBuilder(); - builder.append("None of the available extractors ( "); + builder.append("None of the available parsers ( "); builder.append(extractorNames[0]); for (int i = 1; i < extractorNames.length; i++) { builder.append(", "); @@ -411,10 +411,10 @@ public final class MediaParser { // Instance creation methods. /** - * Creates an instance backed by the extractor with the given {@code name}. The returned - * instance will attempt extraction without sniffing the content. + * Creates an instance backed by the parser with the given {@code name}. The returned instance + * will attempt parsing without sniffing the content. * - * @param name The name of the extractor that will be associated with the created instance. + * @param name The name of the parser that will be associated with the created instance. * @param outputConsumer The {@link OutputConsumer} to which track data and samples are pushed. * @return A new instance. * @throws IllegalArgumentException If an invalid name is provided. @@ -428,42 +428,42 @@ public final class MediaParser { } /** - * Creates an instance whose backing extractor will be selected by sniffing the content during - * the first {@link #advance} call. Extractor implementations will sniff the content in order of - * appearance in {@code extractorNames}. + * Creates an instance whose backing parser will be selected by sniffing the content during the + * first {@link #advance} call. Parser implementations will sniff the content in order of + * appearance in {@code parserNames}. * * @param outputConsumer The {@link OutputConsumer} to which extracted data is output. - * @param extractorNames The names of the extractors to sniff the content with. If empty, a - * default array of names is used. + * @param parserNames The names of the parsers to sniff the content with. If empty, a default + * array of names is used. * @return A new instance. */ @NonNull public static MediaParser create( - @NonNull OutputConsumer outputConsumer, @NonNull String... extractorNames) { - assertValidNames(extractorNames); - if (extractorNames.length == 0) { - extractorNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]); + @NonNull OutputConsumer outputConsumer, @NonNull String... parserNames) { + assertValidNames(parserNames); + if (parserNames.length == 0) { + parserNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]); } - return new MediaParser(outputConsumer, /* sniff= */ true, extractorNames); + return new MediaParser(outputConsumer, /* sniff= */ true, parserNames); } // Misc static methods. /** - * Returns an immutable list with the names of the extractors that are suitable for container + * Returns an immutable list with the names of the parsers that are suitable for container * formats with the given {@link MediaFormat}. * * <p>TODO: List which properties are taken into account. E.g. MimeType. */ @NonNull - public static List<String> getExtractorNames(@NonNull MediaFormat mediaFormat) { + public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) { throw new UnsupportedOperationException(); } // Private fields. private final OutputConsumer mOutputConsumer; - private final String[] mExtractorNamesPool; + private final String[] mParserNamesPool; private final PositionHolder mPositionHolder; private final InputReadingDataSource mDataSource; private final ExtractorInputAdapter mScratchExtractorInputAdapter; @@ -477,18 +477,18 @@ public final class MediaParser { // Public methods. /** - * Returns the name of the backing extractor implementation. + * Returns the name of the backing parser implementation. * * <p>If this instance was creating using {@link #createByName}, the provided name is returned. * If this instance was created using {@link #create}, this method will return null until the - * first call to {@link #advance}, after which the name of the backing extractor implementation - * is returned. + * first call to {@link #advance}, after which the name of the backing parser implementation is + * returned. * - * @return The name of the backing extractor implementation, or null if the backing extractor + * @return The name of the backing parser implementation, or null if the backing parser * implementation has not yet been selected. */ @Nullable - public String getExtractorName() { + public String getParserName() { return mExtractorName; } @@ -499,7 +499,7 @@ public final class MediaParser { * <p>This method will block until some progress has been made. * * <p>If this instance was created using {@link #create}. the first call to this method will - * sniff the content with the extractors with the provided names. + * sniff the content with the parsers with the provided names. * * @param seekableInputReader The {@link SeekableInputReader} from which to obtain the media * container data. @@ -520,13 +520,15 @@ public final class MediaParser { } mDataSource.mInputReader = seekableInputReader; - if (mExtractor == null) { - for (String extractorName : mExtractorNamesPool) { - Extractor extractor = - EXTRACTOR_FACTORIES_BY_NAME.get(extractorName).createInstance(); + // TODO: Apply parameters when creating extractor instances. + if (mExtractorName != null) { + mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance(); + } else if (mExtractor == null) { + for (String parserName : mParserNamesPool) { + Extractor extractor = EXTRACTOR_FACTORIES_BY_NAME.get(parserName).createInstance(); try { if (extractor.sniff(mExtractorInput)) { - mExtractorName = extractorName; + mExtractorName = parserName; mExtractor = extractor; mExtractor.init(new ExtractorOutputAdapter()); break; @@ -540,7 +542,7 @@ public final class MediaParser { } } if (mExtractor == null) { - throw UnrecognizedInputFormatException.createForExtractors(mExtractorNamesPool); + throw UnrecognizedInputFormatException.createForExtractors(mParserNamesPool); } return true; } @@ -586,22 +588,21 @@ public final class MediaParser { * Releases any acquired resources. * * <p>After calling this method, this instance becomes unusable and no other methods should be - * invoked. DESIGN NOTE: Should be removed. There shouldn't be any resource for releasing. + * invoked. */ public void release() { + // TODO: Dump media metrics here. mExtractorInput = null; mExtractor = null; } // Private methods. - private MediaParser( - OutputConsumer outputConsumer, boolean sniff, String... extractorNamesPool) { + private MediaParser(OutputConsumer outputConsumer, boolean sniff, String... parserNamesPool) { mOutputConsumer = outputConsumer; - mExtractorNamesPool = extractorNamesPool; + mParserNamesPool = parserNamesPool; if (!sniff) { - mExtractorName = extractorNamesPool[0]; - mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance(); + mExtractorName = parserNamesPool[0]; } mPositionHolder = new PositionHolder(); mDataSource = new InputReadingDataSource(); @@ -921,7 +922,7 @@ public final class MediaParser { throw new IllegalArgumentException( "Invalid extractor name: " + name - + ". Supported extractors are: " + + ". Supported parsers are: " + TextUtils.join(", ", EXTRACTOR_FACTORIES_BY_NAME.keySet()) + "."); } @@ -933,20 +934,20 @@ public final class MediaParser { static { // Using a LinkedHashMap to keep the insertion order when iterating over the keys. LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>(); - extractorFactoriesByName.put("exo.Ac3Extractor", Ac3Extractor::new); - extractorFactoriesByName.put("exo.Ac4Extractor", Ac4Extractor::new); - extractorFactoriesByName.put("exo.AdtsExtractor", AdtsExtractor::new); - extractorFactoriesByName.put("exo.AmrExtractor", AmrExtractor::new); - extractorFactoriesByName.put("exo.FlacExtractor", FlacExtractor::new); - extractorFactoriesByName.put("exo.FlvExtractor", FlvExtractor::new); - extractorFactoriesByName.put("exo.FragmentedMp4Extractor", FragmentedMp4Extractor::new); - extractorFactoriesByName.put("exo.MatroskaExtractor", MatroskaExtractor::new); - extractorFactoriesByName.put("exo.Mp3Extractor", Mp3Extractor::new); - extractorFactoriesByName.put("exo.Mp4Extractor", Mp4Extractor::new); - extractorFactoriesByName.put("exo.OggExtractor", OggExtractor::new); - extractorFactoriesByName.put("exo.PsExtractor", PsExtractor::new); - extractorFactoriesByName.put("exo.TsExtractor", TsExtractor::new); - extractorFactoriesByName.put("exo.WavExtractor", WavExtractor::new); + extractorFactoriesByName.put("exo.Ac3Parser", Ac3Extractor::new); + extractorFactoriesByName.put("exo.Ac4Parser", Ac4Extractor::new); + extractorFactoriesByName.put("exo.AdtsParser", AdtsExtractor::new); + extractorFactoriesByName.put("exo.AmrParser", AmrExtractor::new); + extractorFactoriesByName.put("exo.FlacParser", FlacExtractor::new); + extractorFactoriesByName.put("exo.FlvParser", FlvExtractor::new); + extractorFactoriesByName.put("exo.FragmentedMp4Parser", FragmentedMp4Extractor::new); + extractorFactoriesByName.put("exo.MatroskaParser", MatroskaExtractor::new); + extractorFactoriesByName.put("exo.Mp3Parser", Mp3Extractor::new); + extractorFactoriesByName.put("exo.Mp4Parser", Mp4Extractor::new); + extractorFactoriesByName.put("exo.OggParser", OggExtractor::new); + extractorFactoriesByName.put("exo.PsParser", PsExtractor::new); + extractorFactoriesByName.put("exo.TsParser", TsExtractor::new); + extractorFactoriesByName.put("exo.WavParser", WavExtractor::new); EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName); } } diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java index 6c7f82a11908..0d163cf55e4e 100644 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java +++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java @@ -40,7 +40,7 @@ public interface RuntimePermissionsPersistence { * @return the runtime permissions read */ @Nullable - RuntimePermissionsState read(@NonNull UserHandle user); + RuntimePermissionsState readAsUser(@NonNull UserHandle user); /** * Write the runtime permissions to persistence. @@ -50,7 +50,7 @@ public interface RuntimePermissionsPersistence { * @param runtimePermissions the runtime permissions to write * @param user the user to write for */ - void write(@NonNull RuntimePermissionsState runtimePermissions, @NonNull UserHandle user); + void writeAsUser(@NonNull RuntimePermissionsState runtimePermissions, @NonNull UserHandle user); /** * Delete the runtime permissions from persistence. @@ -59,7 +59,7 @@ public interface RuntimePermissionsPersistence { * * @param user the user to delete for */ - void delete(@NonNull UserHandle user); + void deleteAsUser(@NonNull UserHandle user); /** * Create a new instance of {@link RuntimePermissionsPersistence} implementation. diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java index 90b1c4b6ff5f..205ffc297945 100644 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java +++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java @@ -67,7 +67,7 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers @Nullable @Override - public RuntimePermissionsState read(@NonNull UserHandle user) { + public RuntimePermissionsState readAsUser(@NonNull UserHandle user) { File file = getFile(user); try (FileInputStream inputStream = new AtomicFile(file).openRead()) { XmlPullParser parser = Xml.newPullParser(); @@ -172,7 +172,7 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers } @Override - public void write(@NonNull RuntimePermissionsState runtimePermissions, + public void writeAsUser(@NonNull RuntimePermissionsState runtimePermissions, @NonNull UserHandle user) { File file = getFile(user); AtomicFile atomicFile = new AtomicFile(file); @@ -252,7 +252,7 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers } @Override - public void delete(@NonNull UserHandle user) { + public void deleteAsUser(@NonNull UserHandle user) { getFile(user).delete(); } diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java index 2908a3872df9..64d6545c87cf 100644 --- a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java +++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java @@ -40,7 +40,7 @@ public interface RolesPersistence { * @return the roles read */ @Nullable - RolesState read(@NonNull UserHandle user); + RolesState readAsUser(@NonNull UserHandle user); /** * Write the roles to persistence. @@ -50,7 +50,7 @@ public interface RolesPersistence { * @param roles the roles to write * @param user the user to write for */ - void write(@NonNull RolesState roles, @NonNull UserHandle user); + void writeAsUser(@NonNull RolesState roles, @NonNull UserHandle user); /** * Delete the roles from persistence. @@ -59,7 +59,7 @@ public interface RolesPersistence { * * @param user the user to delete for */ - void delete(@NonNull UserHandle user); + void deleteAsUser(@NonNull UserHandle user); /** * Create a new instance of {@link RolesPersistence} implementation. diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java index 06fad77c495c..3031c8213982 100644 --- a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java +++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java @@ -65,7 +65,7 @@ public class RolesPersistenceImpl implements RolesPersistence { @Nullable @Override - public RolesState read(@NonNull UserHandle user) { + public RolesState readAsUser(@NonNull UserHandle user) { File file = getFile(user); try (FileInputStream inputStream = new AtomicFile(file).openRead()) { XmlPullParser parser = Xml.newPullParser(); @@ -146,8 +146,7 @@ public class RolesPersistenceImpl implements RolesPersistence { } @Override - public void write(@NonNull RolesState roles, - @NonNull UserHandle user) { + public void writeAsUser(@NonNull RolesState roles, @NonNull UserHandle user) { File file = getFile(user); AtomicFile atomicFile = new AtomicFile(file); FileOutputStream outputStream = null; @@ -206,7 +205,7 @@ public class RolesPersistenceImpl implements RolesPersistence { } @Override - public void delete(@NonNull UserHandle user) { + public void deleteAsUser(@NonNull UserHandle user) { getFile(user).delete(); } diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp index 0e9311034ee0..2f3e2acb5d7a 100644 --- a/apex/statsd/Android.bp +++ b/apex/statsd/Android.bp @@ -21,14 +21,16 @@ apex { apex_defaults { native_shared_libs: [ "libstats_jni", + "libstatspull", + "libstatssocket", ], - // binaries: ["vold"], + binaries: ["statsd"], java_libs: [ "framework-statsd", "service-statsd", ], compile_multilib: "both", - // prebuilts: ["my_prebuilt"], + prebuilts: ["com.android.os.statsd.init.rc"], name: "com.android.os.statsd-defaults", key: "com.android.os.statsd.key", certificate: ":com.android.os.statsd.certificate", @@ -47,6 +49,12 @@ android_app_certificate { certificate: "com.android.os.statsd", } +prebuilt_etc { + name: "com.android.os.statsd.init.rc", + src: "statsd.rc", + filename: "init.rc", + installable: false, +} // JNI library for StatsLog.write cc_library_shared { diff --git a/cmds/statsd/statsd.rc b/apex/statsd/statsd.rc index a98ecd586b42..605da2af0c19 100644 --- a/cmds/statsd/statsd.rc +++ b/apex/statsd/statsd.rc @@ -12,19 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -service statsd /system/bin/statsd +service statsd /apex/com.android.os.statsd/bin/statsd class main socket statsdw dgram+passcred 0222 statsd statsd user statsd group statsd log writepid /dev/cpuset/system-background/tasks - -on property:ro.statsd.enable=false - stop statsd - -on post-fs-data - # Create directory for statsd - mkdir /data/misc/stats-data/ 0770 statsd system - mkdir /data/misc/stats-service/ 0770 statsd system - mkdir /data/misc/stats-active-metric/ 0770 statsd system - mkdir /data/misc/train-info/ 0770 statsd system diff --git a/api/current.txt b/api/current.txt index c6bb79f5bf26..c69dbff53aca 100644 --- a/api/current.txt +++ b/api/current.txt @@ -32,6 +32,7 @@ package android { field public static final String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES"; field @Deprecated public static final String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; field public static final String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE"; + field public static final String BIND_CONTROLS = "android.permission.BIND_CONTROLS"; field public static final String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN"; field public static final String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE"; field public static final String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE"; @@ -6969,7 +6970,6 @@ package android.app.admin { method public boolean removeOverrideApn(@NonNull android.content.ComponentName, int); method public boolean removeUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle); method public boolean requestBugreport(@NonNull android.content.ComponentName); - method public void requestSetLocationProviderAllowed(@NonNull android.content.ComponentName, @NonNull String, boolean); method @Deprecated public boolean resetPassword(String, int); method public boolean resetPasswordWithToken(@NonNull android.content.ComponentName, String, byte[], int); method @Nullable public java.util.List<android.app.admin.NetworkEvent> retrieveNetworkLogs(@Nullable android.content.ComponentName, long); @@ -12328,6 +12328,7 @@ package android.content.pm { method public int describeContents(); method public void dump(android.util.Printer, String); method public final int getIconResource(); + method public boolean isCrossProfileIntentForwarderActivity(); method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager); method public CharSequence loadLabel(android.content.pm.PackageManager); method public void writeToParcel(android.os.Parcel, int); @@ -12466,6 +12467,7 @@ package android.content.pm { method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(int); method public boolean isRateLimitingActive(); method public boolean isRequestPinShortcutSupported(); + method public void pushDynamicShortcut(@NonNull android.content.pm.ShortcutInfo); method public void removeAllDynamicShortcuts(); method public void removeDynamicShortcuts(@NonNull java.util.List<java.lang.String>); method public void removeLongLivedShortcuts(@NonNull java.util.List<java.lang.String>); @@ -24786,7 +24788,7 @@ package android.media { } public abstract class DrmInitData { - method public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID); + method @Deprecated public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID); method @NonNull public android.media.DrmInitData.SchemeInitData getSchemeInitDataAt(int); method public int getSchemeInitDataCount(); } @@ -25305,6 +25307,10 @@ package android.media { method public void recycle(); } + public class MediaCodec.IncompatibleWithBlockModelException extends java.lang.RuntimeException { + ctor public MediaCodec.IncompatibleWithBlockModelException(); + } + public static final class MediaCodec.LinearBlock { method protected void finalize(); method public static boolean isCodecCopyFreeCompatible(@NonNull String[]); @@ -25332,23 +25338,24 @@ package android.media { } public static final class MediaCodec.OutputFrame { - method public void getChangedKeys(@NonNull java.util.Set<java.lang.String>); method public int getFlags(); method @NonNull public android.media.MediaFormat getFormat(); method @Nullable public android.media.MediaCodec.GraphicBlock getGraphicBlock(); method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock(); method public long getPresentationTimeUs(); + method public void retrieveChangedKeys(@NonNull java.util.Set<java.lang.String>); } public final class MediaCodec.QueueRequest { method public void queue(); method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer); - method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int); + method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int); method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float); - method @NonNull public android.media.MediaCodec.QueueRequest setGraphicBlock(@NonNull android.media.MediaCodec.GraphicBlock, long, int); + method @NonNull public android.media.MediaCodec.QueueRequest setGraphicBlock(@NonNull android.media.MediaCodec.GraphicBlock); method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int); - method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, long, int); + method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @Nullable android.media.MediaCodec.CryptoInfo); method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long); + method @NonNull public android.media.MediaCodec.QueueRequest setPresentationTimeUs(long); method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String); } @@ -26402,8 +26409,8 @@ package android.media { method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException, java.lang.InterruptedException; method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...); method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer); - method @Nullable public String getExtractorName(); - method @NonNull public static java.util.List<java.lang.String> getExtractorNames(@NonNull android.media.MediaFormat); + method @Nullable public String getParserName(); + method @NonNull public static java.util.List<java.lang.String> getParserNames(@NonNull android.media.MediaFormat); method public void release(); method public void seek(@NonNull android.media.MediaParser.SeekPoint); } @@ -43399,6 +43406,7 @@ package android.service.controls { method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherFor(@NonNull java.util.List<java.lang.String>); method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForAllAvailable(); method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForSuggested(); + method public static void requestAddControl(@NonNull android.content.Context, @NonNull android.content.ComponentName, @NonNull android.service.controls.Control); field public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService"; field @NonNull public static final String TAG = "ControlsProviderService"; } @@ -45645,7 +45653,7 @@ package android.telecom { method public final android.telecom.CallAudioState getCallAudioState(); method public final String getCallerDisplayName(); method public final int getCallerDisplayNamePresentation(); - method public int getCallerNumberVerificationStatus(); + method public final int getCallerNumberVerificationStatus(); method public final android.telecom.Conference getConference(); method public final java.util.List<android.telecom.Conferenceable> getConferenceables(); method public final int getConnectionCapabilities(); @@ -45698,7 +45706,7 @@ package android.telecom { method public final void setAudioModeIsVoip(boolean); method public final void setAudioRoute(int); method public final void setCallerDisplayName(String, int); - method public void setCallerNumberVerificationStatus(int); + method public final void setCallerNumberVerificationStatus(int); method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>); method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>); method public final void setConnectionCapabilities(int); @@ -47065,6 +47073,350 @@ package android.telephony { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ClosedSubscriberGroupInfo> CREATOR; } + public final class DataFailCause { + field public static final int ACCESS_ATTEMPT_ALREADY_IN_PROGRESS = 2219; // 0x8ab + field public static final int ACCESS_BLOCK = 2087; // 0x827 + field public static final int ACCESS_BLOCK_ALL = 2088; // 0x828 + field public static final int ACCESS_CLASS_DSAC_REJECTION = 2108; // 0x83c + field public static final int ACCESS_CONTROL_LIST_CHECK_FAILURE = 2128; // 0x850 + field public static final int ACTIVATION_REJECTED_BCM_VIOLATION = 48; // 0x30 + field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e + field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f + field public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 65; // 0x41 + field public static final int APN_DISABLED = 2045; // 0x7fd + field public static final int APN_DISALLOWED_ON_ROAMING = 2059; // 0x80b + field public static final int APN_MISMATCH = 2054; // 0x806 + field public static final int APN_PARAMETERS_CHANGED = 2060; // 0x80c + field public static final int APN_PENDING_HANDOVER = 2041; // 0x7f9 + field public static final int APN_TYPE_CONFLICT = 112; // 0x70 + field public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 122; // 0x7a + field public static final int BEARER_HANDLING_NOT_SUPPORTED = 60; // 0x3c + field public static final int CALL_DISALLOWED_IN_ROAMING = 2068; // 0x814 + field public static final int CALL_PREEMPT_BY_EMERGENCY_APN = 127; // 0x7f + field public static final int CANNOT_ENCODE_OTA_MESSAGE = 2159; // 0x86f + field public static final int CDMA_ALERT_STOP = 2077; // 0x81d + field public static final int CDMA_INCOMING_CALL = 2076; // 0x81c + field public static final int CDMA_INTERCEPT = 2073; // 0x819 + field public static final int CDMA_LOCK = 2072; // 0x818 + field public static final int CDMA_RELEASE_DUE_TO_SO_REJECTION = 2075; // 0x81b + field public static final int CDMA_REORDER = 2074; // 0x81a + field public static final int CDMA_RETRY_ORDER = 2086; // 0x826 + field public static final int CHANNEL_ACQUISITION_FAILURE = 2078; // 0x81e + field public static final int CLOSE_IN_PROGRESS = 2030; // 0x7ee + field public static final int COLLISION_WITH_NETWORK_INITIATED_REQUEST = 56; // 0x38 + field public static final int COMPANION_IFACE_IN_USE = 118; // 0x76 + field public static final int CONCURRENT_SERVICES_INCOMPATIBLE = 2083; // 0x823 + field public static final int CONCURRENT_SERVICES_NOT_ALLOWED = 2091; // 0x82b + field public static final int CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION = 2080; // 0x820 + field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64 + field public static final int CONGESTION = 2106; // 0x83a + field public static final int CONNECTION_RELEASED = 2113; // 0x841 + field public static final int CS_DOMAIN_NOT_AVAILABLE = 2181; // 0x885 + field public static final int CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED = 2188; // 0x88c + field public static final int DATA_PLAN_EXPIRED = 2198; // 0x896 + field public static final int DATA_ROAMING_SETTINGS_DISABLED = 2064; // 0x810 + field public static final int DATA_SETTINGS_DISABLED = 2063; // 0x80f + field public static final int DBM_OR_SMS_IN_PROGRESS = 2211; // 0x8a3 + field public static final int DDS_SWITCHED = 2065; // 0x811 + field public static final int DDS_SWITCH_IN_PROGRESS = 2067; // 0x813 + field public static final int DRB_RELEASED_BY_RRC = 2112; // 0x840 + field public static final int DS_EXPLICIT_DEACTIVATION = 2125; // 0x84d + field public static final int DUAL_SWITCH = 2227; // 0x8b3 + field public static final int DUN_CALL_DISALLOWED = 2056; // 0x808 + field public static final int DUPLICATE_BEARER_ID = 2118; // 0x846 + field public static final int EHRPD_TO_HRPD_FALLBACK = 2049; // 0x801 + field public static final int EMBMS_NOT_ENABLED = 2193; // 0x891 + field public static final int EMBMS_REGULAR_DEACTIVATION = 2195; // 0x893 + field public static final int EMERGENCY_IFACE_ONLY = 116; // 0x74 + field public static final int EMERGENCY_MODE = 2221; // 0x8ad + field public static final int EMM_ACCESS_BARRED = 115; // 0x73 + field public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 121; // 0x79 + field public static final int EMM_ATTACH_FAILED = 2115; // 0x843 + field public static final int EMM_ATTACH_STARTED = 2116; // 0x844 + field public static final int EMM_DETACHED = 2114; // 0x842 + field public static final int EMM_T3417_EXPIRED = 2130; // 0x852 + field public static final int EMM_T3417_EXT_EXPIRED = 2131; // 0x853 + field public static final int EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED = 2178; // 0x882 + field public static final int EPS_SERVICES_NOT_ALLOWED_IN_PLMN = 2179; // 0x883 + field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff + field public static final int ESM_BAD_OTA_MESSAGE = 2122; // 0x84a + field public static final int ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK = 2120; // 0x848 + field public static final int ESM_COLLISION_SCENARIOS = 2119; // 0x847 + field public static final int ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT = 2124; // 0x84c + field public static final int ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL = 2123; // 0x84b + field public static final int ESM_FAILURE = 2182; // 0x886 + field public static final int ESM_INFO_NOT_RECEIVED = 53; // 0x35 + field public static final int ESM_LOCAL_CAUSE_NONE = 2126; // 0x84e + field public static final int ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER = 2121; // 0x849 + field public static final int ESM_PROCEDURE_TIME_OUT = 2155; // 0x86b + field public static final int ESM_UNKNOWN_EPS_BEARER_CONTEXT = 2111; // 0x83f + field public static final int EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE = 2201; // 0x899 + field public static final int EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY = 2200; // 0x898 + field public static final int EVDO_HDR_CHANGED = 2202; // 0x89a + field public static final int EVDO_HDR_CONNECTION_SETUP_TIMEOUT = 2206; // 0x89e + field public static final int EVDO_HDR_EXITED = 2203; // 0x89b + field public static final int EVDO_HDR_NO_SESSION = 2204; // 0x89c + field public static final int EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL = 2205; // 0x89d + field public static final int FADE = 2217; // 0x8a9 + field public static final int FAILED_TO_ACQUIRE_COLOCATED_HDR = 2207; // 0x89f + field public static final int FEATURE_NOT_SUPP = 40; // 0x28 + field public static final int FILTER_SEMANTIC_ERROR = 44; // 0x2c + field public static final int FILTER_SYTAX_ERROR = 45; // 0x2d + field public static final int FORBIDDEN_APN_NAME = 2066; // 0x812 + field public static final int GPRS_REGISTRATION_FAIL = -2; // 0xfffffffe + field public static final int GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED = 2097; // 0x831 + field public static final int GPRS_SERVICES_NOT_ALLOWED = 2098; // 0x832 + field public static final int GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN = 2103; // 0x837 + field public static final int HANDOFF_PREFERENCE_CHANGED = 2251; // 0x8cb + field public static final int HDR_ACCESS_FAILURE = 2213; // 0x8a5 + field public static final int HDR_FADE = 2212; // 0x8a4 + field public static final int HDR_NO_LOCK_GRANTED = 2210; // 0x8a2 + field public static final int IFACE_AND_POL_FAMILY_MISMATCH = 120; // 0x78 + field public static final int IFACE_MISMATCH = 117; // 0x75 + field public static final int ILLEGAL_ME = 2096; // 0x830 + field public static final int ILLEGAL_MS = 2095; // 0x82f + field public static final int IMEI_NOT_ACCEPTED = 2177; // 0x881 + field public static final int IMPLICITLY_DETACHED = 2100; // 0x834 + field public static final int IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER = 2176; // 0x880 + field public static final int INCOMING_CALL_REJECTED = 2092; // 0x82c + field public static final int INSUFFICIENT_RESOURCES = 26; // 0x1a + field public static final int INTERFACE_IN_USE = 2058; // 0x80a + field public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 114; // 0x72 + field public static final int INTERNAL_EPC_NONEPC_TRANSITION = 2057; // 0x809 + field public static final int INVALID_CONNECTION_ID = 2156; // 0x86c + field public static final int INVALID_DNS_ADDR = 123; // 0x7b + field public static final int INVALID_EMM_STATE = 2190; // 0x88e + field public static final int INVALID_MANDATORY_INFO = 96; // 0x60 + field public static final int INVALID_MODE = 2223; // 0x8af + field public static final int INVALID_PCSCF_ADDR = 113; // 0x71 + field public static final int INVALID_PCSCF_OR_DNS_ADDRESS = 124; // 0x7c + field public static final int INVALID_PRIMARY_NSAPI = 2158; // 0x86e + field public static final int INVALID_SIM_STATE = 2224; // 0x8b0 + field public static final int INVALID_TRANSACTION_ID = 81; // 0x51 + field public static final int IPV6_ADDRESS_TRANSFER_FAILED = 2047; // 0x7ff + field public static final int IPV6_PREFIX_UNAVAILABLE = 2250; // 0x8ca + field public static final int IP_ADDRESS_MISMATCH = 119; // 0x77 + field public static final int IP_VERSION_MISMATCH = 2055; // 0x807 + field public static final int IRAT_HANDOVER_FAILED = 2194; // 0x892 + field public static final int IS707B_MAX_ACCESS_PROBES = 2089; // 0x829 + field public static final int LIMITED_TO_IPV4 = 2234; // 0x8ba + field public static final int LIMITED_TO_IPV6 = 2235; // 0x8bb + field public static final int LLC_SNDCP = 25; // 0x19 + field public static final int LOCAL_END = 2215; // 0x8a7 + field public static final int LOCATION_AREA_NOT_ALLOWED = 2102; // 0x836 + field public static final int LOST_CONNECTION = 65540; // 0x10004 + field public static final int LOWER_LAYER_REGISTRATION_FAILURE = 2197; // 0x895 + field public static final int LOW_POWER_MODE_OR_POWERING_DOWN = 2044; // 0x7fc + field public static final int LTE_NAS_SERVICE_REQUEST_FAILED = 2117; // 0x845 + field public static final int LTE_THROTTLING_NOT_REQUIRED = 2127; // 0x84f + field public static final int MAC_FAILURE = 2183; // 0x887 + field public static final int MAXIMIUM_NSAPIS_EXCEEDED = 2157; // 0x86d + field public static final int MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED = 2166; // 0x876 + field public static final int MAX_ACCESS_PROBE = 2079; // 0x81f + field public static final int MAX_IPV4_CONNECTIONS = 2052; // 0x804 + field public static final int MAX_IPV6_CONNECTIONS = 2053; // 0x805 + field public static final int MAX_PPP_INACTIVITY_TIMER_EXPIRED = 2046; // 0x7fe + field public static final int MESSAGE_INCORRECT_SEMANTIC = 95; // 0x5f + field public static final int MESSAGE_TYPE_UNSUPPORTED = 97; // 0x61 + field public static final int MIP_CONFIG_FAILURE = 2050; // 0x802 + field public static final int MIP_FA_ADMIN_PROHIBITED = 2001; // 0x7d1 + field public static final int MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED = 2012; // 0x7dc + field public static final int MIP_FA_ENCAPSULATION_UNAVAILABLE = 2008; // 0x7d8 + field public static final int MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE = 2004; // 0x7d4 + field public static final int MIP_FA_INSUFFICIENT_RESOURCES = 2002; // 0x7d2 + field public static final int MIP_FA_MALFORMED_REPLY = 2007; // 0x7d7 + field public static final int MIP_FA_MALFORMED_REQUEST = 2006; // 0x7d6 + field public static final int MIP_FA_MISSING_CHALLENGE = 2017; // 0x7e1 + field public static final int MIP_FA_MISSING_HOME_ADDRESS = 2015; // 0x7df + field public static final int MIP_FA_MISSING_HOME_AGENT = 2014; // 0x7de + field public static final int MIP_FA_MISSING_NAI = 2013; // 0x7dd + field public static final int MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE = 2003; // 0x7d3 + field public static final int MIP_FA_REASON_UNSPECIFIED = 2000; // 0x7d0 + field public static final int MIP_FA_REQUESTED_LIFETIME_TOO_LONG = 2005; // 0x7d5 + field public static final int MIP_FA_REVERSE_TUNNEL_IS_MANDATORY = 2011; // 0x7db + field public static final int MIP_FA_REVERSE_TUNNEL_UNAVAILABLE = 2010; // 0x7da + field public static final int MIP_FA_STALE_CHALLENGE = 2018; // 0x7e2 + field public static final int MIP_FA_UNKNOWN_CHALLENGE = 2016; // 0x7e0 + field public static final int MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE = 2009; // 0x7d9 + field public static final int MIP_HA_ADMIN_PROHIBITED = 2020; // 0x7e4 + field public static final int MIP_HA_ENCAPSULATION_UNAVAILABLE = 2029; // 0x7ed + field public static final int MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE = 2023; // 0x7e7 + field public static final int MIP_HA_INSUFFICIENT_RESOURCES = 2021; // 0x7e5 + field public static final int MIP_HA_MALFORMED_REQUEST = 2025; // 0x7e9 + field public static final int MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE = 2022; // 0x7e6 + field public static final int MIP_HA_REASON_UNSPECIFIED = 2019; // 0x7e3 + field public static final int MIP_HA_REGISTRATION_ID_MISMATCH = 2024; // 0x7e8 + field public static final int MIP_HA_REVERSE_TUNNEL_IS_MANDATORY = 2028; // 0x7ec + field public static final int MIP_HA_REVERSE_TUNNEL_UNAVAILABLE = 2027; // 0x7eb + field public static final int MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS = 2026; // 0x7ea + field public static final int MISSING_UNKNOWN_APN = 27; // 0x1b + field public static final int MODEM_APP_PREEMPTED = 2032; // 0x7f0 + field public static final int MODEM_RESTART = 2037; // 0x7f5 + field public static final int MSC_TEMPORARILY_NOT_REACHABLE = 2180; // 0x884 + field public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 101; // 0x65 + field public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 98; // 0x62 + field public static final int MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK = 2099; // 0x833 + field public static final int MULTIPLE_PDP_CALL_NOT_ALLOWED = 2192; // 0x890 + field public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 55; // 0x37 + field public static final int NAS_LAYER_FAILURE = 2191; // 0x88f + field public static final int NAS_REQUEST_REJECTED_BY_NETWORK = 2167; // 0x877 + field public static final int NAS_SIGNALLING = 14; // 0xe + field public static final int NETWORK_FAILURE = 38; // 0x26 + field public static final int NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH = 2154; // 0x86a + field public static final int NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH = 2153; // 0x869 + field public static final int NETWORK_INITIATED_TERMINATION = 2031; // 0x7ef + field public static final int NONE = 0; // 0x0 + field public static final int NON_IP_NOT_SUPPORTED = 2069; // 0x815 + field public static final int NORMAL_RELEASE = 2218; // 0x8aa + field public static final int NO_CDMA_SERVICE = 2084; // 0x824 + field public static final int NO_COLLOCATED_HDR = 2225; // 0x8b1 + field public static final int NO_EPS_BEARER_CONTEXT_ACTIVATED = 2189; // 0x88d + field public static final int NO_GPRS_CONTEXT = 2094; // 0x82e + field public static final int NO_HYBRID_HDR_SERVICE = 2209; // 0x8a1 + field public static final int NO_PDP_CONTEXT_ACTIVATED = 2107; // 0x83b + field public static final int NO_RESPONSE_FROM_BASE_STATION = 2081; // 0x821 + field public static final int NO_SERVICE = 2216; // 0x8a8 + field public static final int NO_SERVICE_ON_GATEWAY = 2093; // 0x82d + field public static final int NSAPI_IN_USE = 35; // 0x23 + field public static final int NULL_APN_DISALLOWED = 2061; // 0x80d + field public static final int OEM_DCFAILCAUSE_1 = 4097; // 0x1001 + field public static final int OEM_DCFAILCAUSE_10 = 4106; // 0x100a + field public static final int OEM_DCFAILCAUSE_11 = 4107; // 0x100b + field public static final int OEM_DCFAILCAUSE_12 = 4108; // 0x100c + field public static final int OEM_DCFAILCAUSE_13 = 4109; // 0x100d + field public static final int OEM_DCFAILCAUSE_14 = 4110; // 0x100e + field public static final int OEM_DCFAILCAUSE_15 = 4111; // 0x100f + field public static final int OEM_DCFAILCAUSE_2 = 4098; // 0x1002 + field public static final int OEM_DCFAILCAUSE_3 = 4099; // 0x1003 + field public static final int OEM_DCFAILCAUSE_4 = 4100; // 0x1004 + field public static final int OEM_DCFAILCAUSE_5 = 4101; // 0x1005 + field public static final int OEM_DCFAILCAUSE_6 = 4102; // 0x1006 + field public static final int OEM_DCFAILCAUSE_7 = 4103; // 0x1007 + field public static final int OEM_DCFAILCAUSE_8 = 4104; // 0x1008 + field public static final int OEM_DCFAILCAUSE_9 = 4105; // 0x1009 + field public static final int ONLY_IPV4V6_ALLOWED = 57; // 0x39 + field public static final int ONLY_IPV4_ALLOWED = 50; // 0x32 + field public static final int ONLY_IPV6_ALLOWED = 51; // 0x33 + field public static final int ONLY_NON_IP_ALLOWED = 58; // 0x3a + field public static final int ONLY_SINGLE_BEARER_ALLOWED = 52; // 0x34 + field public static final int OPERATOR_BARRED = 8; // 0x8 + field public static final int OTASP_COMMIT_IN_PROGRESS = 2208; // 0x8a0 + field public static final int PDN_CONN_DOES_NOT_EXIST = 54; // 0x36 + field public static final int PDN_INACTIVITY_TIMER_EXPIRED = 2051; // 0x803 + field public static final int PDN_IPV4_CALL_DISALLOWED = 2033; // 0x7f1 + field public static final int PDN_IPV4_CALL_THROTTLED = 2034; // 0x7f2 + field public static final int PDN_IPV6_CALL_DISALLOWED = 2035; // 0x7f3 + field public static final int PDN_IPV6_CALL_THROTTLED = 2036; // 0x7f4 + field public static final int PDN_NON_IP_CALL_DISALLOWED = 2071; // 0x817 + field public static final int PDN_NON_IP_CALL_THROTTLED = 2070; // 0x816 + field public static final int PDP_ACTIVATE_MAX_RETRY_FAILED = 2109; // 0x83d + field public static final int PDP_DUPLICATE = 2104; // 0x838 + field public static final int PDP_ESTABLISH_TIMEOUT_EXPIRED = 2161; // 0x871 + field public static final int PDP_INACTIVE_TIMEOUT_EXPIRED = 2163; // 0x873 + field public static final int PDP_LOWERLAYER_ERROR = 2164; // 0x874 + field public static final int PDP_MODIFY_COLLISION = 2165; // 0x875 + field public static final int PDP_MODIFY_TIMEOUT_EXPIRED = 2162; // 0x872 + field public static final int PDP_PPP_NOT_SUPPORTED = 2038; // 0x7f6 + field public static final int PDP_WITHOUT_ACTIVE_TFT = 46; // 0x2e + field public static final int PHONE_IN_USE = 2222; // 0x8ae + field public static final int PHYSICAL_LINK_CLOSE_IN_PROGRESS = 2040; // 0x7f8 + field public static final int PLMN_NOT_ALLOWED = 2101; // 0x835 + field public static final int PPP_AUTH_FAILURE = 2229; // 0x8b5 + field public static final int PPP_CHAP_FAILURE = 2232; // 0x8b8 + field public static final int PPP_CLOSE_IN_PROGRESS = 2233; // 0x8b9 + field public static final int PPP_OPTION_MISMATCH = 2230; // 0x8b6 + field public static final int PPP_PAP_FAILURE = 2231; // 0x8b7 + field public static final int PPP_TIMEOUT = 2228; // 0x8b4 + field public static final int PREF_RADIO_TECH_CHANGED = -4; // 0xfffffffc + field public static final int PROFILE_BEARER_INCOMPATIBLE = 2042; // 0x7fa + field public static final int PROTOCOL_ERRORS = 111; // 0x6f + field public static final int QOS_NOT_ACCEPTED = 37; // 0x25 + field public static final int RADIO_ACCESS_BEARER_FAILURE = 2110; // 0x83e + field public static final int RADIO_ACCESS_BEARER_SETUP_FAILURE = 2160; // 0x870 + field public static final int RADIO_NOT_AVAILABLE = 65537; // 0x10001 + field public static final int RADIO_POWER_OFF = -5; // 0xfffffffb + field public static final int REDIRECTION_OR_HANDOFF_IN_PROGRESS = 2220; // 0x8ac + field public static final int REGISTRATION_FAIL = -1; // 0xffffffff + field public static final int REGULAR_DEACTIVATION = 36; // 0x24 + field public static final int REJECTED_BY_BASE_STATION = 2082; // 0x822 + field public static final int RRC_CONNECTION_ABORTED_AFTER_HANDOVER = 2173; // 0x87d + field public static final int RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE = 2174; // 0x87e + field public static final int RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE = 2171; // 0x87b + field public static final int RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE = 2175; // 0x87f + field public static final int RRC_CONNECTION_ABORT_REQUEST = 2151; // 0x867 + field public static final int RRC_CONNECTION_ACCESS_BARRED = 2139; // 0x85b + field public static final int RRC_CONNECTION_ACCESS_STRATUM_FAILURE = 2137; // 0x859 + field public static final int RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS = 2138; // 0x85a + field public static final int RRC_CONNECTION_CELL_NOT_CAMPED = 2144; // 0x860 + field public static final int RRC_CONNECTION_CELL_RESELECTION = 2140; // 0x85c + field public static final int RRC_CONNECTION_CONFIG_FAILURE = 2141; // 0x85d + field public static final int RRC_CONNECTION_INVALID_REQUEST = 2168; // 0x878 + field public static final int RRC_CONNECTION_LINK_FAILURE = 2143; // 0x85f + field public static final int RRC_CONNECTION_NORMAL_RELEASE = 2147; // 0x863 + field public static final int RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER = 2150; // 0x866 + field public static final int RRC_CONNECTION_RADIO_LINK_FAILURE = 2148; // 0x864 + field public static final int RRC_CONNECTION_REESTABLISHMENT_FAILURE = 2149; // 0x865 + field public static final int RRC_CONNECTION_REJECT_BY_NETWORK = 2146; // 0x862 + field public static final int RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE = 2172; // 0x87c + field public static final int RRC_CONNECTION_RF_UNAVAILABLE = 2170; // 0x87a + field public static final int RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR = 2152; // 0x868 + field public static final int RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE = 2145; // 0x861 + field public static final int RRC_CONNECTION_TIMER_EXPIRED = 2142; // 0x85e + field public static final int RRC_CONNECTION_TRACKING_AREA_ID_CHANGED = 2169; // 0x879 + field public static final int RRC_UPLINK_CONNECTION_RELEASE = 2134; // 0x856 + field public static final int RRC_UPLINK_DATA_TRANSMISSION_FAILURE = 2132; // 0x854 + field public static final int RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER = 2133; // 0x855 + field public static final int RRC_UPLINK_ERROR_REQUEST_FROM_NAS = 2136; // 0x858 + field public static final int RRC_UPLINK_RADIO_LINK_FAILURE = 2135; // 0x857 + field public static final int RUIM_NOT_PRESENT = 2085; // 0x825 + field public static final int SECURITY_MODE_REJECTED = 2186; // 0x88a + field public static final int SERVICE_NOT_ALLOWED_ON_PLMN = 2129; // 0x851 + field public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 33; // 0x21 + field public static final int SERVICE_OPTION_NOT_SUPPORTED = 32; // 0x20 + field public static final int SERVICE_OPTION_OUT_OF_ORDER = 34; // 0x22 + field public static final int SIGNAL_LOST = -3; // 0xfffffffd + field public static final int SIM_CARD_CHANGED = 2043; // 0x7fb + field public static final int SYNCHRONIZATION_FAILURE = 2184; // 0x888 + field public static final int TEST_LOOPBACK_REGULAR_DEACTIVATION = 2196; // 0x894 + field public static final int TETHERED_CALL_ACTIVE = -6; // 0xfffffffa + field public static final int TFT_SEMANTIC_ERROR = 41; // 0x29 + field public static final int TFT_SYTAX_ERROR = 42; // 0x2a + field public static final int THERMAL_EMERGENCY = 2090; // 0x82a + field public static final int THERMAL_MITIGATION = 2062; // 0x80e + field public static final int TRAT_SWAP_FAILED = 2048; // 0x800 + field public static final int UE_INITIATED_DETACH_OR_DISCONNECT = 128; // 0x80 + field public static final int UE_IS_ENTERING_POWERSAVE_MODE = 2226; // 0x8b2 + field public static final int UE_RAT_CHANGE = 2105; // 0x839 + field public static final int UE_SECURITY_CAPABILITIES_MISMATCH = 2185; // 0x889 + field public static final int UMTS_HANDOVER_TO_IWLAN = 2199; // 0x897 + field public static final int UMTS_REACTIVATION_REQ = 39; // 0x27 + field public static final int UNACCEPTABLE_NETWORK_PARAMETER = 65538; // 0x10002 + field public static final int UNACCEPTABLE_NON_EPS_AUTHENTICATION = 2187; // 0x88b + field public static final int UNKNOWN = 65536; // 0x10000 + field public static final int UNKNOWN_INFO_ELEMENT = 99; // 0x63 + field public static final int UNKNOWN_PDP_ADDRESS_TYPE = 28; // 0x1c + field public static final int UNKNOWN_PDP_CONTEXT = 43; // 0x2b + field public static final int UNPREFERRED_RAT = 2039; // 0x7f7 + field public static final int UNSUPPORTED_1X_PREV = 2214; // 0x8a6 + field public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 66; // 0x42 + field public static final int UNSUPPORTED_QCI_VALUE = 59; // 0x3b + field public static final int USER_AUTHENTICATION = 29; // 0x1d + field public static final int VSNCP_ADMINISTRATIVELY_PROHIBITED = 2245; // 0x8c5 + field public static final int VSNCP_APN_UNAUTHORIZED = 2238; // 0x8be + field public static final int VSNCP_GEN_ERROR = 2237; // 0x8bd + field public static final int VSNCP_INSUFFICIENT_PARAMETERS = 2243; // 0x8c3 + field public static final int VSNCP_NO_PDN_GATEWAY_ADDRESS = 2240; // 0x8c0 + field public static final int VSNCP_PDN_EXISTS_FOR_THIS_APN = 2248; // 0x8c8 + field public static final int VSNCP_PDN_GATEWAY_REJECT = 2242; // 0x8c2 + field public static final int VSNCP_PDN_GATEWAY_UNREACHABLE = 2241; // 0x8c1 + field public static final int VSNCP_PDN_ID_IN_USE = 2246; // 0x8c6 + field public static final int VSNCP_PDN_LIMIT_EXCEEDED = 2239; // 0x8bf + field public static final int VSNCP_RECONNECT_NOT_ALLOWED = 2249; // 0x8c9 + field public static final int VSNCP_RESOURCE_UNAVAILABLE = 2244; // 0x8c4 + field public static final int VSNCP_SUBSCRIBER_LIMITATION = 2247; // 0x8c7 + field public static final int VSNCP_TIMEOUT = 2236; // 0x8bc + } + public final class DisplayInfo implements android.os.Parcelable { method public int describeContents(); method public int getNetworkType(); diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 7b66f735b5ad..6863221f7fda 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -30,7 +30,6 @@ package android.net { } public class TetheringConstants { - ctor public TetheringConstants(); field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; diff --git a/api/module-lib-lint-baseline.txt b/api/module-lib-lint-baseline.txt index 6e59596cb05f..56f7a02260a7 100644 --- a/api/module-lib-lint-baseline.txt +++ b/api/module-lib-lint-baseline.txt @@ -27,7 +27,3 @@ PrivateSuperclass: android.location.GnssAntennaInfo.PhaseCenterVariationCorrecti Public class android.location.GnssAntennaInfo.PhaseCenterVariationCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections PrivateSuperclass: android.location.GnssAntennaInfo.SignalGainCorrections: Public class android.location.GnssAntennaInfo.SignalGainCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections - - -StaticUtils: android.net.TetheringConstants: - Fully-static utility classes must not have constructor diff --git a/api/system-current.txt b/api/system-current.txt index 3ef785700eee..9663c0c53a39 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -6747,337 +6747,6 @@ package android.net.apf { } -package android.net.eap { - - public final class EapSessionConfig { - } - - public static final class EapSessionConfig.Builder { - ctor public EapSessionConfig.Builder(); - method @NonNull public android.net.eap.EapSessionConfig build(); - method @NonNull public android.net.eap.EapSessionConfig.Builder setEapAkaConfig(int, int); - method @NonNull public android.net.eap.EapSessionConfig.Builder setEapAkaPrimeConfig(int, int, @NonNull String, boolean); - method @NonNull public android.net.eap.EapSessionConfig.Builder setEapIdentity(@NonNull byte[]); - method @NonNull public android.net.eap.EapSessionConfig.Builder setEapMsChapV2Config(@NonNull String, @NonNull String); - method @NonNull public android.net.eap.EapSessionConfig.Builder setEapSimConfig(int, int); - } - - public static class EapSessionConfig.EapAkaConfig extends android.net.eap.EapSessionConfig.EapUiccConfig { - } - - public static class EapSessionConfig.EapAkaPrimeConfig extends android.net.eap.EapSessionConfig.EapAkaConfig { - method public boolean allowsMismatchedNetworkNames(); - method @NonNull public String getNetworkName(); - } - - public abstract static class EapSessionConfig.EapMethodConfig { - method public int getMethodType(); - } - - public static class EapSessionConfig.EapMsChapV2Config extends android.net.eap.EapSessionConfig.EapMethodConfig { - method @NonNull public String getPassword(); - method @NonNull public String getUsername(); - } - - public static class EapSessionConfig.EapSimConfig extends android.net.eap.EapSessionConfig.EapUiccConfig { - } - - public abstract static class EapSessionConfig.EapUiccConfig extends android.net.eap.EapSessionConfig.EapMethodConfig { - method public int getAppType(); - method public int getSubId(); - } - -} - -package android.net.ipsec.ike { - - public final class ChildSaProposal extends android.net.ipsec.ike.SaProposal { - } - - public static final class ChildSaProposal.Builder { - ctor public ChildSaProposal.Builder(); - method @NonNull public android.net.ipsec.ike.ChildSaProposal.Builder addDhGroup(int); - method @NonNull public android.net.ipsec.ike.ChildSaProposal.Builder addEncryptionAlgorithm(int, int); - method @NonNull public android.net.ipsec.ike.ChildSaProposal.Builder addIntegrityAlgorithm(int); - method @NonNull public android.net.ipsec.ike.ChildSaProposal build(); - } - - public interface ChildSessionCallback { - method public void onClosed(); - method public void onClosedExceptionally(@NonNull android.net.ipsec.ike.exceptions.IkeException); - method public void onIpSecTransformCreated(@NonNull android.net.IpSecTransform, int); - method public void onIpSecTransformDeleted(@NonNull android.net.IpSecTransform, int); - method public void onOpened(@NonNull android.net.ipsec.ike.ChildSessionConfiguration); - } - - public final class ChildSessionConfiguration { - method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getInboundTrafficSelectors(); - method @NonNull public java.util.List<android.net.LinkAddress> getInternalAddresses(); - method @NonNull public java.util.List<java.net.InetAddress> getInternalDhcpServers(); - method @NonNull public java.util.List<java.net.InetAddress> getInternalDnsServers(); - method @NonNull public java.util.List<android.net.IpPrefix> getInternalSubnets(); - method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getOutboundTrafficSelectors(); - } - - public abstract class ChildSessionParams { - method public long getHardLifetime(); - method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getLocalTrafficSelectors(); - method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getRemoteTrafficSelectors(); - method @NonNull public java.util.List<android.net.ipsec.ike.ChildSaProposal> getSaProposals(); - method public long getSoftLifetime(); - } - - public class IkeFqdnIdentification extends android.net.ipsec.ike.IkeIdentification { - ctor public IkeFqdnIdentification(@NonNull String); - field @NonNull public final String fqdn; - } - - public abstract class IkeIdentification { - } - - public final class IkeIpv4AddrIdentification extends android.net.ipsec.ike.IkeIdentification { - ctor public IkeIpv4AddrIdentification(@NonNull java.net.Inet4Address); - field @NonNull public final java.net.Inet4Address ipv4Address; - } - - public class IkeIpv6AddrIdentification extends android.net.ipsec.ike.IkeIdentification { - ctor public IkeIpv6AddrIdentification(@NonNull java.net.Inet6Address); - field @NonNull public final java.net.Inet6Address ipv6Address; - } - - public final class IkeKeyIdIdentification extends android.net.ipsec.ike.IkeIdentification { - ctor public IkeKeyIdIdentification(@NonNull byte[]); - field @NonNull public final byte[] keyId; - } - - public final class IkeRfc822AddrIdentification extends android.net.ipsec.ike.IkeIdentification { - ctor public IkeRfc822AddrIdentification(@NonNull String); - field @NonNull public final String rfc822Name; - } - - public final class IkeSaProposal extends android.net.ipsec.ike.SaProposal { - method @NonNull public java.util.List<java.lang.Integer> getPseudorandomFunctions(); - } - - public static final class IkeSaProposal.Builder { - ctor public IkeSaProposal.Builder(); - method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addDhGroup(int); - method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addEncryptionAlgorithm(int, int); - method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addIntegrityAlgorithm(int); - method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addPseudorandomFunction(int); - method @NonNull public android.net.ipsec.ike.IkeSaProposal build(); - } - - public final class IkeSession implements java.lang.AutoCloseable { - ctor public IkeSession(@NonNull android.content.Context, @NonNull android.net.ipsec.ike.IkeSessionParams, @NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull java.util.concurrent.Executor, @NonNull android.net.ipsec.ike.IkeSessionCallback, @NonNull android.net.ipsec.ike.ChildSessionCallback); - method public void close(); - method public void closeChildSession(@NonNull android.net.ipsec.ike.ChildSessionCallback); - method public void kill(); - method public void openChildSession(@NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull android.net.ipsec.ike.ChildSessionCallback); - } - - public interface IkeSessionCallback { - method public void onClosed(); - method public void onClosedExceptionally(@NonNull android.net.ipsec.ike.exceptions.IkeException); - method public void onError(@NonNull android.net.ipsec.ike.exceptions.IkeProtocolException); - method public void onOpened(@NonNull android.net.ipsec.ike.IkeSessionConfiguration); - } - - public final class IkeSessionConfiguration { - method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers(); - method @NonNull public String getRemoteApplicationVersion(); - method @NonNull public java.util.List<byte[]> getRemoteVendorIDs(); - method public boolean isIkeExtensionEnabled(int); - field public static final int EXTENSION_TYPE_FRAGMENTATION = 1; // 0x1 - field public static final int EXTENSION_TYPE_MOBIKE = 2; // 0x2 - } - - public final class IkeSessionParams { - method @NonNull public java.util.List<android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest> getConfigurationRequests(); - method public long getHardLifetime(); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getLocalAuthConfig(); - method @NonNull public android.net.ipsec.ike.IkeIdentification getLocalIdentification(); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getRemoteAuthConfig(); - method @NonNull public android.net.ipsec.ike.IkeIdentification getRemoteIdentification(); - method @NonNull public java.util.List<android.net.ipsec.ike.IkeSaProposal> getSaProposals(); - method @NonNull public java.net.InetAddress getServerAddress(); - method public long getSoftLifetime(); - method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket getUdpEncapsulationSocket(); - } - - public static final class IkeSessionParams.Builder { - ctor public IkeSessionParams.Builder(); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(@NonNull java.net.InetAddress); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(int); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal); - method @NonNull public android.net.ipsec.ike.IkeSessionParams build(); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.util.List<java.security.cert.X509Certificate>, @NonNull java.security.PrivateKey); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthEap(@Nullable java.security.cert.X509Certificate, @NonNull android.net.eap.EapSessionConfig); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthPsk(@NonNull byte[]); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setLifetime(long, long); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setLocalIdentification(@NonNull android.net.ipsec.ike.IkeIdentification); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setRemoteIdentification(@NonNull android.net.ipsec.ike.IkeIdentification); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setServerAddress(@NonNull java.net.InetAddress); - method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket); - } - - public static interface IkeSessionParams.ConfigRequestIpv4PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest { - method @Nullable public java.net.Inet4Address getAddress(); - } - - public static interface IkeSessionParams.ConfigRequestIpv6PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest { - method @Nullable public java.net.Inet6Address getAddress(); - } - - public abstract static class IkeSessionParams.IkeAuthConfig { - } - - public static class IkeSessionParams.IkeAuthDigitalSignLocalConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig { - method @NonNull public java.security.cert.X509Certificate getClientEndCertificate(); - method @NonNull public java.util.List<java.security.cert.X509Certificate> getIntermediateCertificates(); - method @NonNull public java.security.PrivateKey getPrivateKey(); - } - - public static class IkeSessionParams.IkeAuthDigitalSignRemoteConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig { - method @Nullable public java.security.cert.X509Certificate getRemoteCaCert(); - } - - public static class IkeSessionParams.IkeAuthEapConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig { - method @NonNull public android.net.eap.EapSessionConfig getEapConfig(); - } - - public static class IkeSessionParams.IkeAuthPskConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig { - method @NonNull public byte[] getPsk(); - } - - public static interface IkeSessionParams.IkeConfigRequest { - } - - public final class IkeTrafficSelector { - ctor public IkeTrafficSelector(int, int, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress); - field public final int endPort; - field @NonNull public final java.net.InetAddress endingAddress; - field public final int startPort; - field @NonNull public final java.net.InetAddress startingAddress; - } - - public abstract class SaProposal { - method @NonNull public java.util.List<java.lang.Integer> getDhGroups(); - method @NonNull public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getEncryptionAlgorithms(); - method @NonNull public java.util.List<java.lang.Integer> getIntegrityAlgorithms(); - field public static final int DH_GROUP_1024_BIT_MODP = 2; // 0x2 - field public static final int DH_GROUP_2048_BIT_MODP = 14; // 0xe - field public static final int DH_GROUP_NONE = 0; // 0x0 - field public static final int ENCRYPTION_ALGORITHM_3DES = 3; // 0x3 - field public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; // 0xc - field public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19; // 0x13 - field public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20; // 0x14 - field public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18; // 0x12 - field public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; // 0x5 - field public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; // 0x2 - field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; // 0xc - field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; // 0xd - field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; // 0xe - field public static final int INTEGRITY_ALGORITHM_NONE = 0; // 0x0 - field public static final int KEY_LEN_AES_128 = 128; // 0x80 - field public static final int KEY_LEN_AES_192 = 192; // 0xc0 - field public static final int KEY_LEN_AES_256 = 256; // 0x100 - field public static final int KEY_LEN_UNUSED = 0; // 0x0 - field public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; // 0x4 - field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2 - } - - public final class TransportModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams { - } - - public static final class TransportModeChildSessionParams.Builder { - ctor public TransportModeChildSessionParams.Builder(); - method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); - method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); - method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal); - method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams build(); - method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder setLifetime(long, long); - } - - public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams { - method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest> getConfigurationRequests(); - } - - public static final class TunnelModeChildSessionParams.Builder { - ctor public TunnelModeChildSessionParams.Builder(); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(int); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(@NonNull java.net.Inet4Address); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(@NonNull java.net.Inet6Address, int); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDhcpServerRequest(int); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDnsServerRequest(int); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams build(); - method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder setLifetime(long, long); - } - - public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest { - method @Nullable public java.net.Inet4Address getAddress(); - } - - public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest { - method @Nullable public java.net.Inet4Address getAddress(); - } - - public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest { - method @Nullable public java.net.Inet4Address getAddress(); - } - - public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest { - } - - public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest { - method @Nullable public java.net.Inet6Address getAddress(); - method public int getPrefixLength(); - } - - public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest { - method @Nullable public java.net.Inet6Address getAddress(); - } - - public static interface TunnelModeChildSessionParams.TunnelModeChildConfigRequest { - } - -} - -package android.net.ipsec.ike.exceptions { - - public abstract class IkeException extends java.lang.Exception { - } - - public final class IkeInternalException extends android.net.ipsec.ike.exceptions.IkeException { - } - - public abstract class IkeProtocolException extends android.net.ipsec.ike.exceptions.IkeException { - method @Nullable public byte[] getErrorData(); - method public int getErrorType(); - field public static final int ERROR_TYPE_AUTHENTICATION_FAILED = 24; // 0x18 - field public static final int ERROR_TYPE_CHILD_SA_NOT_FOUND = 44; // 0x2c - field public static final int ERROR_TYPE_FAILED_CP_REQUIRED = 37; // 0x25 - field public static final int ERROR_TYPE_INTERNAL_ADDRESS_FAILURE = 36; // 0x24 - field public static final int ERROR_TYPE_INVALID_IKE_SPI = 4; // 0x4 - field public static final int ERROR_TYPE_INVALID_KE_PAYLOAD = 17; // 0x11 - field public static final int ERROR_TYPE_INVALID_MAJOR_VERSION = 5; // 0x5 - field public static final int ERROR_TYPE_INVALID_MESSAGE_ID = 9; // 0x9 - field public static final int ERROR_TYPE_INVALID_SELECTORS = 39; // 0x27 - field public static final int ERROR_TYPE_INVALID_SYNTAX = 7; // 0x7 - field public static final int ERROR_TYPE_NO_ADDITIONAL_SAS = 35; // 0x23 - field public static final int ERROR_TYPE_NO_PROPOSAL_CHOSEN = 14; // 0xe - field public static final int ERROR_TYPE_SINGLE_PAIR_REQUIRED = 34; // 0x22 - field public static final int ERROR_TYPE_TEMPORARY_FAILURE = 43; // 0x2b - field public static final int ERROR_TYPE_TS_UNACCEPTABLE = 38; // 0x26 - field public static final int ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD = 1; // 0x1 - } - -} - package android.net.metrics { public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event { @@ -11400,347 +11069,7 @@ package android.telephony { } public final class DataFailCause { - field public static final int ACCESS_ATTEMPT_ALREADY_IN_PROGRESS = 2219; // 0x8ab - field public static final int ACCESS_BLOCK = 2087; // 0x827 - field public static final int ACCESS_BLOCK_ALL = 2088; // 0x828 - field public static final int ACCESS_CLASS_DSAC_REJECTION = 2108; // 0x83c - field public static final int ACCESS_CONTROL_LIST_CHECK_FAILURE = 2128; // 0x850 - field public static final int ACTIVATION_REJECTED_BCM_VIOLATION = 48; // 0x30 - field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e - field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f - field public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 65; // 0x41 - field public static final int APN_DISABLED = 2045; // 0x7fd - field public static final int APN_DISALLOWED_ON_ROAMING = 2059; // 0x80b - field public static final int APN_MISMATCH = 2054; // 0x806 - field public static final int APN_PARAMETERS_CHANGED = 2060; // 0x80c - field public static final int APN_PENDING_HANDOVER = 2041; // 0x7f9 - field public static final int APN_TYPE_CONFLICT = 112; // 0x70 - field public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 122; // 0x7a - field public static final int BEARER_HANDLING_NOT_SUPPORTED = 60; // 0x3c - field public static final int CALL_DISALLOWED_IN_ROAMING = 2068; // 0x814 - field public static final int CALL_PREEMPT_BY_EMERGENCY_APN = 127; // 0x7f - field public static final int CANNOT_ENCODE_OTA_MESSAGE = 2159; // 0x86f - field public static final int CDMA_ALERT_STOP = 2077; // 0x81d - field public static final int CDMA_INCOMING_CALL = 2076; // 0x81c - field public static final int CDMA_INTERCEPT = 2073; // 0x819 - field public static final int CDMA_LOCK = 2072; // 0x818 - field public static final int CDMA_RELEASE_DUE_TO_SO_REJECTION = 2075; // 0x81b - field public static final int CDMA_REORDER = 2074; // 0x81a - field public static final int CDMA_RETRY_ORDER = 2086; // 0x826 - field public static final int CHANNEL_ACQUISITION_FAILURE = 2078; // 0x81e - field public static final int CLOSE_IN_PROGRESS = 2030; // 0x7ee - field public static final int COLLISION_WITH_NETWORK_INITIATED_REQUEST = 56; // 0x38 - field public static final int COMPANION_IFACE_IN_USE = 118; // 0x76 - field public static final int CONCURRENT_SERVICES_INCOMPATIBLE = 2083; // 0x823 - field public static final int CONCURRENT_SERVICES_NOT_ALLOWED = 2091; // 0x82b - field public static final int CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION = 2080; // 0x820 - field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64 - field public static final int CONGESTION = 2106; // 0x83a - field public static final int CONNECTION_RELEASED = 2113; // 0x841 - field public static final int CS_DOMAIN_NOT_AVAILABLE = 2181; // 0x885 - field public static final int CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED = 2188; // 0x88c - field public static final int DATA_PLAN_EXPIRED = 2198; // 0x896 - field public static final int DATA_ROAMING_SETTINGS_DISABLED = 2064; // 0x810 - field public static final int DATA_SETTINGS_DISABLED = 2063; // 0x80f - field public static final int DBM_OR_SMS_IN_PROGRESS = 2211; // 0x8a3 - field public static final int DDS_SWITCHED = 2065; // 0x811 - field public static final int DDS_SWITCH_IN_PROGRESS = 2067; // 0x813 - field public static final int DRB_RELEASED_BY_RRC = 2112; // 0x840 - field public static final int DS_EXPLICIT_DEACTIVATION = 2125; // 0x84d - field public static final int DUAL_SWITCH = 2227; // 0x8b3 - field public static final int DUN_CALL_DISALLOWED = 2056; // 0x808 - field public static final int DUPLICATE_BEARER_ID = 2118; // 0x846 - field public static final int EHRPD_TO_HRPD_FALLBACK = 2049; // 0x801 - field public static final int EMBMS_NOT_ENABLED = 2193; // 0x891 - field public static final int EMBMS_REGULAR_DEACTIVATION = 2195; // 0x893 - field public static final int EMERGENCY_IFACE_ONLY = 116; // 0x74 - field public static final int EMERGENCY_MODE = 2221; // 0x8ad - field public static final int EMM_ACCESS_BARRED = 115; // 0x73 - field public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 121; // 0x79 - field public static final int EMM_ATTACH_FAILED = 2115; // 0x843 - field public static final int EMM_ATTACH_STARTED = 2116; // 0x844 - field public static final int EMM_DETACHED = 2114; // 0x842 - field public static final int EMM_T3417_EXPIRED = 2130; // 0x852 - field public static final int EMM_T3417_EXT_EXPIRED = 2131; // 0x853 - field public static final int EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED = 2178; // 0x882 - field public static final int EPS_SERVICES_NOT_ALLOWED_IN_PLMN = 2179; // 0x883 - field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff - field public static final int ESM_BAD_OTA_MESSAGE = 2122; // 0x84a - field public static final int ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK = 2120; // 0x848 - field public static final int ESM_COLLISION_SCENARIOS = 2119; // 0x847 - field public static final int ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT = 2124; // 0x84c - field public static final int ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL = 2123; // 0x84b - field public static final int ESM_FAILURE = 2182; // 0x886 - field public static final int ESM_INFO_NOT_RECEIVED = 53; // 0x35 - field public static final int ESM_LOCAL_CAUSE_NONE = 2126; // 0x84e - field public static final int ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER = 2121; // 0x849 - field public static final int ESM_PROCEDURE_TIME_OUT = 2155; // 0x86b - field public static final int ESM_UNKNOWN_EPS_BEARER_CONTEXT = 2111; // 0x83f - field public static final int EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE = 2201; // 0x899 - field public static final int EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY = 2200; // 0x898 - field public static final int EVDO_HDR_CHANGED = 2202; // 0x89a - field public static final int EVDO_HDR_CONNECTION_SETUP_TIMEOUT = 2206; // 0x89e - field public static final int EVDO_HDR_EXITED = 2203; // 0x89b - field public static final int EVDO_HDR_NO_SESSION = 2204; // 0x89c - field public static final int EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL = 2205; // 0x89d - field public static final int FADE = 2217; // 0x8a9 - field public static final int FAILED_TO_ACQUIRE_COLOCATED_HDR = 2207; // 0x89f - field public static final int FEATURE_NOT_SUPP = 40; // 0x28 - field public static final int FILTER_SEMANTIC_ERROR = 44; // 0x2c - field public static final int FILTER_SYTAX_ERROR = 45; // 0x2d - field public static final int FORBIDDEN_APN_NAME = 2066; // 0x812 - field public static final int GPRS_REGISTRATION_FAIL = -2; // 0xfffffffe - field public static final int GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED = 2097; // 0x831 - field public static final int GPRS_SERVICES_NOT_ALLOWED = 2098; // 0x832 - field public static final int GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN = 2103; // 0x837 - field public static final int HANDOFF_PREFERENCE_CHANGED = 2251; // 0x8cb - field public static final int HDR_ACCESS_FAILURE = 2213; // 0x8a5 - field public static final int HDR_FADE = 2212; // 0x8a4 - field public static final int HDR_NO_LOCK_GRANTED = 2210; // 0x8a2 - field public static final int IFACE_AND_POL_FAMILY_MISMATCH = 120; // 0x78 - field public static final int IFACE_MISMATCH = 117; // 0x75 - field public static final int ILLEGAL_ME = 2096; // 0x830 - field public static final int ILLEGAL_MS = 2095; // 0x82f - field public static final int IMEI_NOT_ACCEPTED = 2177; // 0x881 - field public static final int IMPLICITLY_DETACHED = 2100; // 0x834 - field public static final int IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER = 2176; // 0x880 - field public static final int INCOMING_CALL_REJECTED = 2092; // 0x82c - field public static final int INSUFFICIENT_RESOURCES = 26; // 0x1a - field public static final int INTERFACE_IN_USE = 2058; // 0x80a - field public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 114; // 0x72 - field public static final int INTERNAL_EPC_NONEPC_TRANSITION = 2057; // 0x809 - field public static final int INVALID_CONNECTION_ID = 2156; // 0x86c - field public static final int INVALID_DNS_ADDR = 123; // 0x7b - field public static final int INVALID_EMM_STATE = 2190; // 0x88e - field public static final int INVALID_MANDATORY_INFO = 96; // 0x60 - field public static final int INVALID_MODE = 2223; // 0x8af - field public static final int INVALID_PCSCF_ADDR = 113; // 0x71 - field public static final int INVALID_PCSCF_OR_DNS_ADDRESS = 124; // 0x7c - field public static final int INVALID_PRIMARY_NSAPI = 2158; // 0x86e - field public static final int INVALID_SIM_STATE = 2224; // 0x8b0 - field public static final int INVALID_TRANSACTION_ID = 81; // 0x51 - field public static final int IPV6_ADDRESS_TRANSFER_FAILED = 2047; // 0x7ff - field public static final int IPV6_PREFIX_UNAVAILABLE = 2250; // 0x8ca - field public static final int IP_ADDRESS_MISMATCH = 119; // 0x77 - field public static final int IP_VERSION_MISMATCH = 2055; // 0x807 - field public static final int IRAT_HANDOVER_FAILED = 2194; // 0x892 - field public static final int IS707B_MAX_ACCESS_PROBES = 2089; // 0x829 - field public static final int LIMITED_TO_IPV4 = 2234; // 0x8ba - field public static final int LIMITED_TO_IPV6 = 2235; // 0x8bb - field public static final int LLC_SNDCP = 25; // 0x19 - field public static final int LOCAL_END = 2215; // 0x8a7 - field public static final int LOCATION_AREA_NOT_ALLOWED = 2102; // 0x836 - field public static final int LOST_CONNECTION = 65540; // 0x10004 - field public static final int LOWER_LAYER_REGISTRATION_FAILURE = 2197; // 0x895 - field public static final int LOW_POWER_MODE_OR_POWERING_DOWN = 2044; // 0x7fc - field public static final int LTE_NAS_SERVICE_REQUEST_FAILED = 2117; // 0x845 - field public static final int LTE_THROTTLING_NOT_REQUIRED = 2127; // 0x84f - field public static final int MAC_FAILURE = 2183; // 0x887 - field public static final int MAXIMIUM_NSAPIS_EXCEEDED = 2157; // 0x86d - field public static final int MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED = 2166; // 0x876 - field public static final int MAX_ACCESS_PROBE = 2079; // 0x81f - field public static final int MAX_IPV4_CONNECTIONS = 2052; // 0x804 - field public static final int MAX_IPV6_CONNECTIONS = 2053; // 0x805 - field public static final int MAX_PPP_INACTIVITY_TIMER_EXPIRED = 2046; // 0x7fe - field public static final int MESSAGE_INCORRECT_SEMANTIC = 95; // 0x5f - field public static final int MESSAGE_TYPE_UNSUPPORTED = 97; // 0x61 - field public static final int MIP_CONFIG_FAILURE = 2050; // 0x802 - field public static final int MIP_FA_ADMIN_PROHIBITED = 2001; // 0x7d1 - field public static final int MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED = 2012; // 0x7dc - field public static final int MIP_FA_ENCAPSULATION_UNAVAILABLE = 2008; // 0x7d8 - field public static final int MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE = 2004; // 0x7d4 - field public static final int MIP_FA_INSUFFICIENT_RESOURCES = 2002; // 0x7d2 - field public static final int MIP_FA_MALFORMED_REPLY = 2007; // 0x7d7 - field public static final int MIP_FA_MALFORMED_REQUEST = 2006; // 0x7d6 - field public static final int MIP_FA_MISSING_CHALLENGE = 2017; // 0x7e1 - field public static final int MIP_FA_MISSING_HOME_ADDRESS = 2015; // 0x7df - field public static final int MIP_FA_MISSING_HOME_AGENT = 2014; // 0x7de - field public static final int MIP_FA_MISSING_NAI = 2013; // 0x7dd - field public static final int MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE = 2003; // 0x7d3 - field public static final int MIP_FA_REASON_UNSPECIFIED = 2000; // 0x7d0 - field public static final int MIP_FA_REQUESTED_LIFETIME_TOO_LONG = 2005; // 0x7d5 - field public static final int MIP_FA_REVERSE_TUNNEL_IS_MANDATORY = 2011; // 0x7db - field public static final int MIP_FA_REVERSE_TUNNEL_UNAVAILABLE = 2010; // 0x7da - field public static final int MIP_FA_STALE_CHALLENGE = 2018; // 0x7e2 - field public static final int MIP_FA_UNKNOWN_CHALLENGE = 2016; // 0x7e0 - field public static final int MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE = 2009; // 0x7d9 - field public static final int MIP_HA_ADMIN_PROHIBITED = 2020; // 0x7e4 - field public static final int MIP_HA_ENCAPSULATION_UNAVAILABLE = 2029; // 0x7ed - field public static final int MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE = 2023; // 0x7e7 - field public static final int MIP_HA_INSUFFICIENT_RESOURCES = 2021; // 0x7e5 - field public static final int MIP_HA_MALFORMED_REQUEST = 2025; // 0x7e9 - field public static final int MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE = 2022; // 0x7e6 - field public static final int MIP_HA_REASON_UNSPECIFIED = 2019; // 0x7e3 - field public static final int MIP_HA_REGISTRATION_ID_MISMATCH = 2024; // 0x7e8 - field public static final int MIP_HA_REVERSE_TUNNEL_IS_MANDATORY = 2028; // 0x7ec - field public static final int MIP_HA_REVERSE_TUNNEL_UNAVAILABLE = 2027; // 0x7eb - field public static final int MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS = 2026; // 0x7ea - field public static final int MISSING_UNKNOWN_APN = 27; // 0x1b - field public static final int MODEM_APP_PREEMPTED = 2032; // 0x7f0 - field public static final int MODEM_RESTART = 2037; // 0x7f5 - field public static final int MSC_TEMPORARILY_NOT_REACHABLE = 2180; // 0x884 - field public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 101; // 0x65 - field public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 98; // 0x62 - field public static final int MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK = 2099; // 0x833 - field public static final int MULTIPLE_PDP_CALL_NOT_ALLOWED = 2192; // 0x890 - field public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 55; // 0x37 - field public static final int NAS_LAYER_FAILURE = 2191; // 0x88f - field public static final int NAS_REQUEST_REJECTED_BY_NETWORK = 2167; // 0x877 - field public static final int NAS_SIGNALLING = 14; // 0xe - field public static final int NETWORK_FAILURE = 38; // 0x26 - field public static final int NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH = 2154; // 0x86a - field public static final int NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH = 2153; // 0x869 - field public static final int NETWORK_INITIATED_TERMINATION = 2031; // 0x7ef - field public static final int NONE = 0; // 0x0 - field public static final int NON_IP_NOT_SUPPORTED = 2069; // 0x815 - field public static final int NORMAL_RELEASE = 2218; // 0x8aa - field public static final int NO_CDMA_SERVICE = 2084; // 0x824 - field public static final int NO_COLLOCATED_HDR = 2225; // 0x8b1 - field public static final int NO_EPS_BEARER_CONTEXT_ACTIVATED = 2189; // 0x88d - field public static final int NO_GPRS_CONTEXT = 2094; // 0x82e - field public static final int NO_HYBRID_HDR_SERVICE = 2209; // 0x8a1 - field public static final int NO_PDP_CONTEXT_ACTIVATED = 2107; // 0x83b - field public static final int NO_RESPONSE_FROM_BASE_STATION = 2081; // 0x821 - field public static final int NO_SERVICE = 2216; // 0x8a8 - field public static final int NO_SERVICE_ON_GATEWAY = 2093; // 0x82d - field public static final int NSAPI_IN_USE = 35; // 0x23 - field public static final int NULL_APN_DISALLOWED = 2061; // 0x80d - field public static final int OEM_DCFAILCAUSE_1 = 4097; // 0x1001 - field public static final int OEM_DCFAILCAUSE_10 = 4106; // 0x100a - field public static final int OEM_DCFAILCAUSE_11 = 4107; // 0x100b - field public static final int OEM_DCFAILCAUSE_12 = 4108; // 0x100c - field public static final int OEM_DCFAILCAUSE_13 = 4109; // 0x100d - field public static final int OEM_DCFAILCAUSE_14 = 4110; // 0x100e - field public static final int OEM_DCFAILCAUSE_15 = 4111; // 0x100f - field public static final int OEM_DCFAILCAUSE_2 = 4098; // 0x1002 - field public static final int OEM_DCFAILCAUSE_3 = 4099; // 0x1003 - field public static final int OEM_DCFAILCAUSE_4 = 4100; // 0x1004 - field public static final int OEM_DCFAILCAUSE_5 = 4101; // 0x1005 - field public static final int OEM_DCFAILCAUSE_6 = 4102; // 0x1006 - field public static final int OEM_DCFAILCAUSE_7 = 4103; // 0x1007 - field public static final int OEM_DCFAILCAUSE_8 = 4104; // 0x1008 - field public static final int OEM_DCFAILCAUSE_9 = 4105; // 0x1009 - field public static final int ONLY_IPV4V6_ALLOWED = 57; // 0x39 - field public static final int ONLY_IPV4_ALLOWED = 50; // 0x32 - field public static final int ONLY_IPV6_ALLOWED = 51; // 0x33 - field public static final int ONLY_NON_IP_ALLOWED = 58; // 0x3a - field public static final int ONLY_SINGLE_BEARER_ALLOWED = 52; // 0x34 - field public static final int OPERATOR_BARRED = 8; // 0x8 - field public static final int OTASP_COMMIT_IN_PROGRESS = 2208; // 0x8a0 - field public static final int PDN_CONN_DOES_NOT_EXIST = 54; // 0x36 - field public static final int PDN_INACTIVITY_TIMER_EXPIRED = 2051; // 0x803 - field public static final int PDN_IPV4_CALL_DISALLOWED = 2033; // 0x7f1 - field public static final int PDN_IPV4_CALL_THROTTLED = 2034; // 0x7f2 - field public static final int PDN_IPV6_CALL_DISALLOWED = 2035; // 0x7f3 - field public static final int PDN_IPV6_CALL_THROTTLED = 2036; // 0x7f4 - field public static final int PDN_NON_IP_CALL_DISALLOWED = 2071; // 0x817 - field public static final int PDN_NON_IP_CALL_THROTTLED = 2070; // 0x816 - field public static final int PDP_ACTIVATE_MAX_RETRY_FAILED = 2109; // 0x83d - field public static final int PDP_DUPLICATE = 2104; // 0x838 - field public static final int PDP_ESTABLISH_TIMEOUT_EXPIRED = 2161; // 0x871 - field public static final int PDP_INACTIVE_TIMEOUT_EXPIRED = 2163; // 0x873 - field public static final int PDP_LOWERLAYER_ERROR = 2164; // 0x874 - field public static final int PDP_MODIFY_COLLISION = 2165; // 0x875 - field public static final int PDP_MODIFY_TIMEOUT_EXPIRED = 2162; // 0x872 - field public static final int PDP_PPP_NOT_SUPPORTED = 2038; // 0x7f6 - field public static final int PDP_WITHOUT_ACTIVE_TFT = 46; // 0x2e - field public static final int PHONE_IN_USE = 2222; // 0x8ae - field public static final int PHYSICAL_LINK_CLOSE_IN_PROGRESS = 2040; // 0x7f8 - field public static final int PLMN_NOT_ALLOWED = 2101; // 0x835 - field public static final int PPP_AUTH_FAILURE = 2229; // 0x8b5 - field public static final int PPP_CHAP_FAILURE = 2232; // 0x8b8 - field public static final int PPP_CLOSE_IN_PROGRESS = 2233; // 0x8b9 - field public static final int PPP_OPTION_MISMATCH = 2230; // 0x8b6 - field public static final int PPP_PAP_FAILURE = 2231; // 0x8b7 - field public static final int PPP_TIMEOUT = 2228; // 0x8b4 - field public static final int PREF_RADIO_TECH_CHANGED = -4; // 0xfffffffc - field public static final int PROFILE_BEARER_INCOMPATIBLE = 2042; // 0x7fa - field public static final int PROTOCOL_ERRORS = 111; // 0x6f - field public static final int QOS_NOT_ACCEPTED = 37; // 0x25 - field public static final int RADIO_ACCESS_BEARER_FAILURE = 2110; // 0x83e - field public static final int RADIO_ACCESS_BEARER_SETUP_FAILURE = 2160; // 0x870 - field public static final int RADIO_NOT_AVAILABLE = 65537; // 0x10001 - field public static final int RADIO_POWER_OFF = -5; // 0xfffffffb - field public static final int REDIRECTION_OR_HANDOFF_IN_PROGRESS = 2220; // 0x8ac - field public static final int REGISTRATION_FAIL = -1; // 0xffffffff - field public static final int REGULAR_DEACTIVATION = 36; // 0x24 - field public static final int REJECTED_BY_BASE_STATION = 2082; // 0x822 - field public static final int RRC_CONNECTION_ABORTED_AFTER_HANDOVER = 2173; // 0x87d - field public static final int RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE = 2174; // 0x87e - field public static final int RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE = 2171; // 0x87b - field public static final int RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE = 2175; // 0x87f - field public static final int RRC_CONNECTION_ABORT_REQUEST = 2151; // 0x867 - field public static final int RRC_CONNECTION_ACCESS_BARRED = 2139; // 0x85b - field public static final int RRC_CONNECTION_ACCESS_STRATUM_FAILURE = 2137; // 0x859 - field public static final int RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS = 2138; // 0x85a - field public static final int RRC_CONNECTION_CELL_NOT_CAMPED = 2144; // 0x860 - field public static final int RRC_CONNECTION_CELL_RESELECTION = 2140; // 0x85c - field public static final int RRC_CONNECTION_CONFIG_FAILURE = 2141; // 0x85d - field public static final int RRC_CONNECTION_INVALID_REQUEST = 2168; // 0x878 - field public static final int RRC_CONNECTION_LINK_FAILURE = 2143; // 0x85f - field public static final int RRC_CONNECTION_NORMAL_RELEASE = 2147; // 0x863 - field public static final int RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER = 2150; // 0x866 - field public static final int RRC_CONNECTION_RADIO_LINK_FAILURE = 2148; // 0x864 - field public static final int RRC_CONNECTION_REESTABLISHMENT_FAILURE = 2149; // 0x865 - field public static final int RRC_CONNECTION_REJECT_BY_NETWORK = 2146; // 0x862 - field public static final int RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE = 2172; // 0x87c - field public static final int RRC_CONNECTION_RF_UNAVAILABLE = 2170; // 0x87a - field public static final int RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR = 2152; // 0x868 - field public static final int RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE = 2145; // 0x861 - field public static final int RRC_CONNECTION_TIMER_EXPIRED = 2142; // 0x85e - field public static final int RRC_CONNECTION_TRACKING_AREA_ID_CHANGED = 2169; // 0x879 - field public static final int RRC_UPLINK_CONNECTION_RELEASE = 2134; // 0x856 - field public static final int RRC_UPLINK_DATA_TRANSMISSION_FAILURE = 2132; // 0x854 - field public static final int RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER = 2133; // 0x855 - field public static final int RRC_UPLINK_ERROR_REQUEST_FROM_NAS = 2136; // 0x858 - field public static final int RRC_UPLINK_RADIO_LINK_FAILURE = 2135; // 0x857 - field public static final int RUIM_NOT_PRESENT = 2085; // 0x825 - field public static final int SECURITY_MODE_REJECTED = 2186; // 0x88a - field public static final int SERVICE_NOT_ALLOWED_ON_PLMN = 2129; // 0x851 - field public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 33; // 0x21 - field public static final int SERVICE_OPTION_NOT_SUPPORTED = 32; // 0x20 - field public static final int SERVICE_OPTION_OUT_OF_ORDER = 34; // 0x22 - field public static final int SIGNAL_LOST = -3; // 0xfffffffd - field public static final int SIM_CARD_CHANGED = 2043; // 0x7fb - field public static final int SYNCHRONIZATION_FAILURE = 2184; // 0x888 - field public static final int TEST_LOOPBACK_REGULAR_DEACTIVATION = 2196; // 0x894 - field public static final int TETHERED_CALL_ACTIVE = -6; // 0xfffffffa - field public static final int TFT_SEMANTIC_ERROR = 41; // 0x29 - field public static final int TFT_SYTAX_ERROR = 42; // 0x2a - field public static final int THERMAL_EMERGENCY = 2090; // 0x82a - field public static final int THERMAL_MITIGATION = 2062; // 0x80e - field public static final int TRAT_SWAP_FAILED = 2048; // 0x800 - field public static final int UE_INITIATED_DETACH_OR_DISCONNECT = 128; // 0x80 - field public static final int UE_IS_ENTERING_POWERSAVE_MODE = 2226; // 0x8b2 - field public static final int UE_RAT_CHANGE = 2105; // 0x839 - field public static final int UE_SECURITY_CAPABILITIES_MISMATCH = 2185; // 0x889 - field public static final int UMTS_HANDOVER_TO_IWLAN = 2199; // 0x897 - field public static final int UMTS_REACTIVATION_REQ = 39; // 0x27 - field public static final int UNACCEPTABLE_NETWORK_PARAMETER = 65538; // 0x10002 - field public static final int UNACCEPTABLE_NON_EPS_AUTHENTICATION = 2187; // 0x88b - field public static final int UNKNOWN = 65536; // 0x10000 - field public static final int UNKNOWN_INFO_ELEMENT = 99; // 0x63 - field public static final int UNKNOWN_PDP_ADDRESS_TYPE = 28; // 0x1c - field public static final int UNKNOWN_PDP_CONTEXT = 43; // 0x2b - field public static final int UNPREFERRED_RAT = 2039; // 0x7f7 - field public static final int UNSUPPORTED_1X_PREV = 2214; // 0x8a6 - field public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 66; // 0x42 - field public static final int UNSUPPORTED_QCI_VALUE = 59; // 0x3b - field public static final int USER_AUTHENTICATION = 29; // 0x1d - field public static final int VSNCP_ADMINISTRATIVELY_PROHIBITED = 2245; // 0x8c5 - field public static final int VSNCP_APN_UNATHORIZED = 2238; // 0x8be - field public static final int VSNCP_GEN_ERROR = 2237; // 0x8bd - field public static final int VSNCP_INSUFFICIENT_PARAMETERS = 2243; // 0x8c3 - field public static final int VSNCP_NO_PDN_GATEWAY_ADDRESS = 2240; // 0x8c0 - field public static final int VSNCP_PDN_EXISTS_FOR_THIS_APN = 2248; // 0x8c8 - field public static final int VSNCP_PDN_GATEWAY_REJECT = 2242; // 0x8c2 - field public static final int VSNCP_PDN_GATEWAY_UNREACHABLE = 2241; // 0x8c1 - field public static final int VSNCP_PDN_ID_IN_USE = 2246; // 0x8c6 - field public static final int VSNCP_PDN_LIMIT_EXCEEDED = 2239; // 0x8bf - field public static final int VSNCP_RECONNECT_NOT_ALLOWED = 2249; // 0x8c9 - field public static final int VSNCP_RESOURCE_UNAVAILABLE = 2244; // 0x8c4 - field public static final int VSNCP_SUBSCRIBER_LIMITATION = 2247; // 0x8c7 - field public static final int VSNCP_TIMEOUT = 2236; // 0x8bc + field @Deprecated public static final int VSNCP_APN_UNATHORIZED = 2238; // 0x8be } public final class DataSpecificRegistrationInfo implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index 8a0be6960ae5..9e37a3c5be96 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1478,6 +1478,10 @@ package android.media { method @NonNull public String getOriginalId(); } + public class MediaRouter2.RoutingController { + method @NonNull public String getOriginalId(); + } + public final class PlaybackParams implements android.os.Parcelable { method public int getAudioStretchMode(); method public android.media.PlaybackParams setAudioStretchMode(int); @@ -2534,6 +2538,11 @@ package android.os { method public void log(android.os.StrictMode.ViolationInfo); } + public static final class StrictMode.VmPolicy.Builder { + method @NonNull public android.os.StrictMode.VmPolicy.Builder detectIncorrectContextUse(); + method @NonNull public android.os.StrictMode.VmPolicy.Builder permitIncorrectContextUse(); + } + public class SystemConfigManager { method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps(); method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp index f56dd6e4968e..95de6c506795 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.cpp +++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp @@ -129,7 +129,7 @@ JNIEnv* DeviceCallback::getJNIEnv() { } std::unique_ptr<Device> Device::open(int32_t id, const char* name, int32_t vid, int32_t pid, - const std::vector<uint8_t>& descriptor, + uint16_t bus, const std::vector<uint8_t>& descriptor, std::unique_ptr<DeviceCallback> callback) { size_t size = descriptor.size(); if (size > HID_MAX_DESCRIPTOR_SIZE) { @@ -148,7 +148,7 @@ std::unique_ptr<Device> Device::open(int32_t id, const char* name, int32_t vid, strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name)); memcpy(&ev.u.create2.rd_data, descriptor.data(), size * sizeof(ev.u.create2.rd_data[0])); ev.u.create2.rd_size = size; - ev.u.create2.bus = BUS_BLUETOOTH; + ev.u.create2.bus = bus; ev.u.create2.vendor = vid; ev.u.create2.product = pid; ev.u.create2.version = 0; @@ -293,8 +293,8 @@ std::vector<uint8_t> getData(JNIEnv* env, jbyteArray javaArray) { return data; } -static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid, jint pid, - jbyteArray rawDescriptor, jobject callback) { +static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid, + jint pid, jint bus, jbyteArray rawDescriptor, jobject callback) { ScopedUtfChars name(env, rawName); if (name.c_str() == nullptr) { return 0; @@ -305,7 +305,7 @@ static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint i std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback)); std::unique_ptr<uhid::Device> d = - uhid::Device::open(id, reinterpret_cast<const char*>(name.c_str()), vid, pid, desc, + uhid::Device::open(id, reinterpret_cast<const char*>(name.c_str()), vid, pid, bus, desc, std::move(cb)); return reinterpret_cast<jlong>(d.release()); } @@ -339,14 +339,14 @@ static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) { } static JNINativeMethod sMethods[] = { - { "nativeOpenDevice", - "(Ljava/lang/String;III[B" - "Lcom/android/commands/hid/Device$DeviceCallback;)J", - reinterpret_cast<void*>(openDevice) }, - { "nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport) }, - { "nativeSendGetFeatureReportReply", "(JI[B)V", - reinterpret_cast<void*>(sendGetFeatureReportReply) }, - { "nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice) }, + {"nativeOpenDevice", + "(Ljava/lang/String;IIII[B" + "Lcom/android/commands/hid/Device$DeviceCallback;)J", + reinterpret_cast<void*>(openDevice)}, + {"nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport)}, + {"nativeSendGetFeatureReportReply", "(JI[B)V", + reinterpret_cast<void*>(sendGetFeatureReportReply)}, + {"nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice)}, }; int register_com_android_commands_hid_Device(JNIEnv* env) { diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h index 93ea881cfe28..7202b45adcde 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.h +++ b/cmds/hid/jni/com_android_commands_hid_Device.h @@ -43,7 +43,7 @@ private: class Device { public: static std::unique_ptr<Device> open(int32_t id, const char* name, int32_t vid, int32_t pid, - const std::vector<uint8_t>& descriptor, + uint16_t bus, const std::vector<uint8_t>& descriptor, std::unique_ptr<DeviceCallback> callback); ~Device(); diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java index 874604ceb5e4..dade41511ae6 100644 --- a/cmds/hid/src/com/android/commands/hid/Device.java +++ b/cmds/hid/src/com/android/commands/hid/Device.java @@ -52,13 +52,13 @@ public class Device { System.loadLibrary("hidcommand_jni"); } - private static native long nativeOpenDevice(String name, int id, int vid, int pid, + private static native long nativeOpenDevice(String name, int id, int vid, int pid, int bus, byte[] descriptor, DeviceCallback callback); private static native void nativeSendReport(long ptr, byte[] data); private static native void nativeSendGetFeatureReportReply(long ptr, int id, byte[] data); private static native void nativeCloseDevice(long ptr); - public Device(int id, String name, int vid, int pid, byte[] descriptor, + public Device(int id, String name, int vid, int pid, int bus, byte[] descriptor, byte[] report, SparseArray<byte[]> featureReports, Map<ByteBuffer, byte[]> outputs) { mId = id; mThread = new HandlerThread("HidDeviceHandler"); @@ -70,6 +70,7 @@ public class Device { args.argi1 = id; args.argi2 = vid; args.argi3 = pid; + args.argi4 = bus; if (name != null) { args.arg1 = name; } else { @@ -115,7 +116,7 @@ public class Device { case MSG_OPEN_DEVICE: SomeArgs args = (SomeArgs) msg.obj; mPtr = nativeOpenDevice((String) args.arg1, args.argi1, args.argi2, args.argi3, - (byte[]) args.arg2, new DeviceCallback()); + args.argi4, (byte[]) args.arg2, new DeviceCallback()); pauseEvents(); break; case MSG_SEND_REPORT: diff --git a/cmds/hid/src/com/android/commands/hid/Event.java b/cmds/hid/src/com/android/commands/hid/Event.java index 62587a70f10d..d4bf1d820a70 100644 --- a/cmds/hid/src/com/android/commands/hid/Event.java +++ b/cmds/hid/src/com/android/commands/hid/Event.java @@ -36,12 +36,28 @@ public class Event { public static final String COMMAND_DELAY = "delay"; public static final String COMMAND_REPORT = "report"; + // These constants come from "include/uapi/linux/input.h" in the kernel + enum Bus { + USB(0x03), BLUETOOTH(0x05); + + Bus(int value) { + mValue = value; + } + + int getValue() { + return mValue; + } + + private int mValue; + } + private int mId; private String mCommand; private String mName; private byte[] mDescriptor; private int mVid; private int mPid; + private Bus mBus; private byte[] mReport; private SparseArray<byte[]> mFeatureReports; private Map<ByteBuffer, byte[]> mOutputs; @@ -71,6 +87,10 @@ public class Event { return mPid; } + public int getBus() { + return mBus.getValue(); + } + public byte[] getReport() { return mReport; } @@ -94,6 +114,7 @@ public class Event { + ", descriptor=" + Arrays.toString(mDescriptor) + ", vid=" + mVid + ", pid=" + mPid + + ", bus=" + mBus + ", report=" + Arrays.toString(mReport) + ", feature_reports=" + mFeatureReports.toString() + ", outputs=" + mOutputs.toString() @@ -144,6 +165,10 @@ public class Event { mEvent.mPid = pid; } + public void setBus(Bus bus) { + mEvent.mBus = bus; + } + public void setDuration(int duration) { mEvent.mDuration = duration; } @@ -206,6 +231,9 @@ public class Event { case "pid": eb.setPid(readInt()); break; + case "bus": + eb.setBus(readBus()); + break; case "report": eb.setReport(readData()); break; @@ -264,6 +292,11 @@ public class Event { return Integer.decode(val); } + private Bus readBus() throws IOException { + String val = mReader.nextString(); + return Bus.valueOf(val.toUpperCase()); + } + private SparseArray<byte[]> readFeatureReports() throws IllegalStateException, IOException { SparseArray<byte[]> featureReports = new SparseArray<>(); diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java index 0ee2cc45932f..fac0ab2ef125 100644 --- a/cmds/hid/src/com/android/commands/hid/Hid.java +++ b/cmds/hid/src/com/android/commands/hid/Hid.java @@ -113,7 +113,7 @@ public class Hid { "Tried to send command \"" + e.getCommand() + "\" to an unregistered device!"); } int id = e.getId(); - Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), + Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(), e.getDescriptor(), e.getReport(), e.getFeatureReports(), e.getOutputs()); mDevices.append(id, d); } diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 93522d4ff0a6..73a8f666b78e 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -217,7 +217,10 @@ cc_binary { shared_libs: ["libgtest_prod"], - init_rc: ["statsd.rc"], + apex_available: [ + "com.android.os.statsd", + "test_com.android.os.statsd", + ], } // ============== @@ -299,6 +302,11 @@ cc_test { static_libs: [ "libgmock", "libplatformprotos", + + // TODO(b/149842105): Make libstatssocket shared and remove libcutils once statsd_test is + // moved to the apex. + "libstatssocket", + "libcutils", ], proto: { @@ -308,7 +316,6 @@ cc_test { shared_libs: [ "libprotobuf-cpp-lite", - "libstatssocket" ], } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index a4e8fdc10a3b..43d0fce316ad 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -392,6 +392,7 @@ message Atom { WifiFailureStatReported wifi_failure_stat_reported = 252 [(module) = "wifi"]; WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"]; AppFreezeChanged app_freeze_changed = 254 [(module) = "framework"]; + SnapshotMergeReported snapshot_merge_reported = 255; SdkExtensionStatus sdk_extension_status = 354; } @@ -4418,6 +4419,52 @@ message BootTimeEventErrorCode { optional int32 error_code = 2; } +/** + * Collects Virtual A/B statistics related to the use of dm-snapshot performed + * after an OTA. + * + * Logged from: + * - system/core/fs_mgr/libsnapshot/snapshot.cpp + * - system/core/fs_mgr/libsnapshot/snapshotctl.cpp + */ +message SnapshotMergeReported { + // Keep in sync with + // system/core/fs_mgr/libsnapshot/android/snapshot/snapshot.proto + enum UpdateState { + // No update or merge is in progress. + NONE = 0; + // An update is applying; snapshots may already exist. + INITIATED = 1; + // An update is pending, but has not been successfully booted yet. + UNVERIFIED = 2; + // The kernel is merging in the background. + MERGING = 3; + // Post-merge cleanup steps could not be completed due to a transient + // error, but the next reboot will finish any pending operations. + MERGE_NEEDS_REBOOT = 4; + // Merging is complete, and needs to be acknowledged. + MERGE_COMPLETED = 5; + // Merging failed due to an unrecoverable error. + MERGE_FAILED = 6; + // The update was implicitly cancelled, either by a rollback or a flash + // operation via fastboot. This state can only be returned by WaitForMerge. + CANCELLED = 7; + }; + + // Status of the update after the merge attempts. + optional UpdateState final_state = 1; + + // Time to complete a merge operation in milliseconds. + // A negative value corresponds to the case in which the merge operation + // was interrupted and resumed (e.g. in case of a system reboot during the + // merge). + optional int64 duration_millis = 2; + + // Number of reboots that occurred after issuing and before completing the + // merge of all the snapshot devices. + optional int32 intermediate_reboots = 3; +} + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS index 265674a74b7e..c6f42f719caa 100644 --- a/core/java/android/accessibilityservice/OWNERS +++ b/core/java/android/accessibilityservice/OWNERS @@ -1,3 +1,4 @@ svetoslavganov@google.com pweaver@google.com rhedjao@google.com +qasid@google.com diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 6b5bfda92cd0..8df26cbbb4f4 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -16,6 +16,9 @@ package android.app; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.StrictMode.vmIncorrectContextUseEnabled; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -64,6 +67,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Process; import android.os.RemoteException; +import android.os.StrictMode; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; @@ -247,6 +251,7 @@ class ContextImpl extends Context { private boolean mIsSystemOrSystemUiContext; private boolean mIsUiContext; + private boolean mIsAssociatedWithDisplay; @GuardedBy("mSync") private File mDatabasesDir; @@ -1891,9 +1896,20 @@ class ContextImpl extends Context { @Override public Object getSystemService(String name) { - if (isUiComponent(name) && !isUiContext()) { - Log.w(TAG, name + " should be accessed from Activity or other visual Context"); + // Check incorrect Context usage. + if (isUiComponent(name) && !isUiContext() && vmIncorrectContextUseEnabled()) { + final String errorMessage = "Tried to access visual service " + name + + " from a non-visual Context."; + final String message = "Visual services, such as WindowManager, WallpaperService or " + + "LayoutInflater should be accessed from Activity or other visual Context. " + + "Use an Activity or a Context created with " + + "Context#createWindowContext(int, Bundle), which are adjusted to the " + + "configuration and visual bounds of an area on screen."; + final Exception exception = new IllegalAccessException(errorMessage); + StrictMode.onIncorrectContextUsed(message, exception); + Log.e(TAG, errorMessage + message, exception); } + return SystemServiceRegistry.getSystemService(this, name); } @@ -1902,8 +1918,17 @@ class ContextImpl extends Context { return SystemServiceRegistry.getSystemServiceName(serviceClass); } - boolean isUiContext() { - return mIsSystemOrSystemUiContext || mIsUiContext; + private boolean isUiContext() { + return mIsSystemOrSystemUiContext || mIsUiContext || isSystemOrSystemUI(); + } + + /** + * Temporary workaround to permit incorrect usages of Context by SystemUI. + * TODO(b/149790106): Fix usages and remove. + */ + private boolean isSystemOrSystemUI() { + return ActivityThread.isSystem() || checkPermission("android.permission.STATUS_BAR_SERVICE", + Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED; } private static boolean isUiComponent(String name) { @@ -1925,7 +1950,7 @@ class ContextImpl extends Context { final int appId = UserHandle.getAppId(uid); if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) { Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission); - return PackageManager.PERMISSION_GRANTED; + return PERMISSION_GRANTED; } Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold " + permission); @@ -1989,7 +2014,7 @@ class ContextImpl extends Context { private void enforce( String permission, int resultOfCheck, boolean selfToo, int uid, String message) { - if (resultOfCheck != PackageManager.PERMISSION_GRANTED) { + if (resultOfCheck != PERMISSION_GRANTED) { throw new SecurityException( (message != null ? (message + ": ") : "") + (selfToo @@ -2116,15 +2141,15 @@ class ContextImpl extends Context { if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { if (readPermission == null || checkPermission(readPermission, pid, uid) - == PackageManager.PERMISSION_GRANTED) { - return PackageManager.PERMISSION_GRANTED; + == PERMISSION_GRANTED) { + return PERMISSION_GRANTED; } } if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { if (writePermission == null || checkPermission(writePermission, pid, uid) - == PackageManager.PERMISSION_GRANTED) { - return PackageManager.PERMISSION_GRANTED; + == PERMISSION_GRANTED) { + return PERMISSION_GRANTED; } } return uri != null ? checkUriPermission(uri, pid, uid, modeFlags) @@ -2157,7 +2182,7 @@ class ContextImpl extends Context { private void enforceForUri( int modeFlags, int resultOfCheck, boolean selfToo, int uid, Uri uri, String message) { - if (resultOfCheck != PackageManager.PERMISSION_GRANTED) { + if (resultOfCheck != PERMISSION_GRANTED) { throw new SecurityException( (message != null ? (message + ": ") : "") + (selfToo @@ -2373,6 +2398,7 @@ class ContextImpl extends Context { null, getDisplayAdjustments(displayId).getCompatibilityInfo(), mResources.getLoaders())); context.mDisplay = display; + context.mIsAssociatedWithDisplay = true; return context; } @@ -2390,6 +2416,7 @@ class ContextImpl extends Context { ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName, token, mUser, mFlags, mClassLoader, null); context.mIsUiContext = true; + context.mIsAssociatedWithDisplay = true; return context; } @@ -2440,6 +2467,19 @@ class ContextImpl extends Context { @Override public Display getDisplay() { + if (!mIsSystemOrSystemUiContext && !mIsAssociatedWithDisplay && !isSystemOrSystemUI()) { + throw new UnsupportedOperationException("Tried to obtain display from a Context not " + + "associated with one. Only visual Contexts (such as Activity or one created " + + "with Context#createWindowContext) or ones created with " + + "Context#createDisplayContext are associated with displays. Other types of " + + "Contexts are typically related to background entities and may return an " + + "arbitrary display."); + } + return getDisplayNoVerify(); + } + + @Override + public Display getDisplayNoVerify() { if (mDisplay == null) { return mResourcesManager.getAdjustedDisplay(Display.DEFAULT_DISPLAY, mResources); @@ -2450,13 +2490,14 @@ class ContextImpl extends Context { @Override public int getDisplayId() { - final Display display = getDisplay(); + final Display display = getDisplayNoVerify(); return display != null ? display.getDisplayId() : Display.DEFAULT_DISPLAY; } @Override public void updateDisplay(int displayId) { mDisplay = mResourcesManager.getAdjustedDisplay(displayId, mResources); + mIsAssociatedWithDisplay = true; } @Override @@ -2630,6 +2671,7 @@ class ContextImpl extends Context { ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, activityInfo.splitName, activityToken, null, 0, classLoader, null); context.mIsUiContext = true; + context.mIsAssociatedWithDisplay = true; // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY. displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY; diff --git a/core/java/android/app/TaskEmbedder.java b/core/java/android/app/TaskEmbedder.java index 761b225a7cdc..5ebcc46aa0d8 100644 --- a/core/java/android/app/TaskEmbedder.java +++ b/core/java/android/app/TaskEmbedder.java @@ -597,7 +597,7 @@ public class TaskEmbedder { if (mTmpDisplayMetrics == null) { mTmpDisplayMetrics = new DisplayMetrics(); } - mContext.getDisplay().getMetrics(mTmpDisplayMetrics); + mContext.getDisplayNoVerify().getRealMetrics(mTmpDisplayMetrics); return mTmpDisplayMetrics.densityDpi; } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 5f74d2e1dd57..d9405e18a162 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -2097,7 +2097,7 @@ public class WallpaperManager { public ColorManagementProxy(@NonNull Context context) { // Get a list of supported wide gamut color spaces. - Display display = context.getDisplay(); + Display display = context.getDisplayNoVerify(); if (display != null) { mSupportedColorSpaces.addAll(Arrays.asList(display.getSupportedWideColorGamut())); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 139b1796d8fc..55be08232e41 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -33,6 +33,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; +import android.annotation.UserHandleAware; import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.app.Activity; @@ -5740,6 +5741,25 @@ public class DevicePolicyManager { } /** + * Returns whether the admin has enabled always-on VPN lockdown for the current user. + * + * Only callable by the system. + * @hide + */ + @UserHandleAware + public boolean isAlwaysOnVpnLockdownEnabled() { + throwIfParentInstance("isAlwaysOnVpnLockdownEnabled"); + if (mService != null) { + try { + return mService.isAlwaysOnVpnLockdownEnabledForUser(myUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } + + /** * Called by device or profile owner to query the set of packages that are allowed to access * the network directly when always-on VPN is in lockdown mode but not connected. Returns * {@code null} when always-on VPN is not active or not in lockdown mode. @@ -5786,6 +5806,26 @@ public class DevicePolicyManager { } /** + * Returns the VPN package name if the admin has enabled always-on VPN on the current user, + * or {@code null} if none is set. + * + * Only callable by the system. + * @hide + */ + @UserHandleAware + public @Nullable String getAlwaysOnVpnPackage() { + throwIfParentInstance("getAlwaysOnVpnPackage"); + if (mService != null) { + try { + return mService.getAlwaysOnVpnPackageForUser(myUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return null; + } + + /** * Called by an application that is administering the device to disable all cameras on the * device, for this user. After setting this, no applications running as this user will be able * to access any cameras on the device. @@ -8949,49 +8989,6 @@ public class DevicePolicyManager { } /** - * Called by device owners to request a location provider to change its allowed state. For a - * provider to be enabled requires both that the master location setting is enabled, and that - * the provider itself is allowed. Most location providers are always allowed. Some location - * providers may have user consents or terms and conditions that must be accepted, or some other - * type of blocker before they are allowed however. Every location provider is responsible for - * its own allowed state. - * - * <p>This method requests that a location provider change its allowed state. For providers that - * are always allowed and have no state to change, this will have no effect. If the provider - * does require some consent, terms and conditions, or other blocking state, using this API - * implies that the device owner is agreeing/disagreeing to any consents, terms and conditions, - * etc, and the provider should make a best effort to adjust it's allowed state accordingly. - * - * <p>Location providers are generally only responsible for the current user, and callers must - * assume that this method will only affect provider state for the current user. Callers are - * responsible for tracking current user changes and re-updating provider state as necessary. - * - * <p>While providers are expected to make a best effort to honor this request, it is not a - * given that all providers will support such a request. If a provider does change its state as - * a result of this request, that may happen asynchronously after some delay. Test location - * providers set through {@link android.location.LocationManager#addTestProvider} will respond - * to this request to aide in testing. - * - * @param admin Which {@link DeviceAdminReceiver} this request is associated with - * @param provider A location provider as listed by - * {@link android.location.LocationManager#getAllProviders()} - * @param providerAllowed Whether the location provider is being requested to enable or disable - * itself - * @throws SecurityException if {@code admin} is not a device owner. - */ - public void requestSetLocationProviderAllowed(@NonNull ComponentName admin, - @NonNull String provider, boolean providerAllowed) { - throwIfParentInstance("requestSetLocationProviderAllowed"); - if (mService != null) { - try { - mService.requestSetLocationProviderAllowed(admin, provider, providerAllowed); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - } - - /** * Called by profile or device owners to update {@link android.provider.Settings.Secure} * settings. Validation that the value of the setting is in the correct form for the setting * type should be performed by the caller. diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 25c1e1205d2e..da48663145e1 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -196,7 +196,9 @@ interface IDevicePolicyManager { boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage, boolean lockdown, in List<String> lockdownWhitelist); String getAlwaysOnVpnPackage(in ComponentName who); + String getAlwaysOnVpnPackageForUser(int userHandle); boolean isAlwaysOnVpnLockdownEnabled(in ComponentName who); + boolean isAlwaysOnVpnLockdownEnabledForUser(int userHandle); List<String> getAlwaysOnVpnLockdownWhitelist(in ComponentName who); void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity); @@ -270,7 +272,6 @@ interface IDevicePolicyManager { boolean hasLockdownAdminConfiguredNetworks(in ComponentName who); void setLocationEnabled(in ComponentName who, boolean locationEnabled); - void requestSetLocationProviderAllowed(in ComponentName who, in String provider, boolean providerAllowed); boolean setTime(in ComponentName who, long millis); boolean setTimeZone(in ComponentName who, String timeZone); diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index e1942da8ac7f..bd3298c79fff 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -580,6 +580,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @Override + public void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri, + RemoteCallback callback) { + final Bundle result = new Bundle(); + result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, + canonicalize(callingPkg, featureId, uri)); + callback.sendResult(result); + } + + @Override public Uri uncanonicalize(String callingPkg, String featureId, Uri uri) { uri = validateIncomingUri(uri); int userId = getUserIdFromUri(uri); diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index 0f1442d864ba..7bc59013bcfe 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -359,6 +359,16 @@ abstract public class ContentProviderNative extends Binder implements IContentPr return true; } + case CANONICALIZE_ASYNC_TRANSACTION: { + data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); + String featureId = data.readString(); + Uri uri = Uri.CREATOR.createFromParcel(data); + RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data); + canonicalizeAsync(callingPkg, featureId, uri, callback); + return true; + } + case UNCANONICALIZE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); @@ -823,6 +833,25 @@ final class ContentProviderProxy implements IContentProvider } @Override + /* oneway */ public void canonicalizeAsync(String callingPkg, @Nullable String featureId, + Uri uri, RemoteCallback callback) throws RemoteException { + Parcel data = Parcel.obtain(); + try { + data.writeInterfaceToken(IContentProvider.descriptor); + + data.writeString(callingPkg); + data.writeString(featureId); + uri.writeToParcel(data, 0); + callback.writeToParcel(data, 0); + + mRemote.transact(IContentProvider.CANONICALIZE_ASYNC_TRANSACTION, data, null, + Binder.FLAG_ONEWAY); + } finally { + data.recycle(); + } + } + + @Override public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri url) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 0e0161ff4e9f..b748cfa775ed 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -712,14 +712,17 @@ public abstract class ContentResolver implements ContentInterface { * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS}. * @hide */ - public static final int CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS = + public static final int CONTENT_PROVIDER_READY_TIMEOUT_MILLIS = CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000; + // Timeout given a ContentProvider that has already been started and connected to. + private static final int CONTENT_PROVIDER_TIMEOUT_MILLIS = 3 * 1000; + // Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how // long ActivityManagerService is giving a content provider to get published if a new process // needs to be started for that. - private static final int GET_TYPE_TIMEOUT_MILLIS = - CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS + 5 * 1000; + private static final int REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS = + CONTENT_PROVIDER_READY_TIMEOUT_MILLIS + CONTENT_PROVIDER_TIMEOUT_MILLIS; public ContentResolver(@Nullable Context context) { this(context, null); @@ -833,10 +836,10 @@ public abstract class ContentResolver implements ContentInterface { IContentProvider provider = acquireExistingProvider(url); if (provider != null) { try { - final GetTypeResultListener resultListener = new GetTypeResultListener(); + final StringResultListener resultListener = new StringResultListener(); provider.getTypeAsync(url, new RemoteCallback(resultListener)); - resultListener.waitForResult(); - return resultListener.type; + resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS); + return resultListener.result; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. @@ -854,13 +857,13 @@ public abstract class ContentResolver implements ContentInterface { } try { - GetTypeResultListener resultListener = new GetTypeResultListener(); + final StringResultListener resultListener = new StringResultListener(); ActivityManager.getService().getProviderMimeTypeAsync( ContentProvider.getUriWithoutUserId(url), resolveUserId(url), new RemoteCallback(resultListener)); - resultListener.waitForResult(); - return resultListener.type; + resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS); + return resultListener.result; } catch (RemoteException e) { // We just failed to send a oneway request to the System Server. Nothing to do. return null; @@ -870,27 +873,29 @@ public abstract class ContentResolver implements ContentInterface { } } - private static class GetTypeResultListener implements RemoteCallback.OnResultListener { + private abstract static class ResultListener<T> implements RemoteCallback.OnResultListener { @GuardedBy("this") public boolean done; @GuardedBy("this") - public String type; + public T result; @Override public void onResult(Bundle result) { synchronized (this) { - type = result.getString(REMOTE_CALLBACK_RESULT); + this.result = getResultFromBundle(result); done = true; notifyAll(); } } - public void waitForResult() { + protected abstract T getResultFromBundle(Bundle result); + + public void waitForResult(long timeout) { synchronized (this) { if (!done) { try { - wait(GET_TYPE_TIMEOUT_MILLIS); + wait(timeout); } catch (InterruptedException e) { // Ignore } @@ -899,6 +904,20 @@ public abstract class ContentResolver implements ContentInterface { } } + private static class StringResultListener extends ResultListener<String> { + @Override + protected String getResultFromBundle(Bundle result) { + return result.getString(REMOTE_CALLBACK_RESULT); + } + } + + private static class UriResultListener extends ResultListener<Uri> { + @Override + protected Uri getResultFromBundle(Bundle result) { + return result.getParcelable(REMOTE_CALLBACK_RESULT); + } + } + /** * Query for the possible MIME types for the representations the given * content URL can be returned when opened as as stream with @@ -1192,7 +1211,11 @@ public abstract class ContentResolver implements ContentInterface { } try { - return provider.canonicalize(mPackageName, mFeatureId, url); + final UriResultListener resultListener = new UriResultListener(); + provider.canonicalizeAsync(mPackageName, mFeatureId, url, + new RemoteCallback(resultListener)); + resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS); + return resultListener.result; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ae12de027e6e..c6e84b7e46b0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3495,13 +3495,23 @@ public abstract class Context { * <dl> * <dt> {@link #WINDOW_SERVICE} ("window") * <dd> The top-level window manager in which you can place custom - * windows. The returned object is a {@link android.view.WindowManager}. + * windows. The returned object is a {@link android.view.WindowManager}. Must only be obtained + * from a visual context such as Activity or a Context created with + * {@link #createWindowContext(int, Bundle)}, which are adjusted to the configuration and + * visual bounds of an area on screen. * <dt> {@link #LAYOUT_INFLATER_SERVICE} ("layout_inflater") * <dd> A {@link android.view.LayoutInflater} for inflating layout resources - * in this context. + * in this context. Must only be obtained from a visual context such as Activity or a Context + * created with {@link #createWindowContext(int, Bundle)}, which are adjusted to the + * configuration and visual bounds of an area on screen. * <dt> {@link #ACTIVITY_SERVICE} ("activity") * <dd> A {@link android.app.ActivityManager} for interacting with the * global activity state of the system. + * <dt> {@link #WALLPAPER_SERVICE} ("wallpaper") + * <dd> A {@link android.service.wallpaper.WallpaperService} for accessing wallpapers in this + * context. Must only be obtained from a visual context such as Activity or a Context created + * with {@link #createWindowContext(int, Bundle)}, which are adjusted to the configuration and + * visual bounds of an area on screen. * <dt> {@link #POWER_SERVICE} ("power") * <dd> A {@link android.os.PowerManager} for controlling power * management. @@ -5901,6 +5911,8 @@ public abstract class Context { * {@link #createDisplayContext(Display)} to get a display object associated with a Context, or * {@link android.hardware.display.DisplayManager#getDisplay} to get a display object by id. * @return Returns the {@link Display} object this context is associated with. + * @throws UnsupportedOperationException if the method is called on an instance that is not + * associated with any display. */ @Nullable public Display getDisplay() { @@ -5908,6 +5920,17 @@ public abstract class Context { } /** + * A version of {@link #getDisplay()} that does not perform a Context misuse check to be used by + * legacy APIs. + * TODO(b/149790106): Fix usages and remove. + * @hide + */ + @Nullable + public Display getDisplayNoVerify() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** * Gets the ID of the display this context is associated with. * * @return display ID associated with this {@link Context}. diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index f6515e806caa..e5381ea0dd41 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -1003,6 +1003,12 @@ public class ContextWrapper extends Context { return mBase.getDisplay(); } + /** @hide */ + @Override + public @Nullable Display getDisplayNoVerify() { + return mBase.getDisplayNoVerify(); + } + /** * @hide */ diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index 4658ba109d5f..37643da375df 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -45,7 +45,7 @@ public interface IContentProvider extends IInterface { public String getType(Uri url) throws RemoteException; /** - * An oneway version of getType. The functionality is exactly the same, except that the + * A oneway version of getType. The functionality is exactly the same, except that the * call returns immediately, and the resulting type is returned when available via * a binder callback. */ @@ -126,6 +126,14 @@ public interface IContentProvider extends IInterface { public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) throws RemoteException; + /** + * A oneway version of canonicalize. The functionality is exactly the same, except that the + * call returns immediately, and the resulting type is returned when available via + * a binder callback. + */ + void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri, + RemoteCallback callback) throws RemoteException; + public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) throws RemoteException; @@ -162,4 +170,5 @@ public interface IContentProvider extends IInterface { static final int REFRESH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 26; static final int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 27; int GET_TYPE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 28; + int CANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 29; } diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl index 9e85fc301a0c..29a55b7a74da 100644 --- a/core/java/android/content/pm/IShortcutService.aidl +++ b/core/java/android/content/pm/IShortcutService.aidl @@ -76,4 +76,6 @@ interface IShortcutService { void removeLongLivedShortcuts(String packageName, in List shortcutIds, int userId); ParceledListSlice getShortcuts(String packageName, int matchFlags, int userId); + + void pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId); }
\ No newline at end of file diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java index 55846adf2f1f..82b07f24a906 100644 --- a/core/java/android/content/pm/ResolveInfo.java +++ b/core/java/android/content/pm/ResolveInfo.java @@ -39,6 +39,8 @@ import java.util.Comparator; */ public class ResolveInfo implements Parcelable { private static final String TAG = "ResolveInfo"; + private static final String INTENT_FORWARDER_ACTIVITY = + "com.android.internal.app.IntentForwarderActivity"; /** * The activity or broadcast receiver that corresponds to this resolution @@ -351,6 +353,16 @@ public class ResolveInfo implements Parcelable { } } + /** + * Returns whether this resolution represents the intent forwarder activity. + * + * @return whether this resolution represents the intent forwarder activity + */ + public boolean isCrossProfileIntentForwarderActivity() { + return activityInfo != null + && INTENT_FORWARDER_ACTIVITY.equals(activityInfo.targetActivity); + } + public ResolveInfo() { targetUserId = UserHandle.USER_CURRENT; } diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index bde4f614a39e..49e8c052cbca 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -1726,11 +1726,11 @@ public final class ShortcutInfo implements Parcelable { } /** - * @return true if pinned but neither static nor dynamic. + * @return true if pinned or cached, but neither static nor dynamic. * @hide */ public boolean isFloating() { - return isPinned() && !(isDynamic() || isManifestShortcut()); + return (isPinned() || isCached()) && !(isDynamic() || isManifestShortcut()); } /** @hide */ diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index 3eea3f62fd46..35c99a13a152 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; +import android.app.Notification; import android.app.usage.UsageStatsManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -741,4 +742,33 @@ public class ShortcutManager { throw e.rethrowFromSystemServer(); } } + + /** + * Publish a single dynamic shortcut. If there are already dynamic or pinned shortcuts with the + * same ID, each mutable shortcut is updated. + * + * <p>This method is useful when posting notifications which are tagged with shortcut IDs; In + * 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) + * + * <p>If {@link #getMaxShortcutCountPerActivity()} is already reached, an existing shortcut with + * the lowest rank will be removed to add space for the new shortcut. + * + * <p>If the rank of the shortcut is not explicitly set, it will be set to zero, and shortcut + * will be added to the top of the list. + * + * @throws IllegalArgumentException if trying to update an immutable shortcut. + * + * @throws IllegalStateException when the user is locked. + */ + public void pushDynamicShortcut(@NonNull ShortcutInfo shortcut) { + try { + mService.pushDynamicShortcut(mContext.getPackageName(), shortcut, injectMyUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 24d931154533..cc0c1a309038 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -688,17 +688,19 @@ public abstract class CameraDevice implements AutoCloseable { * <tr><th colspan="5">Concurrent stream guaranteed 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 YUV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>In-app video / image processing.</td> </tr> - * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>In-app viewfinder analysis.</td> </tr> - * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>In-app video / processing with preview.</td> </tr> - * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV }</td><td id="rb">{@code MAXIMUM}</td> <td>In-app video / processing with preview.</td> </tr> - * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV }</td><td id="rb">{@code MAXIMUM}</td> <td>Standard Recording.</td> </tr> + * <tr> <td>{@code YUV}</td><td id="rb">{@code s1440p}</td> <td colspan="2" id="rb"></td> <td>In-app video / image processing.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code s1440p}</td> <td colspan="2" id="rb"></td> <td>In-app viewfinder analysis.</td> </tr> + * <tr> <td>{@code JPEG}</td><td id="rb">{@code s1440p}</td> <td colspan="2" id="rb"></td> <td>No viewfinder still image capture.</td> </tr> + * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s720p}</td> <td>{@code JPEG}</td><td id="rb">{@code s1440p}</td> <td> Standard still imaging.</td> </tr> + * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s720p}</td> <td>{@code YUV / PRIV }</td><td id="rb">{@code s1440p}</td> <td>In-app video / processing with preview.</td> </tr> * </table><br> * </p> * - * <p> For guaranteed concurrent stream configurations, MAXIMUM refers to the camera device's - * resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or + * <p> For guaranteed concurrent stream configurations:</p> + * <p> s720p refers to the camera device's resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or * 720p(1280X720) whichever is lower. </p> + * <p> s1440p refers to the camera device's resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or + * 1440p(1920X1440) whichever is lower. </p> * <p>MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} * includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}) devices * supporting {@link android.graphics.ImageFormat#Y8 Y8} support substituting {@code YUV} diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 17c83f3a7eb9..743ce7b46792 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -140,6 +140,11 @@ public final class CameraManager { * <p>The set of combinations doesn't contain physical cameras that can only be used as * part of a logical multi-camera device.</p> * + * <p> If a new camera id becomes available through + * {@link AvailabilityCallback#onCameraUnavailable(String)}, clients can call + * this method to check if new combinations of camera ids which can stream concurrently are + * available. + * * @return The set of combinations of currently connected camera devices, that may have * sessions configured concurrently. The set of combinations will be empty if no such * combinations are supported by the camera subsystem. diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java index 41e1443d6866..f0fab6a99d14 100644 --- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java +++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java @@ -267,7 +267,7 @@ public final class MandatoryStreamCombination { mStreamsInformation.hashCode()); } - private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p } + private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p, s1440p } private static enum ReprocessType { NONE, PRIVATE, YUV } private static final class StreamTemplate { public int mFormat; @@ -651,23 +651,38 @@ public final class MandatoryStreamCombination { private static StreamCombinationTemplate sConcurrentStreamCombinations[] = { new StreamCombinationTemplate(new StreamTemplate [] { - new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p) }, + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p) }, "In-app video / image processing"), new StreamCombinationTemplate(new StreamTemplate [] { - new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p) }, + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p) }, "preview / preview to GPU"), new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p) }, + "No view-finder still image capture"), + new StreamCombinationTemplate(new StreamTemplate [] { new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p), - new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p)}, - "In-app video / image processing with preview"), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)}, + "Two-input in app video / image processing"), new StreamCombinationTemplate(new StreamTemplate [] { new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p), - new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p)}, + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)}, + "High resolution video recording with preview"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)}, + "In-app video / image processing with preview"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p), + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)}, "In-app video / image processing with preview"), new StreamCombinationTemplate(new StreamTemplate [] { new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p), - new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p)}, - "Standard Recording"), + new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)}, + "Standard stil image capture"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p), + new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)}, + "Standard still image capture"), }; /** @@ -720,6 +735,7 @@ public final class MandatoryStreamCombination { + " cannot have mandatory concurrent streams"); } Size size720p = new Size(1280, 720); + Size size1440p = new Size(1920, 1440); ArrayList<MandatoryStreamCombination> availableConcurrentStreamCombinations = new ArrayList<MandatoryStreamCombination>(); @@ -732,8 +748,16 @@ public final class MandatoryStreamCombination { for (StreamTemplate template : combTemplate.mStreamTemplates) { MandatoryStreamInformation streamInfo; List<Size> sizes = new ArrayList<Size>(); + Size formatSize = null; + switch (template.mSizeThreshold) { + case s1440p: + formatSize = size1440p; + break; + default: + formatSize = size720p; + } Size sizeChosen = - getMinSize(size720p, + getMinSize(formatSize, getMaxSize(mStreamConfigMap.getOutputSizes(template.mFormat))); sizes.add(sizeChosen); try { diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java index 6ad7faed16d8..ce26cb0d7adc 100644 --- a/core/java/android/hardware/display/DeviceProductInfo.java +++ b/core/java/android/hardware/display/DeviceProductInfo.java @@ -28,21 +28,21 @@ import java.util.Objects; * @hide */ public final class DeviceProductInfo implements Parcelable { - final private String mName; - final private String mManufacturerPnpId; - final private String mProductId; - final private Integer mModelYear; - final private ManufactureDate mManufactureDate; + private final String mName; + private final String mManufacturerPnpId; + private final String mProductId; + private final Integer mModelYear; + private final ManufactureDate mManufactureDate; public DeviceProductInfo( String name, String manufacturerPnpId, - String productCode, + String productId, Integer modelYear, ManufactureDate manufactureDate) { this.mName = name; this.mManufacturerPnpId = manufacturerPnpId; - this.mProductId = productCode; + this.mProductId = productId; this.mModelYear = modelYear; this.mManufactureDate = manufactureDate; } @@ -158,8 +158,8 @@ public final class DeviceProductInfo implements Parcelable { * @hide */ public static class ManufactureDate implements Parcelable { - final private Integer mWeek; - final private Integer mYear; + private final Integer mWeek; + private final Integer mYear; public ManufactureDate(Integer week, Integer year) { mWeek = week; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index ea26edef10d3..62eff4522d70 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1279,7 +1279,8 @@ public class ConnectivityManager { @UnsupportedAppUsage public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) { try { - return mService.getDefaultNetworkCapabilitiesForUser(userId); + return mService.getDefaultNetworkCapabilitiesForUser( + userId, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1361,7 +1362,7 @@ public class ConnectivityManager { @Nullable public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) { try { - return mService.getNetworkCapabilities(network); + return mService.getNetworkCapabilities(network, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -4039,10 +4040,9 @@ public class ConnectivityManager { @NonNull PendingIntent operation) { printStackTrace(); checkPendingIntentNotNull(operation); - final String callingPackageName = mContext.getOpPackageName(); try { mService.pendingRequestForNetwork( - request.networkCapabilities, operation, callingPackageName); + request.networkCapabilities, operation, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { @@ -4154,10 +4154,9 @@ public class ConnectivityManager { @NonNull PendingIntent operation) { printStackTrace(); checkPendingIntentNotNull(operation); - final String callingPackageName = mContext.getOpPackageName(); try { mService.pendingListenForNetwork( - request.networkCapabilities, operation, callingPackageName); + request.networkCapabilities, operation, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 1c7628f6ad0a..d84d05d522c2 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -60,7 +60,8 @@ interface IConnectivityManager NetworkInfo[] getAllNetworkInfo(); Network getNetworkForType(int networkType); Network[] getAllNetworks(); - NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId); + NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser( + int userId, String callingPackageName); boolean isNetworkSupported(int networkType); @@ -69,7 +70,7 @@ interface IConnectivityManager LinkProperties getLinkPropertiesForType(int networkType); LinkProperties getLinkProperties(in Network network); - NetworkCapabilities getNetworkCapabilities(in Network network); + NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName); @UnsupportedAppUsage NetworkState[] getAllNetworkState(); diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index f8b51dd9906b..83f99802b85f 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -830,6 +830,23 @@ public final class NetworkCapabilities implements Parcelable { * <p>This field keeps track of the UID of the app that created this network and is in charge of * its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running * VPN, or Carrier Service app managing a cellular data connection. + * + * <p>For NetworkCapability instances being sent from ConnectivityService, this value MUST be + * reset to Process.INVALID_UID unless all the following conditions are met: + * + * <ol> + * <li>The destination app is the network owner + * <li>The destination app has the ACCESS_FINE_LOCATION permission granted + * <li>The user's location toggle is on + * </ol> + * + * This is because the owner UID is location-sensitive. The apps that request a network could + * know where the device is if they can tell for sure the system has connected to the network + * they requested. + * + * <p>This is populated by the network agents and for the NetworkCapabilities instance sent by + * an app to the System Server, the value MUST be reset to Process.INVALID_UID by the system + * server. */ private int mOwnerUid = Process.INVALID_UID; @@ -842,7 +859,16 @@ public final class NetworkCapabilities implements Parcelable { } /** - * Retrieves the UID of the owner app. + * Retrieves the UID of the app that owns this network. + * + * <p>For user privacy reasons, this field will only be populated if: + * + * <ol> + * <li>The calling app is the network owner + * <li>The calling app has the ACCESS_FINE_LOCATION permission granted + * <li>The user's location toggle is on + * </ol> + * */ public int getOwnerUid() { return mOwnerUid; @@ -880,8 +906,9 @@ public final class NetworkCapabilities implements Parcelable { * @param administratorUids the UIDs to be set as administrators of this Network. * @hide */ + @NonNull @SystemApi - public @NonNull NetworkCapabilities setAdministratorUids( + public NetworkCapabilities setAdministratorUids( @NonNull final List<Integer> administratorUids) { mAdministratorUids.clear(); mAdministratorUids.addAll(administratorUids); diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index be2245820bb6..d60820ef0f57 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -16,6 +16,8 @@ package android.os; +import static android.view.Display.DEFAULT_DISPLAY; + import static java.nio.charset.StandardCharsets.UTF_8; import android.annotation.NonNull; @@ -33,6 +35,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.PackageManager; +import android.hardware.display.DisplayManager; import android.provider.Settings; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -614,7 +617,8 @@ public class RecoverySystem { // On TV, reboot quiescently if the screen is off if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { - if (context.getDisplay().getState() != Display.STATE_ON) { + DisplayManager dm = context.getSystemService(DisplayManager.class); + if (dm.getDisplay(DEFAULT_DISPLAY).getState() != Display.STATE_ON) { reason += ",quiescent"; } } diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 3faaff73a0ea..e8af56478230 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -42,6 +42,7 @@ import android.os.strictmode.DiskWriteViolation; import android.os.strictmode.ExplicitGcViolation; import android.os.strictmode.FileUriExposedViolation; import android.os.strictmode.ImplicitDirectBootViolation; +import android.os.strictmode.IncorrectContextUseViolation; import android.os.strictmode.InstanceCountViolation; import android.os.strictmode.IntentReceiverLeakedViolation; import android.os.strictmode.LeakedClosableViolation; @@ -250,6 +251,7 @@ public final class StrictMode { DETECT_VM_UNTAGGED_SOCKET, DETECT_VM_NON_SDK_API_USAGE, DETECT_VM_IMPLICIT_DIRECT_BOOT, + DETECT_VM_INCORRECT_CONTEXT_USE, PENALTY_GATHER, PENALTY_LOG, PENALTY_DIALOG, @@ -289,6 +291,8 @@ public final class StrictMode { private static final int DETECT_VM_IMPLICIT_DIRECT_BOOT = 1 << 10; /** @hide */ private static final int DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED = 1 << 11; + /** @hide */ + private static final int DETECT_VM_INCORRECT_CONTEXT_USE = 1 << 12; /** @hide */ private static final int DETECT_VM_ALL = 0x0000ffff; @@ -871,6 +875,9 @@ public final class StrictMode { if (targetSdk >= Build.VERSION_CODES.Q) { detectCredentialProtectedWhileLocked(); } + if (targetSdk >= Build.VERSION_CODES.R) { + detectIncorrectContextUse(); + } // TODO: Decide whether to detect non SDK API usage beyond a certain API level. // TODO: enable detectImplicitDirectBoot() once system is less noisy @@ -1028,6 +1035,32 @@ public final class StrictMode { } /** + * Detect attempts to invoke a method on a {@link Context} that is not suited for such + * operation. + * <p>An example of this is trying to obtain an instance of visual service (e.g. + * {@link android.view.WindowManager}) from a non-visual {@link Context}. This is not + * allowed, since a non-visual {@link Context} is not adjusted to any visual area, and + * therefore can report incorrect metrics or resources. + * @see Context#getDisplay() + * @see Context#getSystemService(String) + * @hide + */ + @TestApi + public @NonNull Builder detectIncorrectContextUse() { + return enable(DETECT_VM_INCORRECT_CONTEXT_USE); + } + + /** + * Disable detection of incorrect context use. + * TODO(b/149790106): Fix usages and remove. + * @hide + */ + @TestApi + public @NonNull Builder permitIncorrectContextUse() { + return disable(DETECT_VM_INCORRECT_CONTEXT_USE); + } + + /** * Crashes the whole process on violation. This penalty runs at the end of all enabled * penalties so you'll still get your logging or other violations before the process * dies. @@ -2057,6 +2090,11 @@ public final class StrictMode { } /** @hide */ + public static boolean vmIncorrectContextUseEnabled() { + return (sVmPolicy.mask & DETECT_VM_INCORRECT_CONTEXT_USE) != 0; + } + + /** @hide */ public static void onSqliteObjectLeaked(String message, Throwable originStack) { onVmPolicyViolation(new SqliteObjectLeakedViolation(message, originStack)); } @@ -2130,6 +2168,11 @@ public final class StrictMode { onVmPolicyViolation(new ImplicitDirectBootViolation()); } + /** @hide */ + public static void onIncorrectContextUsed(String message, Throwable originStack) { + onVmPolicyViolation(new IncorrectContextUseViolation(message, originStack)); + } + /** Assume locked until we hear otherwise */ private static volatile boolean sUserKeyUnlocked = false; diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java index f4e1f967dca8..dea495bf9327 100644 --- a/core/java/android/os/incremental/IncrementalStorage.java +++ b/core/java/android/os/incremental/IncrementalStorage.java @@ -434,6 +434,11 @@ public final class IncrementalStorage { signature = V4Signature.readFrom(input); } + if (!signature.isVersionSupported()) { + throw new IOException("v4 signature version " + signature.version + + " is not supported"); + } + final byte[] rootHash = signature.verityRootHash; final byte[] additionalData = signature.v3Digest; final byte[] pkcs7Signature = signature.pkcs7SignatureBlock; diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java index 6516917afd9d..17adfc8a05d9 100644 --- a/core/java/android/os/incremental/V4Signature.java +++ b/core/java/android/os/incremental/V4Signature.java @@ -31,7 +31,9 @@ import java.io.IOException; */ public class V4Signature { public static final String EXT = ".idsig"; + public static final int SUPPORTED_VERSION = 1; + public final int version; public final byte[] verityRootHash; public final byte[] v3Digest; public final byte[] pkcs7SignatureBlock; @@ -71,20 +73,27 @@ public class V4Signature { } } + boolean isVersionSupported() { + return this.version == SUPPORTED_VERSION; + } + static V4Signature readFrom(DataInputStream stream) throws IOException { + final int version = stream.readInt(); byte[] verityRootHash = readBytes(stream); byte[] v3Digest = readBytes(stream); byte[] pkcs7SignatureBlock = readBytes(stream); - return new V4Signature(verityRootHash, v3Digest, pkcs7SignatureBlock); + return new V4Signature(version, verityRootHash, v3Digest, pkcs7SignatureBlock); } - V4Signature(byte[] verityRootHash, byte[] v3Digest, byte[] pkcs7SignatureBlock) { + V4Signature(int version, byte[] verityRootHash, byte[] v3Digest, byte[] pkcs7SignatureBlock) { + this.version = version; this.verityRootHash = verityRootHash; this.v3Digest = v3Digest; this.pkcs7SignatureBlock = pkcs7SignatureBlock; } void writeTo(DataOutputStream stream) throws IOException { + stream.writeInt(this.version); writeBytes(stream, this.verityRootHash); writeBytes(stream, this.v3Digest); writeBytes(stream, this.pkcs7SignatureBlock); diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 8d04df0560f5..1454aac66d21 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -29,6 +29,7 @@ import static android.app.AppOpsManager.OP_WRITE_MEDIA_IMAGES; import static android.app.AppOpsManager.OP_WRITE_MEDIA_VIDEO; import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.UserHandle.PER_USER_RANGE; import android.annotation.BytesLong; import android.annotation.CallbackExecutor; @@ -2324,17 +2325,34 @@ public class StorageManager { private static final String XATTR_CACHE_TOMBSTONE = "user.cache_tombstone"; - // Matches AID_MEDIA_RW in android_filesystem_config.h - private static final int QUOTA_PROJECT_ID_MEDIA_NONE = 1023; + // Project IDs below must match android_projectid_config.h + /** + * Default project ID for files on external storage + * + * {@hide} + */ + public static final int PROJECT_ID_EXT_DEFAULT = 1000; - // Matches AID_MEDIA_IMAGE in android_filesystem_config.h - private static final int QUOTA_PROJECT_ID_MEDIA_IMAGE = 1057; + /** + * project ID for audio files on external storage + * + * {@hide} + */ + public static final int PROJECT_ID_EXT_MEDIA_AUDIO = 1001; - // Matches AID_MEDIA_AUDIO in android_filesystem_config.h - private static final int QUOTA_PROJECT_ID_MEDIA_AUDIO = 1055; + /** + * project ID for video files on external storage + * + * {@hide} + */ + public static final int PROJECT_ID_EXT_MEDIA_VIDEO = 1002; - // Matches AID_MEDIA_VIDEO in android_filesystem_config.h - private static final int QUOTA_PROJECT_ID_MEDIA_VIDEO = 1056; + /** + * project ID for image files on external storage + * + * {@hide} + */ + public static final int PROJECT_ID_EXT_MEDIA_IMAGE = 1003; /** * Constant for use with @@ -2388,6 +2406,11 @@ public class StorageManager { private static native boolean setQuotaProjectId(String path, long projectId); + private static long getProjectIdForUser(int userId, int projectId) { + // Much like UserHandle.getUid(), store the user ID in the upper bits + return userId * PER_USER_RANGE + projectId; + } + /** * Let StorageManager know that the quota type for a file on external storage should * be updated. Android tracks quotas for various media types. Consequently, this should be @@ -2417,18 +2440,27 @@ public class StorageManager { @QuotaType int quotaType) throws IOException { long projectId; final String filePath = path.getCanonicalPath(); + final StorageVolume volume = getStorageVolume(path); + if (volume == null) { + throw new IllegalStateException("Failed to update quota type for " + filePath); + } + + final int userId = volume.getOwner().getIdentifier(); + if (userId < 0) { + throw new IllegalStateException("Failed to update quota type for " + filePath); + } switch (quotaType) { case QUOTA_TYPE_MEDIA_NONE: - projectId = QUOTA_PROJECT_ID_MEDIA_NONE; + projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_DEFAULT); break; case QUOTA_TYPE_MEDIA_AUDIO: - projectId = QUOTA_PROJECT_ID_MEDIA_AUDIO; + projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_AUDIO); break; case QUOTA_TYPE_MEDIA_VIDEO: - projectId = QUOTA_PROJECT_ID_MEDIA_VIDEO; + projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_VIDEO); break; case QUOTA_TYPE_MEDIA_IMAGE: - projectId = QUOTA_PROJECT_ID_MEDIA_IMAGE; + projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_IMAGE); break; default: throw new IllegalArgumentException("Invalid quota type: " + quotaType); diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java b/core/java/android/os/strictmode/IncorrectContextUseViolation.java index 0b871e433f5b..647db171e080 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java +++ b/core/java/android/os/strictmode/IncorrectContextUseViolation.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,21 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.screenshot; -import android.app.Activity; -import android.os.Bundle; +package android.os.strictmode; -import com.android.systemui.tests.R; +import android.content.Context; /** - * A stub activity used in {@link ScreenshotTest}. + * Incorrect usage of {@link Context}, such as obtaining a visual service from non-visual + * {@link Context} instance. + * @see Context#getSystemService(String) + * @see Context#getDisplayNoVerify() + * @hide */ -public class ScreenshotStubActivity extends Activity { +public final class IncorrectContextUseViolation extends Violation { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); + /** @hide */ + public IncorrectContextUseViolation(String message, Throwable originStack) { + super(message); + initCause(originStack); } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a082f09facba..b9abdf83e260 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11509,6 +11509,7 @@ public final class Settings { * exempted_sync_duration (long) * system_interaction_duration (long) * initial_foreground_service_start_duration (long) + * cross_profile_apps_share_standby_buckets (boolean) * </pre> * * <p> diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java index cb20db90f549..b23d0cd4bd93 100644 --- a/core/java/android/service/controls/ControlsProviderService.java +++ b/core/java/android/service/controls/ControlsProviderService.java @@ -15,11 +15,14 @@ */ package android.service.controls; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.Service; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; @@ -51,6 +54,20 @@ public abstract class ControlsProviderService extends Service { @SdkConstant(SdkConstantType.SERVICE_ACTION) public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService"; + + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ADD_CONTROL = + "android.service.controls.action.ADD_CONTROL"; + + /** + * @hide + */ + public static final String EXTRA_CONTROL = + "android.service.controls.extra.CONTROL"; + /** * @hide */ @@ -325,6 +342,33 @@ public abstract class ControlsProviderService extends Service { } } + /** + * Request SystemUI to prompt the user to add a control to favorites. + * + * @param context A context + * @param componentName Component name of the {@link ControlsProviderService} + * @param control A stateless control to show to the user + */ + public static void requestAddControl(@NonNull Context context, + @NonNull ComponentName componentName, + @NonNull Control control) { + Preconditions.checkNotNull(context); + Preconditions.checkNotNull(componentName); + Preconditions.checkNotNull(control); + final ComponentName sysuiComponent = ComponentName.unflattenFromString( + context.getResources().getString( + com.android.internal.R.string.config_systemUIServiceComponent)); + Intent intent = new Intent(ACTION_ADD_CONTROL); + intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName); + intent.setPackage(sysuiComponent.getPackageName()); + if (isStatelessControl(control)) { + intent.putExtra(EXTRA_CONTROL, control); + } else { + intent.putExtra(EXTRA_CONTROL, new Control.StatelessBuilder(control).build()); + } + context.sendBroadcast(intent, Manifest.permission.BIND_CONTROLS); + } + private static class SubscriptionAdapter extends IControlsSubscription.Stub { final Subscription mSubscription; diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java index 73e17a61d17f..55542d898784 100644 --- a/core/java/android/util/LongSparseArray.java +++ b/core/java/android/util/LongSparseArray.java @@ -16,15 +16,18 @@ package android.util; +import android.annotation.NonNull; import android.os.Parcel; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; import com.android.internal.util.Preconditions; +import com.android.internal.util.function.LongObjPredicate; import libcore.util.EmptyArray; import java.util.Arrays; +import java.util.Objects; /** * SparseArray mapping longs to Objects. Unlike a normal array of Objects, @@ -145,6 +148,18 @@ public class LongSparseArray<E> implements Cloneable { delete(key); } + /** @hide */ + @SuppressWarnings("unchecked") + public void removeIf(@NonNull LongObjPredicate<? super E> filter) { + Objects.requireNonNull(filter); + for (int i = 0; i < mSize; ++i) { + if (mValues[i] != DELETED && filter.test(mKeys[i], (E) mValues[i])) { + mValues[i] = DELETED; + mGarbage = true; + } + } + } + /** * Removes the mapping at the specified index. * diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index deff79d5486a..73707ca889dd 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1586,6 +1586,19 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall info.addChild(wrapper.getLeashToken()); } + @Override + public int getImportantForAccessibility() { + final int mode = super.getImportantForAccessibility(); + // If developers explicitly set the important mode for it, don't change the mode. + // Only change the mode to important when this SurfaceView isn't explicitly set and has + // an embedded hierarchy. + if (mRemoteAccessibilityEmbeddedConnection == null + || mode != IMPORTANT_FOR_ACCESSIBILITY_AUTO) { + return mode; + } + return IMPORTANT_FOR_ACCESSIBILITY_YES; + } + private void initEmbeddedHierarchyForAccessibility(SurfaceControlViewHost.SurfacePackage p) { final IAccessibilityEmbeddedConnection connection = p.getAccessibilityEmbeddedConnection(); final RemoteAccessibilityEmbeddedConnection wrapper = diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4c7307ee5b8c..11ab5724d927 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -13442,8 +13442,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #getImportantForAccessibility() */ public boolean isImportantForAccessibility() { - final int mode = (mPrivateFlags2 & PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK) - >> PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT; + final int mode = getImportantForAccessibility(); if (mode == IMPORTANT_FOR_ACCESSIBILITY_NO || mode == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) { return false; diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java index cf34b0bad78d..f406be9ebac7 100644 --- a/core/java/android/view/WindowContainerTransaction.java +++ b/core/java/android/view/WindowContainerTransaction.java @@ -26,6 +26,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; +import android.view.SurfaceControl; import java.util.ArrayList; import java.util.List; @@ -77,8 +78,28 @@ public class WindowContainerTransaction implements Parcelable { public WindowContainerTransaction scheduleFinishEnterPip(IWindowContainer container, Rect bounds) { Change chg = getOrCreateChange(container.asBinder()); - chg.mSchedulePipCallback = true; chg.mPinnedBounds = new Rect(bounds); + chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK; + + return this; + } + + /** + * Send a SurfaceControl transaction to the server, which the server will apply in sync with + * the next bounds change. As this uses deferred transaction and not BLAST it is only + * able to sync with a single window, and the first visible window in this hierarchy of type + * BASE_APPLICATION to resize will be used. If there are bound changes included in this + * WindowContainer transaction (from setBounds or scheduleFinishEnterPip), the SurfaceControl + * transaction will be synced with those bounds. If there are no changes, then + * the SurfaceControl transaction will be synced with the next bounds change. This means + * that you can call this, apply the WindowContainer transaction, and then later call + * dismissPip() to achieve synchronization. + */ + public WindowContainerTransaction setBoundsChangeTransaction(IWindowContainer container, + SurfaceControl.Transaction t) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mBoundsChangeTransaction = t; + chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION; return this; } @@ -174,6 +195,8 @@ public class WindowContainerTransaction implements Parcelable { */ public static class Change implements Parcelable { public static final int CHANGE_FOCUSABLE = 1; + public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1; + public static final int CHANGE_PIP_CALLBACK = 1 << 2; private final Configuration mConfiguration = new Configuration(); private boolean mFocusable = true; @@ -181,8 +204,8 @@ public class WindowContainerTransaction implements Parcelable { private @ActivityInfo.Config int mConfigSetMask = 0; private @WindowConfiguration.WindowConfig int mWindowSetMask = 0; - private boolean mSchedulePipCallback = false; private Rect mPinnedBounds = null; + private SurfaceControl.Transaction mBoundsChangeTransaction = null; public Change() {} @@ -192,11 +215,14 @@ public class WindowContainerTransaction implements Parcelable { mChangeMask = in.readInt(); mConfigSetMask = in.readInt(); mWindowSetMask = in.readInt(); - mSchedulePipCallback = (in.readInt() != 0); - if (mSchedulePipCallback ) { + if ((mChangeMask & Change.CHANGE_PIP_CALLBACK) != 0) { mPinnedBounds = new Rect(); mPinnedBounds.readFromParcel(in); } + if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION) != 0) { + mBoundsChangeTransaction = + SurfaceControl.Transaction.CREATOR.createFromParcel(in); + } } public Configuration getConfiguration() { @@ -233,6 +259,10 @@ public class WindowContainerTransaction implements Parcelable { return mPinnedBounds; } + public SurfaceControl.Transaction getBoundsChangeTransaction() { + return mBoundsChangeTransaction; + } + @Override public String toString() { final boolean changesBounds = @@ -264,10 +294,12 @@ public class WindowContainerTransaction implements Parcelable { dest.writeInt(mConfigSetMask); dest.writeInt(mWindowSetMask); - dest.writeInt(mSchedulePipCallback ? 1 : 0); - if (mSchedulePipCallback ) { + if (mPinnedBounds != null) { mPinnedBounds.writeToParcel(dest, flags); } + if (mBoundsChangeTransaction != null) { + mBoundsChangeTransaction.writeToParcel(dest, flags); + } } @Override diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 56683dd9a5d1..d40f505f87a0 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -101,7 +101,7 @@ public final class WindowManagerImpl implements WindowManager { @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); - mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); + mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow); mIsViewAdded = true; mLastView = view; mLastParams = (WindowManager.LayoutParams) params; @@ -158,7 +158,7 @@ public final class WindowManagerImpl implements WindowManager { @Override public Display getDefaultDisplay() { - return mContext.getDisplay(); + return mContext.getDisplayNoVerify(); } @Override @@ -240,7 +240,7 @@ public final class WindowManagerImpl implements WindowManager { private Rect getMaximumBounds() { // TODO(b/128338354): Current maximum bound is display size, but it should be displayArea // bound after displayArea feature is finished. - final Display display = mContext.getDisplay(); + final Display display = mContext.getDisplayNoVerify(); final Point displaySize = new Point(); display.getRealSize(displaySize); return new Rect(0, 0, displaySize.x, displaySize.y); diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS index 265674a74b7e..c6f42f719caa 100644 --- a/core/java/android/view/accessibility/OWNERS +++ b/core/java/android/view/accessibility/OWNERS @@ -1,3 +1,4 @@ svetoslavganov@google.com pweaver@google.com rhedjao@google.com +qasid@google.com diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index dbab81b129a9..39d5f5c396a1 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -93,12 +93,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; @@ -415,16 +410,6 @@ public final class InputMethodManager { int mCursorCandEnd; /** - * Initial startInput with {@link StartInputReason.WINDOW_FOCUS_GAIN} is executed - * in a background thread. Later, if there is an actual startInput it will wait on - * main thread till the background thread completes. - */ - private Future<?> mWindowFocusGainFuture; - - private ExecutorService mStartInputWorker = Executors.newSingleThreadExecutor( - new ImeThreadFactory("StartInputWorker")); - - /** * The instance that has previously been sent to the input method. */ private CursorAnchorInfo mCursorAnchorInfo = null; @@ -612,41 +597,36 @@ public final class InputMethodManager { final boolean forceNewFocus1 = forceNewFocus; final int startInputFlags = getStartInputFlags(focusedView, 0); - if (mWindowFocusGainFuture != null) { - mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */); + final ImeFocusController controller = getFocusController(); + if (controller == null) { + return; } - mWindowFocusGainFuture = mStartInputWorker.submit(() -> { - final ImeFocusController controller = getFocusController(); - if (controller == null) { + if (controller.checkFocus(forceNewFocus1, false)) { + // We need to restart input on the current focus view. This + // should be done in conjunction with telling the system service + // about the window gaining focus, to help make the transition + // smooth. + if (startInput(StartInputReason.WINDOW_FOCUS_GAIN, + focusedView, startInputFlags, softInputMode, windowFlags)) { return; } - if (controller.checkFocus(forceNewFocus1, false)) { - // We need to restart input on the current focus view. This - // should be done in conjunction with telling the system service - // about the window gaining focus, to help make the transition - // smooth. - if (startInput(StartInputReason.WINDOW_FOCUS_GAIN, - focusedView, startInputFlags, softInputMode, windowFlags)) { - return; - } - } + } - synchronized (mH) { - // For some reason we didn't do a startInput + windowFocusGain, so - // we'll just do a window focus gain and call it a day. - try { - if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput"); - mService.startInputOrWindowGainedFocus( - StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient, - focusedView.getWindowToken(), startInputFlags, softInputMode, - windowFlags, - null, null, 0 /* missingMethodFlags */, - mCurRootView.mContext.getApplicationInfo().targetSdkVersion); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + synchronized (mH) { + // For some reason we didn't do a startInput + windowFocusGain, so + // we'll just do a window focus gain and call it a day. + try { + if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput"); + mService.startInputOrWindowGainedFocus( + StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient, + focusedView.getWindowToken(), startInputFlags, softInputMode, + windowFlags, + null, null, 0 /* missingMethodFlags */, + mCurRootView.mContext.getApplicationInfo().targetSdkVersion); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } - }); + } } /** @@ -664,10 +644,6 @@ public final class InputMethodManager { */ @Override public void setCurrentRootView(ViewRootImpl rootView) { - if (mWindowFocusGainFuture != null) { - mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */); - mWindowFocusGainFuture = null; - } synchronized (mH) { if (mCurRootView != null) { // Reset the last served view and restart window focus state of the root view. @@ -845,19 +821,16 @@ public final class InputMethodManager { } catch (RemoteException e) { } } - } - // Check focus again in case that "onWindowFocus" is called before - // handling this message. - final View servedView; - synchronized (mH) { - servedView = getServedViewLocked(); - } - if (servedView != null && canStartInput(servedView)) { - if (mCurRootView != null && mCurRootView.getImeFocusController() - .checkFocus(mRestartOnNextWindowFocus, false)) { - final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS - : StartInputReason.DEACTIVATED_BY_IMMS; - mDelegate.startInput(reason, null, 0, 0, 0); + // Check focus again in case that "onWindowFocus" is called before + // handling this message. + final View servedView = getServedViewLocked(); + if (servedView != null && canStartInput(servedView)) { + if (mCurRootView != null && mCurRootView.getImeFocusController() + .checkFocus(mRestartOnNextWindowFocus, false)) { + final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS + : StartInputReason.DEACTIVATED_BY_IMMS; + mDelegate.startInput(reason, null, 0, 0, 0); + } } } return; @@ -1035,6 +1008,13 @@ public final class InputMethodManager { } @Override + public void scheduleStartInputIfNecessary(boolean fullscreen) { + // TODO(b/149859205): See if we can optimize this by having a fused dedicated operation. + mH.obtainMessage(MSG_SET_ACTIVE, 0 /* active */, fullscreen ? 1 : 0).sendToTarget(); + mH.obtainMessage(MSG_SET_ACTIVE, 1 /* active */, fullscreen ? 1 : 0).sendToTarget(); + } + + @Override public void reportFullscreenMode(boolean fullscreen) { mH.obtainMessage(MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, 0) .sendToTarget(); @@ -1430,10 +1410,6 @@ public final class InputMethodManager { */ void clearBindingLocked() { if (DEBUG) Log.v(TAG, "Clearing binding!"); - if (mWindowFocusGainFuture != null) { - mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */); - mWindowFocusGainFuture = null; - } clearConnectionLocked(); setInputChannelLocked(null); mBindSequence = -1; @@ -1826,18 +1802,6 @@ public final class InputMethodManager { boolean startInputInner(@StartInputReason int startInputReason, @Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags) { - if (startInputReason != StartInputReason.WINDOW_FOCUS_GAIN - && mWindowFocusGainFuture != null) { - try { - mWindowFocusGainFuture.get(); - } catch (ExecutionException | InterruptedException e) { - // do nothing - } catch (CancellationException e) { - // window no longer has focus. - return true; - } - } - final View view; synchronized (mH) { view = getServedViewLocked(); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 9de1222c9ac6..13c1f67ef85b 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -83,6 +83,7 @@ import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; +import android.util.TypedValue; import android.view.ActionMode; import android.view.ActionMode.Callback; import android.view.ContextMenu; @@ -151,9 +152,6 @@ public class Editor { private static final String TAG = "Editor"; private static final boolean DEBUG_UNDO = false; - // Specifies whether to allow starting a cursor drag by dragging anywhere over the text. - @VisibleForTesting - public static boolean FLAG_ENABLE_CURSOR_DRAG = true; // Specifies whether to use the magnifier when pressing the insertion or selection handles. private static final boolean FLAG_USE_MAGNIFIER = true; @@ -387,13 +385,25 @@ public class Editor { private final SuggestionHelper mSuggestionHelper = new SuggestionHelper(); - // Specifies whether the cursor control feature set is enabled. - // This can only be true if the text view is editable. - private boolean mCursorControlEnabled; + private boolean mFlagCursorDragFromAnywhereEnabled; + private boolean mFlagInsertionHandleGesturesEnabled; // Specifies whether the new magnifier (with fish-eye effect) is enabled. private final boolean mNewMagnifierEnabled; + // Line height range in DP for the new magnifier. + static private final int MIN_LINE_HEIGHT_FOR_MAGNIFIER = 20; + static private final int MAX_LINE_HEIGHT_FOR_MAGNIFIER = 32; + // Line height range in pixels for the new magnifier. + // - If the line height is bigger than the max, magnifier should be dismissed. + // - If the line height is smaller than the min, magnifier should apply a bigger zoom factor + // to make sure the text can be seen clearly. + private int mMinLineHeightForMagnifier; + private int mMaxLineHeightForMagnifier; + // The zoom factor initially configured. + // The actual zoom value may changes based on this initial zoom value. + private float mInitialZoom = 1f; + Editor(TextView textView) { mTextView = textView; // Synchronize the filter list, which places the undo input filter at the end. @@ -402,26 +412,43 @@ public class Editor { mHapticTextHandleEnabled = mTextView.getContext().getResources().getBoolean( com.android.internal.R.bool.config_enableHapticTextHandle); - mCursorControlEnabled = AppGlobals.getIntCoreSetting( - WidgetFlags.KEY_ENABLE_CURSOR_CONTROL , 0) != 0; + mFlagCursorDragFromAnywhereEnabled = AppGlobals.getIntCoreSetting( + WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, + WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT ? 1 : 0) != 0; + mFlagInsertionHandleGesturesEnabled = AppGlobals.getIntCoreSetting( + WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES, + WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES_DEFAULT ? 1 : 0) != 0; mNewMagnifierEnabled = AppGlobals.getIntCoreSetting( - WidgetFlags.KEY_ENABLE_NEW_MAGNIFIER, 0) != 0; + WidgetFlags.KEY_ENABLE_NEW_MAGNIFIER, + WidgetFlags.ENABLE_NEW_MAGNIFIER_DEFAULT ? 1 : 0) != 0; if (TextView.DEBUG_CURSOR) { - logCursor("Editor", "Cursor control is %s.", - mCursorControlEnabled ? "enabled" : "disabled"); + logCursor("Editor", "Cursor drag from anywhere is %s.", + mFlagCursorDragFromAnywhereEnabled ? "enabled" : "disabled"); + logCursor("Editor", "Insertion handle gestures is %s.", + mFlagInsertionHandleGesturesEnabled ? "enabled" : "disabled"); logCursor("Editor", "New magnifier is %s.", mNewMagnifierEnabled ? "enabled" : "disabled"); } } @VisibleForTesting - public void setCursorControlEnabled(boolean enabled) { - mCursorControlEnabled = enabled; + public boolean getFlagCursorDragFromAnywhereEnabled() { + return mFlagCursorDragFromAnywhereEnabled; + } + + @VisibleForTesting + public void setFlagCursorDragFromAnywhereEnabled(boolean enabled) { + mFlagCursorDragFromAnywhereEnabled = enabled; + } + + @VisibleForTesting + public boolean getFlagInsertionHandleGesturesEnabled() { + return mFlagInsertionHandleGesturesEnabled; } @VisibleForTesting - public boolean getCursorControlEnabled() { - return mCursorControlEnabled; + public void setFlagInsertionHandleGesturesEnabled(boolean enabled) { + mFlagInsertionHandleGesturesEnabled = enabled; } // Lazy creates the magnifier animator. @@ -440,12 +467,12 @@ public class Editor { private Magnifier.Builder createBuilderWithInlineMagnifierDefaults() { final Magnifier.Builder params = new Magnifier.Builder(mTextView); - // TODO: supports changing the height/width dynamically because the text height can be - // dynamically changed. float zoom = AppGlobals.getFloatCoreSetting( - WidgetFlags.KEY_MAGNIFIER_ZOOM_FACTOR, 1.5f); + WidgetFlags.KEY_MAGNIFIER_ZOOM_FACTOR, + WidgetFlags.MAGNIFIER_ZOOM_FACTOR_DEFAULT); float aspectRatio = AppGlobals.getFloatCoreSetting( - WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO, 5.5f); + WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO, + WidgetFlags.MAGNIFIER_ASPECT_RATIO_DEFAULT); // Avoid invalid/unsupported values. if (zoom < 1.2f || zoom > 1.8f) { zoom = 1.5f; @@ -454,13 +481,20 @@ public class Editor { aspectRatio = 5.5f; } + mInitialZoom = zoom; + mMinLineHeightForMagnifier = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, MIN_LINE_HEIGHT_FOR_MAGNIFIER, + mTextView.getContext().getResources().getDisplayMetrics()); + mMaxLineHeightForMagnifier = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, MAX_LINE_HEIGHT_FOR_MAGNIFIER, + mTextView.getContext().getResources().getDisplayMetrics()); + final Layout layout = mTextView.getLayout(); final int line = layout.getLineForOffset(mTextView.getSelectionStart()); final int sourceHeight = layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line); - // Slightly increase the height to avoid tooLargeTextForMagnifier() returns true. - int height = (int)(sourceHeight * zoom) + 2; - int width = (int)(aspectRatio * height); + final int height = (int)(sourceHeight * zoom); + final int width = (int)(aspectRatio * Math.max(sourceHeight, mMinLineHeightForMagnifier)); params.setFishEyeStyle() .setSize(width, height) @@ -4902,6 +4936,12 @@ public class Editor { } private boolean tooLargeTextForMagnifier() { + if (mNewMagnifierEnabled) { + Layout layout = mTextView.getLayout(); + final int line = layout.getLineForOffset(getCurrentCursorOffset()); + return layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line) + >= mMaxLineHeightForMagnifier; + } final float magnifierContentHeight = Math.round( mMagnifierAnimator.mMagnifier.getHeight() / mMagnifierAnimator.mMagnifier.getZoom()); @@ -5111,9 +5151,16 @@ public class Editor { int lineRight = (int) layout.getLineRight(line); lineRight += mTextView.getTotalPaddingLeft() - mTextView.getScrollX(); mMagnifierAnimator.mMagnifier.setSourceHorizontalBounds(lineLeft, lineRight); + final int lineHeight = + layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line); + float zoom = mInitialZoom; + if (lineHeight < mMinLineHeightForMagnifier) { + zoom = zoom * mMinLineHeightForMagnifier / lineHeight; + } + mMagnifierAnimator.mMagnifier.updateSourceFactors(lineHeight, zoom); mMagnifierAnimator.mMagnifier.show(showPosInView.x, showPosInView.y); } else { - mMagnifierAnimator.show(showPosInView.x, showPosInView.y); + mMagnifierAnimator.show(showPosInView.x, showPosInView.y); } updateHandlesVisibility(); } else { @@ -5259,11 +5306,13 @@ public class Editor { int deltaHeight = 0; int opacity = 255; - if (mCursorControlEnabled) { + if (mFlagInsertionHandleGesturesEnabled) { deltaHeight = AppGlobals.getIntCoreSetting( - WidgetFlags.KEY_INSERTION_HANDLE_DELTA_HEIGHT, 25); + WidgetFlags.KEY_INSERTION_HANDLE_DELTA_HEIGHT, + WidgetFlags.INSERTION_HANDLE_DELTA_HEIGHT_DEFAULT); opacity = AppGlobals.getIntCoreSetting( - WidgetFlags.KEY_INSERTION_HANDLE_OPACITY, 50); + WidgetFlags.KEY_INSERTION_HANDLE_OPACITY, + WidgetFlags.INSERTION_HANDLE_OPACITY_DEFAULT); // Avoid invalid/unsupported values. if (deltaHeight < -25 || deltaHeight > 50) { deltaHeight = 25; @@ -5329,7 +5378,7 @@ public class Editor { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (mCursorControlEnabled) { + if (mFlagInsertionHandleGesturesEnabled) { final int height = Math.max( getPreferredHeight() + mDeltaHeight, mDrawable.getIntrinsicHeight()); setMeasuredDimension(getPreferredWidth(), height); @@ -5340,7 +5389,7 @@ public class Editor { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mCursorControlEnabled && FLAG_ENABLE_CURSOR_DRAG) { + if (mFlagInsertionHandleGesturesEnabled && mFlagCursorDragFromAnywhereEnabled) { // Should only enable touch through when cursor drag is enabled. // Otherwise the insertion handle view cannot be moved. return touchThrough(ev); @@ -6031,7 +6080,7 @@ public class Editor { } if (mIsDraggingCursor) { performCursorDrag(event); - } else if (FLAG_ENABLE_CURSOR_DRAG + } else if (mFlagCursorDragFromAnywhereEnabled && mTextView.getLayout() != null && mTextView.isFocused() && mTouchState.isMovedEnoughForDrag() diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index 47ea1cbade93..a299b0185433 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -89,7 +89,7 @@ public final class Magnifier { // The width of the window containing the magnifier. private final int mWindowWidth; // The height of the window containing the magnifier. - private final int mWindowHeight; + private int mWindowHeight; // The zoom applied to the view region copied to the magnifier view. private float mZoom; // The width of the content that will be copied to the magnifier. @@ -485,6 +485,21 @@ public final class Magnifier { } /** + * Updates the factors of source which may impact the magnifier's size. + * This can be called while the magnifier is showing and moving. + * @param sourceHeight the new source height. + * @param zoom the new zoom factor. + */ + void updateSourceFactors(final int sourceHeight, final float zoom) { + mZoom = zoom; + mSourceHeight = sourceHeight; + mWindowHeight = (int) (sourceHeight * zoom); + if (mWindow != null) { + mWindow.updateContentFactors(mWindowHeight, zoom); + } + } + + /** * Returns the zoom to be applied to the magnified view region copied to the magnifier. * If the zoom is x and the magnifier window size is (width, height), the original size * of the content being magnified will be (width / x, height / x). @@ -904,7 +919,7 @@ public final class Magnifier { private final Display mDisplay; // The size of the content of the magnifier. private final int mContentWidth; - private final int mContentHeight; + private int mContentHeight; // The insets of the content inside the allocated surface. private final int mOffsetX; private final int mOffsetY; @@ -947,7 +962,7 @@ public final class Magnifier { // The current content of the magnifier. It is mBitmap + mOverlay, only used for testing. private Bitmap mCurrentContent; - private final float mZoom; + private float mZoom; // The width of the ramp region in pixels on the left & right sides of the fish-eye effect. private final int mRamp; // Whether is in the new magnifier style. @@ -1009,11 +1024,11 @@ public final class Magnifier { final RecordingCanvas canvas = mRenderer.getRootNode().beginRecording(width, height); try { - canvas.insertReorderBarrier(); + canvas.enableZ(); canvas.drawRenderNode(mBitmapRenderNode); - canvas.insertInorderBarrier(); + canvas.disableZ(); canvas.drawRenderNode(mOverlayRenderNode); - canvas.insertInorderBarrier(); + canvas.disableZ(); } finally { mRenderer.getRootNode().endRecording(); } @@ -1034,15 +1049,66 @@ public final class Magnifier { } } + /** + * Updates the factors of content which may resize the window. + * @param contentHeight the new height of content. + * @param zoom the new zoom factor. + */ + private void updateContentFactors(final int contentHeight, final float zoom) { + if (mContentHeight == contentHeight && mZoom == zoom) { + return; + } + if (mContentHeight < contentHeight) { + // Grows the surface height as necessary. + new SurfaceControl.Transaction().setBufferSize( + mSurfaceControl, mContentWidth, contentHeight).apply(); + mSurface.copyFrom(mSurfaceControl); + mRenderer.setSurface(mSurface); + + final Outline outline = new Outline(); + outline.setRoundRect(0, 0, mContentWidth, contentHeight, 0); + outline.setAlpha(1.0f); + + mBitmapRenderNode.setLeftTopRightBottom(mOffsetX, mOffsetY, + mOffsetX + mContentWidth, mOffsetY + contentHeight); + mBitmapRenderNode.setOutline(outline); + + mOverlayRenderNode.setLeftTopRightBottom(mOffsetX, mOffsetY, + mOffsetX + mContentWidth, mOffsetY + contentHeight); + mOverlayRenderNode.setOutline(outline); + + final RecordingCanvas canvas = + mRenderer.getRootNode().beginRecording(mContentWidth, contentHeight); + try { + canvas.enableZ(); + canvas.drawRenderNode(mBitmapRenderNode); + canvas.disableZ(); + canvas.drawRenderNode(mOverlayRenderNode); + canvas.disableZ(); + } finally { + mRenderer.getRootNode().endRecording(); + } + } + mContentHeight = contentHeight; + mZoom = zoom; + fillMeshMatrix(); + } + private void createMeshMatrixForFishEyeEffect() { mMeshWidth = 1; mMeshHeight = 6; + mMeshLeft = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)]; + mMeshRight = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)]; + fillMeshMatrix(); + } + + private void fillMeshMatrix() { + mMeshWidth = 1; + mMeshHeight = 6; final float w = mContentWidth; final float h = mContentHeight; final float h0 = h / mZoom; final float dh = h - h0; - mMeshLeft = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)]; - mMeshRight = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)]; for (int i = 0; i < 2 * (mMeshWidth + 1) * (mMeshHeight + 1); i += 2) { // Calculates X value. final int colIndex = i % (2 * (mMeshWidth + 1)) / 2; @@ -1197,6 +1263,7 @@ public final class Magnifier { if (mBitmap != null) { mBitmap.recycle(); } + mOverlay.setCallback(null); } private void doDraw() { diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java index 1a8e7a713e7a..bce5497a7c2d 100644 --- a/core/java/android/widget/WidgetFlags.java +++ b/core/java/android/widget/WidgetFlags.java @@ -17,27 +17,49 @@ package android.widget; /** - * Keeps the flags related to the Widget namespace in {@link DeviceConfig}. + * Flags in the {@link android.provider.DeviceConfig#NAMESPACE_WIDGET "widget" namespace}. * * @hide */ public final class WidgetFlags { /** - * Whether the cursor control feature set is enabled. - * TODO: Makes this flag key visible to webview/chrome. + * Whether starting a cursor drag from anywhere in the text should be enabled. */ - public static final String ENABLE_CURSOR_CONTROL = - "CursorControlFeature__enable_cursor_control"; + public static final String ENABLE_CURSOR_DRAG_FROM_ANYWHERE = + "CursorControlFeature__enable_cursor_drag_from_anywhere"; /** - * The key name used in app core settings for enable cursor control. + * The key used in app core settings for the flag {@link #ENABLE_CURSOR_DRAG_FROM_ANYWHERE}. */ - public static final String KEY_ENABLE_CURSOR_CONTROL = "widget__enable_cursor_control"; + public static final String KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE = + "widget__enable_cursor_drag_from_anywhere"; + + /** + * Default value for the flag {@link #ENABLE_CURSOR_DRAG_FROM_ANYWHERE}. + */ + public static final boolean ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT = true; + + /** + * Whether additional gestures should be enabled for the insertion cursor handle (e.g. + * long-press or double-tap on the handle to trigger selection). + */ + public static final String ENABLE_INSERTION_HANDLE_GESTURES = + "CursorControlFeature__enable_insertion_handle_gestures"; + + /** + * The key used in app core settings for the flag {@link #ENABLE_INSERTION_HANDLE_GESTURES}. + */ + public static final String KEY_ENABLE_INSERTION_HANDLE_GESTURES = + "widget__enable_insertion_handle_gestures"; + + /** + * Default value for the flag {@link #ENABLE_INSERTION_HANDLE_GESTURES}. + */ + public static final boolean ENABLE_INSERTION_HANDLE_GESTURES_DEFAULT = false; /** * The flag of delta height applies to the insertion handle when cursor control flag is enabled. - * The default value is 25. */ public static final String INSERTION_HANDLE_DELTA_HEIGHT = "CursorControlFeature__insertion_handle_delta_height"; @@ -49,8 +71,13 @@ public final class WidgetFlags { "widget__insertion_handle_delta_height"; /** + * Default value for the flag {@link #INSERTION_HANDLE_DELTA_HEIGHT}. + */ + public static final int INSERTION_HANDLE_DELTA_HEIGHT_DEFAULT = 25; + + /** * The flag of opacity applies to the insertion handle when cursor control flag is enabled. - * The opacity value is in the range of {0..100}. The default value is 50. + * The opacity value is in the range of {0..100}. */ public static final String INSERTION_HANDLE_OPACITY = "CursorControlFeature__insertion_handle_opacity"; @@ -62,6 +89,11 @@ public final class WidgetFlags { "widget__insertion_handle_opacity"; /** + * Default value for the flag {@link #INSERTION_HANDLE_OPACITY}. + */ + public static final int INSERTION_HANDLE_OPACITY_DEFAULT = 50; + + /** * The flag of enabling the new magnifier. */ public static final String ENABLE_NEW_MAGNIFIER = "CursorControlFeature__enable_new_magnifier"; @@ -72,8 +104,12 @@ public final class WidgetFlags { public static final String KEY_ENABLE_NEW_MAGNIFIER = "widget__enable_new_magnifier"; /** + * Default value for the flag {@link #ENABLE_NEW_MAGNIFIER}. + */ + public static final boolean ENABLE_NEW_MAGNIFIER_DEFAULT = false; + + /** * The flag of zoom factor applies to the new magnifier. - * The default value is 1.5f. */ public static final String MAGNIFIER_ZOOM_FACTOR = "CursorControlFeature__magnifier_zoom_factor"; @@ -84,8 +120,12 @@ public final class WidgetFlags { public static final String KEY_MAGNIFIER_ZOOM_FACTOR = "widget__magnifier_zoom_factor"; /** + * Default value for the flag {@link #MAGNIFIER_ZOOM_FACTOR}. + */ + public static final float MAGNIFIER_ZOOM_FACTOR_DEFAULT = 1.5f; + + /** * The flag of aspect ratio (width/height) applies to the new magnifier. - * The default value is 5.5f. */ public static final String MAGNIFIER_ASPECT_RATIO = "CursorControlFeature__magnifier_aspect_ratio"; @@ -95,6 +135,11 @@ public final class WidgetFlags { */ public static final String KEY_MAGNIFIER_ASPECT_RATIO = "widget__magnifier_aspect_ratio"; + /** + * Default value for the flag {@link #MAGNIFIER_ASPECT_RATIO}. + */ + public static final float MAGNIFIER_ASPECT_RATIO_DEFAULT = 5.5f; + private WidgetFlags() { } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index a201a335622a..250b1ea5a4e9 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -1805,6 +1805,10 @@ public class ChooserActivity extends ResolverActivity implements if (!TextUtils.isEmpty(dataString)) { return new IntentFilter(intent.getAction(), dataString); } + if (intent.getType() == null) { + Log.e(TAG, "Failed to get target intent filter: intent data and type are null"); + return null; + } IntentFilter intentFilter = new IntentFilter(intent.getAction(), intent.getType()); List<Uri> contentUris = new ArrayList<>(); if (Intent.ACTION_SEND.equals(intent.getAction())) { @@ -1825,7 +1829,7 @@ public class ChooserActivity extends ResolverActivity implements } return intentFilter; } catch (Exception e) { - Log.e(TAG, "failed to get target intent filter", e); + Log.e(TAG, "Failed to get target intent filter", e); return null; } } diff --git a/core/java/com/android/internal/content/om/TEST_MAPPING b/core/java/com/android/internal/content/om/TEST_MAPPING new file mode 100644 index 000000000000..4cb595b01681 --- /dev/null +++ b/core/java/com/android/internal/content/om/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "com.android.internal.content." + } + ] + } + ] +}
\ No newline at end of file diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 3db8f4ed9408..add2304afe9d 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -393,6 +393,9 @@ public class PowerProfile { } public int getNumCoresInCpuCluster(int cluster) { + if (cluster < 0 || cluster >= mCpuClusters.length) { + return 0; // index out of bound + } return mCpuClusters[cluster].numCpus; } diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING new file mode 100644 index 000000000000..f44b9fb7e723 --- /dev/null +++ b/core/java/com/android/internal/os/TEST_MAPPING @@ -0,0 +1,30 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "com.android.internal.os.KernelCpuUidFreqTimeReaderTest" + }, + { + "include-filter": "com.android.internal.os.KernelCpuUidActiveTimeReaderTest" + }, + { + "include-filter": "com.android.internal.os.KernelCpuUidClusterTimeReaderTest" + }, + { + "include-filter": "com.android.internal.os.KernelSingleUidTimeReaderTest" + }, + { + "include-filter": "com.android.internal.os.KernelCpuUidBpfMapReaderTest" + } + + ], + "file_patterns": [ + "KernelCpuUidTimeReader\\.java", + "KernelCpuUidBpfMapReader\\.java", + "KernelSingleUidTimeReader\\.java" + ] + } + ] +} diff --git a/core/java/com/android/internal/util/function/LongObjPredicate.java b/core/java/com/android/internal/util/function/LongObjPredicate.java new file mode 100644 index 000000000000..9e4630744809 --- /dev/null +++ b/core/java/com/android/internal/util/function/LongObjPredicate.java @@ -0,0 +1,35 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.util.function; + +/** + * Represents a predicate (boolean-valued function) of a {@code long}-valued argument + * and an object-valued argument. + * + * @param <T> the type of the object-valued argument to the predicate + */ +@FunctionalInterface +public interface LongObjPredicate<T> { + /** + * Evaluates this predicate on the given arguments. + * + * @param value the first input argument + * @param t the second input argument + * @return {@code true} if the input arguments match the predicate, + * otherwise {@code false} + */ + boolean test(long value, T t); +} diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java index 69b1609377b7..633d6848030e 100644 --- a/core/java/com/android/internal/view/FloatingActionMode.java +++ b/core/java/com/android/internal/view/FloatingActionMode.java @@ -210,7 +210,7 @@ public final class FloatingActionMode extends ActionMode { } private boolean isContentRectWithinBounds() { - mContext.getDisplay().getRealSize(mDisplaySize); + mContext.getDisplayNoVerify().getRealSize(mDisplaySize); mScreenRect.set(0, 0, mDisplaySize.x, mDisplaySize.y); return intersectsClosed(mContentRectOnScreen, mScreenRect) diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl index 41f902e2f852..45090320c192 100644 --- a/core/java/com/android/internal/view/IInputMethodClient.aidl +++ b/core/java/com/android/internal/view/IInputMethodClient.aidl @@ -28,6 +28,7 @@ oneway interface IInputMethodClient { void onBindMethod(in InputBindResult res); void onUnbindMethod(int sequence, int unbindReason); void setActive(boolean active, boolean fullscreen); + void scheduleStartInputIfNecessary(boolean fullscreen); void reportFullscreenMode(boolean fullscreen); void reportPreRendered(in EditorInfo info); void applyImeVisibility(boolean setVisible); diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java index a5964b509b3c..f29e95ccdf65 100644 --- a/core/java/com/android/internal/view/InputBindResult.java +++ b/core/java/com/android/internal/view/InputBindResult.java @@ -89,57 +89,64 @@ public final class InputBindResult implements Parcelable { */ int SUCCESS_WAITING_IME_BINDING = 2; /** + * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} has a + * pending operation to switch to a different user. + * + * <p>Note that in this state even what would be the next current IME is not determined.</p> + */ + int SUCCESS_WAITING_USER_SWITCHING = 3; + /** * Indicates that this is not intended for starting input but just for reporting window * focus change from the application process. * * <p>All other fields do not have meaningful value.</p> */ - int SUCCESS_REPORT_WINDOW_FOCUS_ONLY = 3; + int SUCCESS_REPORT_WINDOW_FOCUS_ONLY = 4; /** * Indicates somehow * {@link * com.android.server.inputmethod.InputMethodManagerService#startInputOrWindowGainedFocus} * is trying to return null {@link InputBindResult}, which must never happen. */ - int ERROR_NULL = 4; + int ERROR_NULL = 5; /** * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} * recognizes no IME. */ - int ERROR_NO_IME = 5; + int ERROR_NO_IME = 6; /** * Indicates that {@link android.view.inputmethod.EditorInfo#packageName} does not match * the caller UID. * * @see android.view.inputmethod.EditorInfo#packageName */ - int ERROR_INVALID_PACKAGE_NAME = 6; + int ERROR_INVALID_PACKAGE_NAME = 7; /** * Indicates that the system is still in an early stage of the boot process and any 3rd * party application is not allowed to run. * * @see com.android.server.SystemService#PHASE_THIRD_PARTY_APPS_CAN_START */ - int ERROR_SYSTEM_NOT_READY = 7; + int ERROR_SYSTEM_NOT_READY = 8; /** * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} tried to * connect to an {@link android.inputmethodservice.InputMethodService} but failed. * * @see android.content.Context#bindServiceAsUser(Intent, ServiceConnection, int, UserHandle) */ - int ERROR_IME_NOT_CONNECTED = 8; + int ERROR_IME_NOT_CONNECTED = 9; /** * Indicates that the caller is not the foreground user, does not have * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission, or the user * specified in {@link android.view.inputmethod.EditorInfo#targetInputMethodUser} is not * running. */ - int ERROR_INVALID_USER = 9; + int ERROR_INVALID_USER = 10; /** * Indicates that the caller should have specified non-null * {@link android.view.inputmethod.EditorInfo}. */ - int ERROR_NULL_EDITOR_INFO = 10; + int ERROR_NULL_EDITOR_INFO = 11; /** * Indicates that the target window the client specified cannot be the IME target right now. * @@ -149,24 +156,24 @@ public final class InputBindResult implements Parcelable { * * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int, int) */ - int ERROR_NOT_IME_TARGET_WINDOW = 11; + int ERROR_NOT_IME_TARGET_WINDOW = 12; /** * Indicates that focused view in the current window is not an editor. */ - int ERROR_NO_EDITOR = 12; + int ERROR_NO_EDITOR = 13; /** * Indicates that there is a mismatch in display ID between IME client and focused Window. */ - int ERROR_DISPLAY_ID_MISMATCH = 13; + int ERROR_DISPLAY_ID_MISMATCH = 14; /** * Indicates that current IME client is no longer allowed to access to the associated * display. */ - int ERROR_INVALID_DISPLAY_ID = 14; + int ERROR_INVALID_DISPLAY_ID = 15; /** * Indicates that the client is not recognized by the system. */ - int ERROR_INVALID_CLIENT = 15; + int ERROR_INVALID_CLIENT = 16; } @ResultCode @@ -299,6 +306,8 @@ public final class InputBindResult implements Parcelable { return "SUCCESS_WAITING_IME_SESSION"; case ResultCode.SUCCESS_WAITING_IME_BINDING: return "SUCCESS_WAITING_IME_BINDING"; + case ResultCode.SUCCESS_WAITING_USER_SWITCHING: + return "SUCCESS_WAITING_USER_SWITCHING"; case ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY: return "SUCCESS_REPORT_WINDOW_FOCUS_ONLY"; case ResultCode.ERROR_NULL: @@ -386,4 +395,11 @@ public final class InputBindResult implements Parcelable { * Predefined error object for {@link ResultCode#ERROR_INVALID_CLIENT}. */ public static final InputBindResult INVALID_CLIENT = error(ResultCode.ERROR_INVALID_CLIENT); + + /** + * Predefined <strong>success</strong> object for + * {@link ResultCode#SUCCESS_WAITING_USER_SWITCHING}. + */ + public static final InputBindResult USER_SWITCHING = + error(ResultCode.SUCCESS_WAITING_USER_SWITCHING); } diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java index 13425e5e9bec..cfb2bf9df9ed 100644 --- a/core/java/com/android/internal/view/RotationPolicy.java +++ b/core/java/com/android/internal/view/RotationPolicy.java @@ -77,10 +77,7 @@ public final class RotationPolicy { final Point size = new Point(); final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); try { - final Display display = context.getDisplay(); - final int displayId = display != null - ? display.getDisplayId() - : Display.DEFAULT_DISPLAY; + final int displayId = context.getDisplayId(); wm.getInitialDisplaySize(displayId, size); return size.x < size.y ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; diff --git a/core/jni/android_os_storage_StorageManager.cpp b/core/jni/android_os_storage_StorageManager.cpp index aee6733ecf53..fd3e66b1bbce 100644 --- a/core/jni/android_os_storage_StorageManager.cpp +++ b/core/jni/android_os_storage_StorageManager.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "StorageManager" +#include <android-base/file.h> #include <android-base/logging.h> #include <android-base/unique_fd.h> #include <fcntl.h> @@ -25,11 +26,30 @@ namespace android { +static const char* kProcFilesystems = "/proc/filesystems"; + +// Checks whether the passed in filesystem is listed in /proc/filesystems +static bool IsFilesystemSupported(const std::string& fsType) { + std::string supported; + if (!android::base::ReadFileToString(kProcFilesystems, &supported)) { + PLOG(ERROR) << "Failed to read supported filesystems"; + return false; + } + return supported.find(fsType + "\n") != std::string::npos; +} + jboolean android_os_storage_StorageManager_setQuotaProjectId(JNIEnv* env, jobject self, jstring path, jlong projectId) { struct fsxattr fsx; ScopedUtfChars utf_chars_path(env, path); + static bool sdcardFsSupported = IsFilesystemSupported("sdcardfs"); + if (sdcardFsSupported) { + // sdcardfs doesn't support project ID quota tracking and takes care of quota + // in a different way. + return JNI_TRUE; + } + if (projectId > UINT32_MAX) { LOG(ERROR) << "Invalid project id: " << projectId; return JNI_FALSE; diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 27c5a7302ba4..93449ffeae1b 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -186,6 +186,7 @@ static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz, proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer); } proxy->setSurface(window, enableTimeout); + ANativeWindow_release(window); } static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz, diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h index f03f42737134..8bb4d503cc82 100644 --- a/core/jni/core_jni_helpers.h +++ b/core/jni/core_jni_helpers.h @@ -47,28 +47,32 @@ static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) { static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, const char* field_signature) { jfieldID res = env->GetFieldID(clazz, field_name, field_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name, + field_signature); return res; } static inline jmethodID GetMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, const char* method_signature) { jmethodID res = env->GetMethodID(clazz, method_name, method_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s", method_name); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s with signature %s", method_name, + method_signature); return res; } static inline jfieldID GetStaticFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, const char* field_signature) { jfieldID res = env->GetStaticFieldID(clazz, field_name, field_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name, + field_signature); return res; } static inline jmethodID GetStaticMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, const char* method_signature) { jmethodID res = env->GetStaticMethodID(clazz, method_name, method_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s", method_name); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s with signature %s", + method_name, method_signature); return res; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ddfc4b8e94a4..814b8acd63ac 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3084,8 +3084,7 @@ <!-- Allows SystemUI to request third party controls. <p>Should only be requested by the System and required by - ControlsService declarations. - @hide + {@link android.service.controls.ControlsProviderService} declarations. --> <permission android:name="android.permission.BIND_CONTROLS" android:protectionLevel="signature" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 6ca50811115e..68c86b263a75 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2460,6 +2460,12 @@ rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max --> <string name="config_ethernet_tcp_buffers" translatable="false">524288,1048576,3145728,524288,1048576,2097152</string> + <!-- What source to use to estimate link upstream and downstream bandwidth capacities. + Default is carrier_config, but it should be set to modem if the modem is returning + predictive (instead of instantaneous) bandwidth estimate. + Values are carrier_config and modem. --> + <string name="config_bandwidthEstimateSource">carrier_config</string> + <!-- Whether WiFi display is supported by this device. There are many prerequisites for this feature to work correctly. Here are a few of them: diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 85c2a2a24749..1c9e2cd72f28 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -473,6 +473,7 @@ <java-symbol type="integer" name="config_num_physical_slots" /> <java-symbol type="array" name="config_integrityRuleProviderPackages" /> <java-symbol type="bool" name="config_useAssistantVolume" /> + <java-symbol type="string" name="config_bandwidthEstimateSource" /> <java-symbol type="color" name="tab_indicator_text_v4" /> diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml index 899d630a51b7..d8ec72f33ddc 100644 --- a/core/res/res/xml/power_profile.xml +++ b/core/res/res/xml/power_profile.xml @@ -51,6 +51,12 @@ <value>0.1</value> <!-- ~1mA --> </array> + <!-- Additional power consumption by CPU excluding cluster and core when + running --> + <array name="cpu.active"> + <value>0.1</value> + </array> + <!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the number of CPU cores for that cluster. diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java index 78c4420b9496..1737bd0fa20b 100644 --- a/core/tests/coretests/src/android/content/ContentResolverTest.java +++ b/core/tests/coretests/src/android/content/ContentResolverTest.java @@ -234,4 +234,12 @@ public class ContentResolverTest { assertThat(type).isNull(); assertThat(end).isLessThan(start + 5000); } + + @Test + public void testCanonicalize() { + Uri canonical = mResolver.canonicalize( + Uri.parse("content://android.content.FakeProviderRemote/something")); + assertThat(canonical).isEqualTo( + Uri.parse("content://android.content.FakeProviderRemote/canonical")); + } } diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java index f074233070d1..f0997a68492b 100644 --- a/core/tests/coretests/src/android/content/ContextTest.java +++ b/core/tests/coretests/src/android/content/ContextTest.java @@ -16,11 +16,13 @@ package android.content; +import static android.view.Display.DEFAULT_DISPLAY; + import static org.junit.Assert.assertEquals; import android.app.ActivityThread; +import android.hardware.display.DisplayManager; import android.os.UserHandle; -import android.view.WindowManager; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -45,17 +47,16 @@ public class ContextTest { final Context testContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - assertEquals(testContext.getDisplay().getDisplayId(), testContext.getDisplayId()); + assertEquals(testContext.getDisplayNoVerify().getDisplayId(), testContext.getDisplayId()); } - // TODO(b/128338354): Re-visit this test after introducing WindowContext @Test public void testDisplayIdForDefaultDisplayContext() { final Context testContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - final WindowManager wm = testContext.getSystemService(WindowManager.class); + final DisplayManager dm = testContext.getSystemService(DisplayManager.class); final Context defaultDisplayContext = - testContext.createDisplayContext(wm.getDefaultDisplay()); + testContext.createDisplayContext(dm.getDisplay(DEFAULT_DISPLAY)); assertEquals(defaultDisplayContext.getDisplay().getDisplayId(), defaultDisplayContext.getDisplayId()); diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java index 7b9bdbcd7a17..1d7ba5d9be46 100644 --- a/core/tests/coretests/src/android/content/FakeProviderRemote.java +++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java @@ -54,4 +54,10 @@ public class FakeProviderRemote extends ContentProvider { public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } + + @Override + public Uri canonicalize(Uri uri) { + return new Uri.Builder().scheme(uri.getScheme()).authority(uri.getAuthority()) + .appendPath("canonical").build(); + } } diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java index 78c88d71d953..4c2ca7eefcc1 100644 --- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java +++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java @@ -25,9 +25,13 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.Manifest; import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; import android.content.IIntentSender; import android.content.Intent; +import android.content.res.Resources; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -44,6 +48,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -59,6 +64,11 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) public class ControlProviderServiceTest { + private static final ComponentName TEST_SYSUI_COMPONENT = + ComponentName.unflattenFromString("sysui/.test.cls"); + private static final ComponentName TEST_COMPONENT = + ComponentName.unflattenFromString("test.pkg/.test.cls"); + private IBinder mToken = new Binder(); @Mock private IControlsActionCallback.Stub mActionCallback; @@ -66,6 +76,12 @@ public class ControlProviderServiceTest { private IControlsSubscriber.Stub mSubscriber; @Mock private IIntentSender mIIntentSender; + @Mock + private Resources mResources; + @Mock + private Context mContext; + @Captor + private ArgumentCaptor<Intent> mIntentArgumentCaptor; private PendingIntent mPendingIntent; private FakeControlsProviderService mControlsProviderService; @@ -81,6 +97,10 @@ public class ControlProviderServiceTest { when(mSubscriber.asBinder()).thenCallRealMethod(); when(mSubscriber.queryLocalInterface(any())).thenReturn(mSubscriber); + when(mResources.getString(com.android.internal.R.string.config_systemUIServiceComponent)) + .thenReturn(TEST_SYSUI_COMPONENT.flattenToString()); + when(mContext.getResources()).thenReturn(mResources); + Bundle b = new Bundle(); b.putBinder(ControlsProviderService.CALLBACK_TOKEN, mToken); Intent intent = new Intent(); @@ -223,6 +243,21 @@ public class ControlProviderServiceTest { ControlAction.RESPONSE_OK); } + @Test + public void testRequestAdd() { + Control control = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build(); + ControlsProviderService.requestAddControl(mContext, TEST_COMPONENT, control); + + verify(mContext).sendBroadcast(mIntentArgumentCaptor.capture(), + eq(Manifest.permission.BIND_CONTROLS)); + Intent intent = mIntentArgumentCaptor.getValue(); + assertEquals(ControlsProviderService.ACTION_ADD_CONTROL, intent.getAction()); + assertEquals(TEST_SYSUI_COMPONENT.getPackageName(), intent.getPackage()); + assertEquals(TEST_COMPONENT, intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME)); + assertTrue(equals(control, + intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL))); + } + private static boolean equals(Control c1, Control c2) { if (c1 == c2) return true; if (c1 == null || c2 == null) return false; diff --git a/core/tests/coretests/src/android/util/LongSparseArrayTest.java b/core/tests/coretests/src/android/util/LongSparseArrayTest.java new file mode 100644 index 000000000000..bf3f0f50f4bf --- /dev/null +++ b/core/tests/coretests/src/android/util/LongSparseArrayTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.util; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.internal.util.function.LongObjPredicate; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link LongSparseArray}. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class LongSparseArrayTest { + @Test + public void testRemoveIf() { + final LongSparseArray<Integer> sparseArray = new LongSparseArray(); + for (int i = 0; i < 10; ++i) { + for (int j = 100; j < 110; ++j) { + sparseArray.put(i, j); + sparseArray.put(-i, j); + sparseArray.put(j, -i); + sparseArray.put(-j, -i); + } + } + + final LongObjPredicate<Integer> predicate = (value, obj) -> (value < 0 && obj < 0); + sparseArray.removeIf(predicate); + + for (int i = 0; i < sparseArray.size(); ++i) { + assertThat(predicate.test(sparseArray.keyAt(i), sparseArray.valueAt(i))) + .isFalse(); + } + } +} diff --git a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java index b41f90c4689e..7872810717b8 100644 --- a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java +++ b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java @@ -21,10 +21,27 @@ import static com.google.common.truth.Truth.assertThat; import static org.testng.Assert.assertThrows; import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +/** + * Tests for {@link CutoutSpecification} used by {@link DisplayCutout}. + * + * <p>Build/Install/Run: + * atest FrameworksCoreTests:CutoutSpecificationTest + * + * <p>This test class is a part of Window Manager Service tests and specified in + * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit public class CutoutSpecificationTest { private static final String WITHOUT_BIND_CUTOUT_SPECIFICATION = "M 0,0\n" + "h 48\n" @@ -344,7 +361,7 @@ public class CutoutSpecificationTest { .parse("@bottom" + "M 0,0\n" + "v -10\n" - + "h 10\n" + + "h -10\n" + "v 10\n" + "z\n" + "@right\n" diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java index 7c78bce25b1c..7f0e0d2f54c7 100644 --- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java @@ -61,7 +61,7 @@ public class ImeInsetsSourceConsumerTest { .setName("testSurface") .build(); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { - ViewRootImpl viewRootImpl = new ViewRootImpl(mContext, mContext.getDisplay()); + ViewRootImpl viewRootImpl = new ViewRootImpl(mContext, mContext.getDisplayNoVerify()); try { viewRootImpl.setView(new TextView(mContext), new LayoutParams(), null); } catch (BadTokenException e) { diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 24fe2a0a1823..7737b1a2a776 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -110,7 +110,7 @@ public class InsetsControllerTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { Context context = InstrumentationRegistry.getTargetContext(); // cannot mock ViewRootImpl since it's final. - mViewRoot = new ViewRootImpl(context, context.getDisplay()); + mViewRoot = new ViewRootImpl(context, context.getDisplayNoVerify()); try { mViewRoot.setView(new TextView(context), new LayoutParams(), null); } catch (BadTokenException e) { diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index 5e9e2f0065ed..754c6791cade 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -77,7 +77,8 @@ public class InsetsSourceConsumerTest { instrumentation.runOnMainSync(() -> { final Context context = instrumentation.getTargetContext(); // cannot mock ViewRootImpl since it's final. - final ViewRootImpl viewRootImpl = new ViewRootImpl(context, context.getDisplay()); + final ViewRootImpl viewRootImpl = new ViewRootImpl(context, + context.getDisplayNoVerify()); try { viewRootImpl.setView(new TextView(context), new LayoutParams(), null); } catch (BadTokenException e) { diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index df6ed8c3fe0d..e2adbcc600d7 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -61,7 +61,7 @@ public class ViewRootImplTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mViewRootImpl = new ViewRootImplAccessor( - new ViewRootImpl(mContext, mContext.getDisplay())); + new ViewRootImpl(mContext, mContext.getDisplayNoVerify())); }); } diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java index d7abfcc7d6d2..0a094c61d4d5 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java @@ -50,7 +50,6 @@ import com.android.frameworks.coretests.R; import com.google.common.base.Strings; -import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -69,7 +68,6 @@ public class EditorCursorDragTest { public ActivityTestRule<TextViewActivity> mActivityRule = new ActivityTestRule<>( TextViewActivity.class); - private boolean mOriginalFlagValue; private Instrumentation mInstrumentation; private Activity mActivity; @@ -77,13 +75,6 @@ public class EditorCursorDragTest { public void before() throws Throwable { mInstrumentation = InstrumentationRegistry.getInstrumentation(); mActivity = mActivityRule.getActivity(); - mOriginalFlagValue = Editor.FLAG_ENABLE_CURSOR_DRAG; - Editor.FLAG_ENABLE_CURSOR_DRAG = true; - } - - @After - public void after() throws Throwable { - Editor.FLAG_ENABLE_CURSOR_DRAG = mOriginalFlagValue; } @Test diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index 0c38e7136655..88a6f9e4af4b 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -31,8 +31,8 @@ import static android.widget.espresso.TextViewActions.doubleTapAndDragHandle; import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText; import static android.widget.espresso.TextViewActions.doubleTapHandle; import static android.widget.espresso.TextViewActions.dragHandle; -import static android.widget.espresso.TextViewActions.longPressAndDragOnText; import static android.widget.espresso.TextViewActions.longPressAndDragHandle; +import static android.widget.espresso.TextViewActions.longPressAndDragOnText; import static android.widget.espresso.TextViewActions.longPressHandle; import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex; import static android.widget.espresso.TextViewAssertions.doesNotHaveStyledText; @@ -514,29 +514,26 @@ public class TextViewActivityTest { onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f"))); } - @Test - public void testInsertionHandle_touchThrough() { + private void enableFlagsForInsertionHandleGestures() { final TextView textView = mActivity.findViewById(R.id.textview); - boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled(); - boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG; - textView.getEditorForTesting().setCursorControlEnabled(true); - Editor.FLAG_ENABLE_CURSOR_DRAG = true; + final Editor editor = textView.getEditorForTesting(); + editor.setFlagCursorDragFromAnywhereEnabled(true); + editor.setFlagInsertionHandleGesturesEnabled(true); + // Note: We don't need to reset these flags explicitly at the end of each test, because a + // fresh TextView and Editor will be created for each test. + } + @Test + public void testInsertionHandle_touchThrough() { + enableFlagsForInsertionHandleGestures(); testInsertionHandle(); testInsertionHandle_multiLine(); - - textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled); - Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled; } @Test public void testInsertionHandle_longPressToSelect() { - // This test only makes sense when Cursor Control flag is enabled. + enableFlagsForInsertionHandleGestures(); final TextView textView = mActivity.findViewById(R.id.textview); - boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled(); - boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG; - textView.getEditorForTesting().setCursorControlEnabled(true); - Editor.FLAG_ENABLE_CURSOR_DRAG = true; final String text = "hello the world"; onView(withId(R.id.textview)).perform(replaceText(text)); @@ -546,20 +543,12 @@ public class TextViewActivityTest { onHandleView(com.android.internal.R.id.insertion_handle).perform(longPressHandle(textView)); onView(withId(R.id.textview)).check(hasSelection("world")); - - textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled); - Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled; } @Test public void testInsertionHandle_longPressAndDragToSelect() { - // This test only makes sense when Cursor Control flag is enabled. + enableFlagsForInsertionHandleGestures(); final TextView textView = mActivity.findViewById(R.id.textview); - boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled(); - boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG; - textView.getEditorForTesting().setCursorControlEnabled(true); - Editor.FLAG_ENABLE_CURSOR_DRAG = true; - final String text = "hello the world"; onView(withId(R.id.textview)).perform(replaceText(text)); @@ -569,19 +558,12 @@ public class TextViewActivityTest { onHandleView(com.android.internal.R.id.insertion_handle) .perform(longPressAndDragHandle(textView, Handle.INSERTION, text.indexOf('t'))); onView(withId(R.id.textview)).check(hasSelection("the world")); - - textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled); - Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled; } @Test public void testInsertionHandle_doubleTapToSelect() { - // This test only makes sense when Cursor Control flag is enabled. + enableFlagsForInsertionHandleGestures(); final TextView textView = mActivity.findViewById(R.id.textview); - boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled(); - boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG; - textView.getEditorForTesting().setCursorControlEnabled(true); - Editor.FLAG_ENABLE_CURSOR_DRAG = true; final String text = "hello the world"; onView(withId(R.id.textview)).perform(replaceText(text)); @@ -591,19 +573,12 @@ public class TextViewActivityTest { onHandleView(com.android.internal.R.id.insertion_handle).perform(doubleTapHandle(textView)); onView(withId(R.id.textview)).check(hasSelection("world")); - - textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled); - Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled; } @Test public void testInsertionHandle_doubleTapAndDragToSelect() { - // This test only makes sense when Cursor Control flag is enabled. + enableFlagsForInsertionHandleGestures(); final TextView textView = mActivity.findViewById(R.id.textview); - boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled(); - boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG; - textView.getEditorForTesting().setCursorControlEnabled(true); - Editor.FLAG_ENABLE_CURSOR_DRAG = true; final String text = "hello the world"; onView(withId(R.id.textview)).perform(replaceText(text)); @@ -614,9 +589,6 @@ public class TextViewActivityTest { onHandleView(com.android.internal.R.id.insertion_handle) .perform(doubleTapAndDragHandle(textView, Handle.INSERTION, text.indexOf('t'))); onView(withId(R.id.textview)).check(hasSelection("the world")); - - textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled); - Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled; } @Test diff --git a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java index cdcf23fc10bb..3e40466e4b64 100644 --- a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java +++ b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java @@ -53,7 +53,10 @@ public final class DecorContextTest { @Test public void testDecorContextWithDefaultDisplay() { - DecorContext context = new DecorContext(mContext.getApplicationContext(), mContext); + Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(), DEFAULT_DISPLAY, + new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); + DecorContext context = new DecorContext(mContext.getApplicationContext(), + mContext.createDisplayContext(defaultDisplay)); assertDecorContextDisplay(DEFAULT_DISPLAY, context); } diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 914c04645289..56d951cdb338 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -150,11 +150,7 @@ sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, sk_sp<SkColorS AHardwareBuffer_Desc bufferDesc; AHardwareBuffer_describe(hardwareBuffer, &bufferDesc); SkImageInfo info = uirenderer::BufferDescriptionToImageInfo(bufferDesc, colorSpace); - - // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer) - const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width; - const size_t rowBytes = info.bytesPerPixel() * bufferStride; - return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette)); + return createFrom(hardwareBuffer, info, bufferDesc, palette); } sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, SkColorType colorType, @@ -164,8 +160,14 @@ sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, SkColorType co AHardwareBuffer_describe(hardwareBuffer, &bufferDesc); SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType, alphaType, colorSpace); + return createFrom(hardwareBuffer, info, bufferDesc, palette); +} - const size_t rowBytes = info.bytesPerPixel() * bufferDesc.stride; +sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info, + const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette) { + // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer) + const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width; + const size_t rowBytes = info.bytesPerPixel() * bufferStride; return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette)); } #endif diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 3bfb7800f735..b8b59947a57b 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -169,6 +169,12 @@ private: #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes, BitmapPalette palette); + + // Common code for the two public facing createFrom(AHardwareBuffer*, ...) + // methods. + // bufferDesc is only used to compute rowBytes. + static sk_sp<Bitmap> createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info, + const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette); #endif virtual ~Bitmap(); diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp index 027a7488f723..e752b55f5ef7 100644 --- a/libs/usb/Android.bp +++ b/libs/usb/Android.bp @@ -19,5 +19,3 @@ java_sdk_library { srcs: ["src/**/*.java"], api_packages: ["com.android.future.usb"], } - -subdirs = ["tests/*"] diff --git a/libs/usb/tests/AccessoryChat/Android.bp b/libs/usb/tests/AccessoryChat/Android.bp index 63a670c67bfc..19ed3d3ef52e 100644 --- a/libs/usb/tests/AccessoryChat/Android.bp +++ b/libs/usb/tests/AccessoryChat/Android.bp @@ -1,4 +1,3 @@ -subdirs = ["accessorychat"] // // Copyright (C) 2011 The Android Open Source Project // diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java index 085602cbcd4f..6006d5079b07 100644 --- a/location/java/android/location/LocationManagerInternal.java +++ b/location/java/android/location/LocationManagerInternal.java @@ -27,22 +27,6 @@ import android.annotation.NonNull; public abstract class LocationManagerInternal { /** - * Requests that a provider change its allowed state. A provider may or may not honor this - * request, and if the provider does change its state as a result, that may happen - * asynchronously after some delay. - * - * <p>Setting a provider's state to allowed implies that any consents or terms and conditions - * that may be necessary to allow the provider are agreed to. Setting a providers state to - * disallowed implies that any consents or terms and conditions have their agreement revoked. - * - * @param provider A location provider as listed by {@link LocationManager#getAllProviders()} - * @param allowed Whether the location provider is being requested to allow or disallow - * itself - * @throws IllegalArgumentException if provider is null - */ - public abstract void requestSetProviderAllowed(@NonNull String provider, boolean allowed); - - /** * Returns true if the given provider is enabled for the given user. * * @param provider A location provider as listed by {@link LocationManager#getAllProviders()} diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl index b7817ff1e1fc..4246c6cd1004 100644 --- a/location/java/com/android/internal/location/ILocationProvider.aidl +++ b/location/java/com/android/internal/location/ILocationProvider.aidl @@ -37,6 +37,4 @@ interface ILocationProvider { @UnsupportedAppUsage oneway void sendExtraCommand(String command, in Bundle extras); - - oneway void requestSetAllowed(boolean allowed); } diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt index 49fcaabe981a..9cc30d0d751e 100644 --- a/location/lib/api/current.txt +++ b/location/lib/api/current.txt @@ -17,7 +17,6 @@ package com.android.location.provider { method @Deprecated protected int onGetStatus(android.os.Bundle); method @Deprecated protected long onGetStatusUpdateTime(); method protected void onInit(); - method protected void onRequestSetAllowed(boolean); method protected boolean onSendExtraCommand(@Nullable String, @Nullable android.os.Bundle); method protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource); method public void reportLocation(android.location.Location); diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java index bd29d8ac2a85..d3fb58fe257e 100644 --- a/location/lib/java/com/android/location/provider/LocationProviderBase.java +++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java @@ -315,17 +315,6 @@ public abstract class LocationProviderBase { return false; } - /** - * Invoked when the system wishes to request that the provider sets its allowed state as - * desired. This implies that the caller is providing/retracting consent for any terms and - * conditions or consents associated with the provider. - * - * <p>It is generally only necessary to override this function if the provider has some barriers - * or gates for enabling/disabling itself, in which case this function should handle those - * appropriately. A provider that is always allowed has no need to override this function. - */ - protected void onRequestSetAllowed(boolean allowed) {} - private final class Service extends ILocationProvider.Stub { @Override @@ -356,10 +345,5 @@ public abstract class LocationProviderBase { public void sendExtraCommand(String command, Bundle extras) { onSendExtraCommand(command, extras); } - - @Override - public void requestSetAllowed(boolean allowed) { - onRequestSetAllowed(allowed); - } } } diff --git a/media/java/android/media/DrmInitData.java b/media/java/android/media/DrmInitData.java index cc35f140dd7e..d803311fdae3 100644 --- a/media/java/android/media/DrmInitData.java +++ b/media/java/android/media/DrmInitData.java @@ -37,7 +37,9 @@ public abstract class DrmInitData { * * @param schemeUuid The DRM scheme's UUID. * @return The initialization data for the scheme, or null if the scheme is not supported. + * @deprecated Use {@link #getSchemeInitDataCount} and {@link #getSchemeInitDataAt} instead. */ + @Deprecated public abstract SchemeInitData get(UUID schemeUuid); /** diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index abc7e0b7be0e..f2b4db1afdac 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -1959,7 +1959,7 @@ final public class MediaCodec { * If this codec is to be used with {@link LinearBlock} and/or {@link * GraphicBlock}, pass this flag. * <p> - * When this flag is set, the following APIs throw IllegalStateException. + * When this flag is set, the following APIs throw {@link IncompatibleWithBlockModelException}. * <ul> * <li>{@link #getInputBuffer} * <li>{@link #getInputImage} @@ -1986,6 +1986,12 @@ final public class MediaCodec { public @interface ConfigureFlag {} /** + * Thrown when the codec is configured for block model and an incompatible API is called. + */ + public class IncompatibleWithBlockModelException extends RuntimeException { + } + + /** * Configures a component. * * @param format The format of the input data (decoder) or the desired @@ -2526,7 +2532,7 @@ final public class MediaCodec { throws CryptoException { synchronized(mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } invalidateByteBuffer(mCachedInputBuffers, index); mDequeuedInputBuffers.remove(index); @@ -2778,7 +2784,7 @@ final public class MediaCodec { int flags) throws CryptoException { synchronized(mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } invalidateByteBuffer(mCachedInputBuffers, index); mDequeuedInputBuffers.remove(index); @@ -2813,7 +2819,7 @@ final public class MediaCodec { public final int dequeueInputBuffer(long timeoutUs) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } } int res = native_dequeueInputBuffer(timeoutUs); @@ -2848,7 +2854,7 @@ final public class MediaCodec { public boolean isMappable() { synchronized (mLock) { if (!mValid) { - throw new IllegalStateException(); + throw new IllegalStateException("The linear block is invalid"); } return mMappable; } @@ -2867,10 +2873,10 @@ final public class MediaCodec { public @NonNull ByteBuffer map() { synchronized (mLock) { if (!mValid) { - throw new IllegalStateException(); + throw new IllegalStateException("The linear block is invalid"); } if (!mMappable) { - throw new IllegalStateException(); + throw new IllegalStateException("The linear block is not mappable"); } if (mMapped == null) { mMapped = native_map(); @@ -2896,7 +2902,7 @@ final public class MediaCodec { public void recycle() { synchronized (mLock) { if (!mValid) { - throw new IllegalStateException(); + throw new IllegalStateException("The linear block is invalid"); } if (mMapped != null) { mMapped.setAccessible(false); @@ -3002,7 +3008,7 @@ final public class MediaCodec { public boolean isMappable() { synchronized (mLock) { if (!mValid) { - throw new IllegalStateException(); + throw new IllegalStateException("The graphic block is invalid"); } return mMappable; } @@ -3021,10 +3027,10 @@ final public class MediaCodec { public @NonNull Image map() { synchronized (mLock) { if (!mValid) { - throw new IllegalStateException(); + throw new IllegalStateException("The graphic block is invalid"); } if (!mMappable) { - throw new IllegalStateException(); + throw new IllegalStateException("The graphic block is not mappable"); } if (mMapped == null) { mMapped = native_map(); @@ -3050,7 +3056,7 @@ final public class MediaCodec { public void recycle() { synchronized (mLock) { if (!mValid) { - throw new IllegalStateException(); + throw new IllegalStateException("The graphic block is invalid"); } if (mMapped != null) { mMapped.close(); @@ -3127,8 +3133,9 @@ final public class MediaCodec { if (buffer == null) { buffer = new GraphicBlock(); } - if (width < 0 || height < 0) { - throw new IllegalArgumentException(); + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException( + "non-positive width or height: " + width + "x" + height); } synchronized (buffer.mLock) { buffer.native_obtain(width, height, format, usage, codecNames); @@ -3177,16 +3184,8 @@ final public class MediaCodec { * @param block The linear block object * @param offset The byte offset into the input buffer at which the data starts. * @param size The number of bytes of valid input data. - * @param presentationTimeUs The presentation timestamp in microseconds for this - * buffer. This is normally the media time at which this - * buffer should be presented (rendered). When using an output - * surface, this will be propagated as the {@link - * SurfaceTexture#getTimestamp timestamp} for the frame (after - * conversion to nanoseconds). - * @param flags A bitmask of flags - * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}. - * While not prohibited, most codecs do not use the - * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers. + * @param cryptoInfo Metadata describing the structure of the encrypted input sample. + * may be null if clear. * @return this object * @throws IllegalStateException if a buffer is already set */ @@ -3194,106 +3193,91 @@ final public class MediaCodec { @NonNull LinearBlock block, int offset, int size, - long presentationTimeUs, - @BufferFlag int flags) { + @Nullable MediaCodec.CryptoInfo cryptoInfo) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } if (mLinearBlock != null || mGraphicBlock != null) { - throw new IllegalStateException(); + throw new IllegalStateException("Cannot set block twice"); } mLinearBlock = block; mOffset = offset; mSize = size; - mPresentationTimeUs = presentationTimeUs; - mFlags = flags; + mCryptoInfo = cryptoInfo; return this; } /** - * Set an encrypted linear block to this queue request. Exactly one - * buffer must be set for a queue request before calling {@link #queue}. + * Set a graphic block to this queue request. Exactly one buffer must + * be set for a queue request before calling {@link #queue}. * - * @param block The linear block object - * @param offset The byte offset into the input buffer at which the data starts. - * @param presentationTimeUs The presentation timestamp in microseconds for this - * buffer. This is normally the media time at which this - * buffer should be presented (rendered). When using an output - * surface, this will be propagated as the {@link - * SurfaceTexture#getTimestamp timestamp} for the frame (after - * conversion to nanoseconds). - * @param cryptoInfo Metadata describing the structure of the encrypted input sample. - * @param flags A bitmask of flags - * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}. - * While not prohibited, most codecs do not use the - * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers. + * @param block The graphic block object * @return this object * @throws IllegalStateException if a buffer is already set */ - public @NonNull QueueRequest setEncryptedLinearBlock( - @NonNull LinearBlock block, - int offset, - @NonNull MediaCodec.CryptoInfo cryptoInfo, - long presentationTimeUs, - @BufferFlag int flags) { + public @NonNull QueueRequest setGraphicBlock( + @NonNull GraphicBlock block) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } if (mLinearBlock != null || mGraphicBlock != null) { - throw new IllegalStateException(); + throw new IllegalStateException("Cannot set block twice"); } - mLinearBlock = block; - mOffset = offset; - mCryptoInfo = cryptoInfo; - mPresentationTimeUs = presentationTimeUs; - mFlags = flags; + mGraphicBlock = block; return this; } /** - * Set a graphic block to this queue request. Exactly one buffer must - * be set for a queue request before calling {@link #queue}. + * Set timestamp to this queue request. * - * @param block The graphic block object * @param presentationTimeUs The presentation timestamp in microseconds for this * buffer. This is normally the media time at which this * buffer should be presented (rendered). When using an output * surface, this will be propagated as the {@link * SurfaceTexture#getTimestamp timestamp} for the frame (after * conversion to nanoseconds). + * @return this object + */ + public @NonNull QueueRequest setPresentationTimeUs(long presentationTimeUs) { + if (!isAccessible()) { + throw new IllegalStateException("The request is stale"); + } + mPresentationTimeUs = presentationTimeUs; + return this; + } + + /** + * Set flags to this queue request. + * * @param flags A bitmask of flags * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}. * While not prohibited, most codecs do not use the * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers. * @return this object - * @throws IllegalStateException if a buffer is already set */ - public @NonNull QueueRequest setGraphicBlock( - @NonNull GraphicBlock block, - long presentationTimeUs, - @BufferFlag int flags) { + public @NonNull QueueRequest setFlags(@BufferFlag int flags) { if (!isAccessible()) { - throw new IllegalStateException(); - } - if (mLinearBlock != null || mGraphicBlock != null) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } - mGraphicBlock = block; - mPresentationTimeUs = presentationTimeUs; mFlags = flags; return this; } /** - * Add a integer parameter. See {@link MediaFormat} for the list of - * supported tunings. If there was {@link MediaCodec#setParameters} + * Add an integer parameter. + * See {@link MediaFormat} for an exhaustive list of supported keys with + * values of type int, that can also be set with {@link MediaFormat#setInteger}. + * + * If there was {@link MediaCodec#setParameters} * call with the same key which is not processed by the codec yet, the * value set from this method will override the unprocessed value. + * + * @return this object */ public @NonNull QueueRequest setIntegerParameter( @NonNull String key, int value) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } mTuningKeys.add(key); mTuningValues.add(Integer.valueOf(value)); @@ -3301,15 +3285,20 @@ final public class MediaCodec { } /** - * Add a long parameter. See {@link MediaFormat} for the list of - * supported tunings. If there was {@link MediaCodec#setParameters} + * Add a long parameter. + * See {@link MediaFormat} for an exhaustive list of supported keys with + * values of type long, that can also be set with {@link MediaFormat#setLong}. + * + * If there was {@link MediaCodec#setParameters} * call with the same key which is not processed by the codec yet, the * value set from this method will override the unprocessed value. + * + * @return this object */ public @NonNull QueueRequest setLongParameter( @NonNull String key, long value) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } mTuningKeys.add(key); mTuningValues.add(Long.valueOf(value)); @@ -3317,15 +3306,20 @@ final public class MediaCodec { } /** - * Add a float parameter. See {@link MediaFormat} for the list of - * supported tunings. If there was {@link MediaCodec#setParameters} + * Add a float parameter. + * See {@link MediaFormat} for an exhaustive list of supported keys with + * values of type float, that can also be set with {@link MediaFormat#setFloat}. + * + * If there was {@link MediaCodec#setParameters} * call with the same key which is not processed by the codec yet, the * value set from this method will override the unprocessed value. + * + * @return this object */ public @NonNull QueueRequest setFloatParameter( @NonNull String key, float value) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } mTuningKeys.add(key); mTuningValues.add(Float.valueOf(value)); @@ -3333,15 +3327,20 @@ final public class MediaCodec { } /** - * Add a {@link ByteBuffer} parameter. See {@link MediaFormat} for the list of - * supported tunings. If there was {@link MediaCodec#setParameters} + * Add a {@link ByteBuffer} parameter. + * See {@link MediaFormat} for an exhaustive list of supported keys with + * values of byte buffer, that can also be set with {@link MediaFormat#setByteBuffer}. + * + * If there was {@link MediaCodec#setParameters} * call with the same key which is not processed by the codec yet, the * value set from this method will override the unprocessed value. + * + * @return this object */ public @NonNull QueueRequest setByteBufferParameter( @NonNull String key, @NonNull ByteBuffer value) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } mTuningKeys.add(key); mTuningValues.add(value); @@ -3349,15 +3348,20 @@ final public class MediaCodec { } /** - * Add a string parameter. See {@link MediaFormat} for the list of - * supported tunings. If there was {@link MediaCodec#setParameters} + * Add a string parameter. + * See {@link MediaFormat} for an exhaustive list of supported keys with + * values of type string, that can also be set with {@link MediaFormat#setString}. + * + * If there was {@link MediaCodec#setParameters} * call with the same key which is not processed by the codec yet, the * value set from this method will override the unprocessed value. + * + * @return this object */ public @NonNull QueueRequest setStringParameter( @NonNull String key, @NonNull String value) { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } mTuningKeys.add(key); mTuningValues.add(value); @@ -3369,10 +3373,10 @@ final public class MediaCodec { */ public void queue() { if (!isAccessible()) { - throw new IllegalStateException(); + throw new IllegalStateException("The request is stale"); } if (mLinearBlock == null && mGraphicBlock == null) { - throw new IllegalStateException(); + throw new IllegalStateException("No block is set"); } setAccessible(false); if (mLinearBlock != null) { @@ -3447,7 +3451,7 @@ final public class MediaCodec { private final ArrayList<QueueRequest> mQueueRequests = new ArrayList<>(); /** - * Return a clear {@link QueueRequest} object for an input slot index. + * Return a {@link QueueRequest} object for an input slot index. * * @param index input slot index from * {@link Callback#onInputBufferAvailable} @@ -3459,17 +3463,19 @@ final public class MediaCodec { public @NonNull QueueRequest getQueueRequest(int index) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_BLOCK) { - throw new IllegalStateException(); + throw new IllegalStateException("The codec is not configured for block model"); } if (index < 0 || index >= mQueueRequests.size()) { - throw new IllegalArgumentException(); + throw new IndexOutOfBoundsException("Expected range of index: [0," + + (mQueueRequests.size() - 1) + "]; actual: " + index); } QueueRequest request = mQueueRequests.get(index); if (request == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Unavailable index: " + index); } if (!request.isAccessible()) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException( + "The request is stale at index " + index); } return request.clear(); } @@ -3529,7 +3535,7 @@ final public class MediaCodec { @NonNull BufferInfo info, long timeoutUs) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } } int res = native_dequeueOutputBuffer(info, timeoutUs); @@ -3644,7 +3650,8 @@ final public class MediaCodec { frame.clear(); break; default: - throw new IllegalStateException(); + throw new IllegalStateException( + "Unrecognized buffer mode: " + mBufferMode); } } releaseOutputBuffer( @@ -3910,7 +3917,7 @@ final public class MediaCodec { public ByteBuffer[] getInputBuffers() { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } if (mCachedInputBuffers == null) { throw new IllegalStateException(); @@ -3946,7 +3953,7 @@ final public class MediaCodec { public ByteBuffer[] getOutputBuffers() { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } if (mCachedOutputBuffers == null) { throw new IllegalStateException(); @@ -3978,7 +3985,7 @@ final public class MediaCodec { public ByteBuffer getInputBuffer(int index) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } } ByteBuffer newBuffer = getBuffer(true /* input */, index); @@ -4012,7 +4019,7 @@ final public class MediaCodec { public Image getInputImage(int index) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } } Image newImage = getImage(true /* input */, index); @@ -4046,7 +4053,7 @@ final public class MediaCodec { public ByteBuffer getOutputBuffer(int index) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } } ByteBuffer newBuffer = getBuffer(false /* input */, index); @@ -4079,7 +4086,7 @@ final public class MediaCodec { public Image getOutputImage(int index) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_LEGACY) { - throw new IllegalStateException(); + throw new IncompatibleWithBlockModelException(); } } Image newImage = getImage(false /* input */, index); @@ -4106,7 +4113,7 @@ final public class MediaCodec { */ public @Nullable LinearBlock getLinearBlock() { if (mGraphicBlock != null) { - throw new IllegalStateException(); + throw new IllegalStateException("This output frame is not linear"); } return mLinearBlock; } @@ -4118,7 +4125,7 @@ final public class MediaCodec { */ public @Nullable GraphicBlock getGraphicBlock() { if (mLinearBlock != null) { - throw new IllegalStateException(); + throw new IllegalStateException("This output frame is not graphic"); } return mGraphicBlock; } @@ -4139,7 +4146,7 @@ final public class MediaCodec { /** * Returns a read-only {@link MediaFormat} for this frame. The returned - * object is valid only while the client is holding the output frame. + * object is valid only until the client calls {@link MediaCodec#releaseOutputBuffer}. */ public @NonNull MediaFormat getFormat() { return mFormat; @@ -4151,7 +4158,7 @@ final public class MediaCodec { * Client can find out what the change is by querying {@link MediaFormat} * object returned from {@link #getFormat}. */ - public void getChangedKeys(@NonNull Set<String> keys) { + public void retrieveChangedKeys(@NonNull Set<String> keys) { keys.clear(); keys.addAll(mChangedKeys); } @@ -4211,17 +4218,19 @@ final public class MediaCodec { public @NonNull OutputFrame getOutputFrame(int index) { synchronized (mBufferLock) { if (mBufferMode != BUFFER_MODE_BLOCK) { - throw new IllegalStateException(); + throw new IllegalStateException("The codec is not configured for block model"); } if (index < 0 || index >= mOutputFrames.size()) { - throw new IllegalArgumentException(); + throw new IndexOutOfBoundsException("Expected range of index: [0," + + (mQueueRequests.size() - 1) + "]; actual: " + index); } OutputFrame frame = mOutputFrames.get(index); if (frame == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Unavailable index: " + index); } if (!frame.isAccessible()) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException( + "The output frame is stale at index " + index); } if (!frame.isLoaded()) { native_getOutputFrame(frame, index); diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 28bb4c192425..5942a3d05e67 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -21,6 +21,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.content.Context; import android.os.Bundle; import android.os.Handler; @@ -721,8 +722,7 @@ public class MediaRouter2 { RoutingController newController) { for (TransferCallbackRecord record: mTransferCallbackRecords) { record.mExecutor.execute( - () -> record.mTransferCallback.onTransferred(oldController, - newController)); + () -> record.mTransferCallback.onTransferred(oldController, newController)); } } @@ -866,6 +866,20 @@ public class MediaRouter2 { } /** + * Gets the original session id set by + * {@link RoutingSessionInfo.Builder#Builder(String, String)}. + * + * @hide + */ + @NonNull + @TestApi + public String getOriginalId() { + synchronized (mControllerLock) { + return mSessionInfo.getOriginalId(); + } + } + + /** * @return the control hints used to control routing session if available. */ @Nullable diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java index 5ecb8f0288fc..2258ee559ea0 100644 --- a/media/java/android/media/tv/tuner/TunerUtils.java +++ b/media/java/android/media/tv/tuner/TunerUtils.java @@ -16,6 +16,7 @@ package android.media.tv.tuner; +import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.tv.tuner.V1_0.Constants; @@ -165,5 +166,33 @@ public final class TunerUtils { "Invalid filter types. Main type=" + mainType + ", subtype=" + subtype); } + /** + * Gets an throwable instance for the corresponding result. + */ + @Nullable + public static void throwExceptionForResult( + @TunerConstants.Result int r, @Nullable String msg) { + if (msg == null) { + msg = ""; + } + switch (r) { + case TunerConstants.RESULT_INVALID_ARGUMENT: + throw new IllegalArgumentException(msg); + case TunerConstants.RESULT_INVALID_STATE: + throw new IllegalStateException(msg); + case TunerConstants.RESULT_NOT_INITIALIZED: + throw new IllegalStateException("Invalid state: not initialized. " + msg); + case TunerConstants.RESULT_OUT_OF_MEMORY: + throw new OutOfMemoryError(msg); + case TunerConstants.RESULT_UNAVAILABLE: + throw new IllegalStateException("Invalid state: resource unavailable. " + msg); + case TunerConstants.RESULT_UNKNOWN_ERROR: + throw new RuntimeException("Unknown error" + msg); + default: + break; + } + throw new RuntimeException("Unexpected result " + r + ". " + msg); + } + private TunerUtils() {} } diff --git a/media/java/android/media/tv/tuner/filter/TimeFilter.java b/media/java/android/media/tv/tuner/filter/TimeFilter.java index a926d59cdd03..371ccc44337a 100644 --- a/media/java/android/media/tv/tuner/filter/TimeFilter.java +++ b/media/java/android/media/tv/tuner/filter/TimeFilter.java @@ -17,7 +17,9 @@ package android.media.tv.tuner.filter; import android.annotation.SystemApi; +import android.media.tv.tuner.TunerConstants; import android.media.tv.tuner.TunerConstants.Result; +import android.media.tv.tuner.TunerUtils; /** * A timer filter is used to filter data based on timestamps. @@ -51,6 +53,8 @@ public class TimeFilter implements AutoCloseable { private native Long nativeGetSourceTime(); private native int nativeClose(); + private long mNativeContext; + private boolean mEnable = false; // Called by JNI code @@ -139,6 +143,9 @@ public class TimeFilter implements AutoCloseable { */ @Override public void close() { - nativeClose(); + int res = nativeClose(); + if (res != TunerConstants.RESULT_SUCCESS) { + TunerUtils.throwExceptionForResult(res, "Failed to close time filter."); + } } } diff --git a/media/jni/Android.bp b/media/jni/Android.bp index c17b1b773bd5..47ec7e6c5593 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -160,8 +160,3 @@ cc_library_shared { "-Wunreachable-code", ], } - -subdirs = [ - "audioeffect", - "soundpool", -] diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index f4d2d030c6f1..4f31f6c091c9 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -118,10 +118,12 @@ using ::android::hardware::tv::tuner::V1_0::Result; struct fields_t { jfieldID tunerContext; jfieldID filterContext; + jfieldID timeFilterContext; jfieldID descramblerContext; jfieldID dvrContext; jmethodID frontendInitID; jmethodID filterInitID; + jmethodID timeFilterInitID; jmethodID dvrInitID; jmethodID onFrontendEventID; jmethodID onFilterStatusID; @@ -237,6 +239,25 @@ sp<IFilter> Filter::getIFilter() { return mFilterSp; } +/////////////// TimeFilter /////////////////////// + +TimeFilter::TimeFilter(sp<ITimeFilter> sp, jobject obj) : mTimeFilterSp(sp) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + mTimeFilterObj = env->NewWeakGlobalRef(obj); +} + +TimeFilter::~TimeFilter() { + ALOGD("~TimeFilter"); + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->DeleteWeakGlobalRef(mTimeFilterObj); + mTimeFilterObj = NULL; +} + +sp<ITimeFilter> TimeFilter::getITimeFilter() { + return mTimeFilterSp; +} + /////////////// FrontendCallback /////////////////////// FrontendCallback::FrontendCallback(jweak tunerObj, FrontendId id) : mObject(tunerObj), mId(id) {} @@ -841,6 +862,36 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) { return filterObj; } +jobject JTuner::openTimeFilter() { + if (mDemux == NULL) { + if (!openDemux()) { + return NULL; + } + } + sp<ITimeFilter> iTimeFilterSp; + Result res; + mDemux->openTimeFilter( + [&](Result r, const sp<ITimeFilter>& filter) { + iTimeFilterSp = filter; + res = r; + }); + + if (res != Result::SUCCESS || iTimeFilterSp == NULL) { + return NULL; + } + + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jobject timeFilterObj = + env->NewObject( + env->FindClass("android/media/tv/tuner/filter/TimeFilter"), + gFields.timeFilterInitID); + sp<TimeFilter> timeFilterSp = new TimeFilter(iTimeFilterSp, timeFilterObj); + timeFilterSp->incStrong(timeFilterObj); + env->SetLongField(timeFilterObj, gFields.timeFilterContext, (jlong)timeFilterSp.get()); + + return timeFilterObj; +} + jobject JTuner::openDvr(DvrType type, int bufferSize) { ALOGD("JTuner::openDvr"); if (mDemux == NULL) { @@ -1420,6 +1471,10 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) { gFields.onFilterStatusID = env->GetMethodID(filterClazz, "onFilterStatus", "(I)V"); + jclass timeFilterClazz = env->FindClass("android/media/tv/tuner/filter/TimeFilter"); + gFields.timeFilterContext = env->GetFieldID(timeFilterClazz, "mNativeContext", "J"); + gFields.timeFilterInitID = env->GetMethodID(timeFilterClazz, "<init>", "()V"); + jclass descramblerClazz = env->FindClass("android/media/tv/tuner/Descrambler"); gFields.descramblerContext = env->GetFieldID(descramblerClazz, "mNativeContext", "J"); gFields.descramblerInitID = @@ -1515,18 +1570,35 @@ static jobject android_media_tv_Tuner_open_lnb_by_id(JNIEnv *env, jobject thiz, static jobject android_media_tv_Tuner_open_filter( JNIEnv *env, jobject thiz, jint type, jint subType, jlong bufferSize) { sp<JTuner> tuner = getTuner(env, thiz); + DemuxFilterMainType mainType = static_cast<DemuxFilterMainType>(type); DemuxFilterType filterType { - .mainType = static_cast<DemuxFilterMainType>(type), + .mainType = mainType, }; - // TODO: other sub types - filterType.subType.tsFilterType(static_cast<DemuxTsFilterType>(subType)); + switch(mainType) { + case DemuxFilterMainType::TS: + filterType.subType.tsFilterType(static_cast<DemuxTsFilterType>(subType)); + break; + case DemuxFilterMainType::MMTP: + filterType.subType.mmtpFilterType(static_cast<DemuxMmtpFilterType>(subType)); + break; + case DemuxFilterMainType::IP: + filterType.subType.ipFilterType(static_cast<DemuxIpFilterType>(subType)); + break; + case DemuxFilterMainType::TLV: + filterType.subType.tlvFilterType(static_cast<DemuxTlvFilterType>(subType)); + break; + case DemuxFilterMainType::ALP: + filterType.subType.alpFilterType(static_cast<DemuxAlpFilterType>(subType)); + break; + } return tuner->openFilter(filterType, bufferSize); } -static jobject android_media_tv_Tuner_open_time_filter(JNIEnv, jobject) { - return NULL; +static jobject android_media_tv_Tuner_open_time_filter(JNIEnv *env, jobject thiz) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->openTimeFilter(); } static DemuxFilterSectionBits getFilterSectionBits(JNIEnv *env, const jobject& settings) { @@ -1987,26 +2059,98 @@ static int android_media_tv_Tuner_close_filter(JNIEnv*, jobject) { return 0; } -// TODO: implement TimeFilter functions +static sp<TimeFilter> getTimeFilter(JNIEnv *env, jobject filter) { + return (TimeFilter *)env->GetLongField(filter, gFields.timeFilterContext); +} + static int android_media_tv_Tuner_time_filter_set_timestamp( - JNIEnv, jobject, jlong) { - return 0; + JNIEnv *env, jobject filter, jlong timestamp) { + sp<TimeFilter> filterSp = getTimeFilter(env, filter); + if (filterSp == NULL) { + ALOGD("Failed set timestamp: time filter not found"); + return (int) Result::INVALID_STATE; + } + sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter(); + Result r = iFilterSp->setTimeStamp(static_cast<uint64_t>(timestamp)); + return (int) r; } -static int android_media_tv_Tuner_time_filter_clear_timestamp(JNIEnv, jobject) { - return 0; +static int android_media_tv_Tuner_time_filter_clear_timestamp(JNIEnv *env, jobject filter) { + sp<TimeFilter> filterSp = getTimeFilter(env, filter); + if (filterSp == NULL) { + ALOGD("Failed clear timestamp: time filter not found"); + return (int) Result::INVALID_STATE; + } + sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter(); + Result r = iFilterSp->clearTimeStamp(); + return (int) r; } -static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv, jobject) { - return NULL; +static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv *env, jobject filter) { + sp<TimeFilter> filterSp = getTimeFilter(env, filter); + if (filterSp == NULL) { + ALOGD("Failed get timestamp: time filter not found"); + return NULL; + } + + sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter(); + Result res; + uint64_t timestamp; + iFilterSp->getTimeStamp( + [&](Result r, uint64_t t) { + res = r; + timestamp = t; + }); + if (res != Result::SUCCESS) { + return NULL; + } + + jclass longClazz = env->FindClass("java/lang/Long"); + jmethodID longInit = env->GetMethodID(longClazz, "<init>", "(J)V"); + + jobject longObj = env->NewObject(longClazz, longInit, static_cast<jlong>(timestamp)); + return longObj; } -static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv, jobject) { - return NULL; +static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv *env, jobject filter) { + sp<TimeFilter> filterSp = getTimeFilter(env, filter); + if (filterSp == NULL) { + ALOGD("Failed get source time: time filter not found"); + return NULL; + } + + sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter(); + Result res; + uint64_t timestamp; + iFilterSp->getSourceTime( + [&](Result r, uint64_t t) { + res = r; + timestamp = t; + }); + if (res != Result::SUCCESS) { + return NULL; + } + + jclass longClazz = env->FindClass("java/lang/Long"); + jmethodID longInit = env->GetMethodID(longClazz, "<init>", "(J)V"); + + jobject longObj = env->NewObject(longClazz, longInit, static_cast<jlong>(timestamp)); + return longObj; } -static int android_media_tv_Tuner_time_filter_close(JNIEnv, jobject) { - return 0; +static int android_media_tv_Tuner_time_filter_close(JNIEnv *env, jobject filter) { + sp<TimeFilter> filterSp = getTimeFilter(env, filter); + if (filterSp == NULL) { + ALOGD("Failed close time filter: time filter not found"); + return (int) Result::INVALID_STATE; + } + + Result r = filterSp->getITimeFilter()->close(); + if (r == Result::SUCCESS) { + filterSp->decStrong(filter); + env->SetLongField(filter, gFields.timeFilterContext, 0); + } + return (int) r; } static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz) { diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index d899bbd6467f..c5590b956eda 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -54,6 +54,7 @@ using ::android::hardware::tv::tuner::V1_0::IFrontend; using ::android::hardware::tv::tuner::V1_0::IFrontendCallback; using ::android::hardware::tv::tuner::V1_0::ILnb; using ::android::hardware::tv::tuner::V1_0::ILnbCallback; +using ::android::hardware::tv::tuner::V1_0::ITimeFilter; using ::android::hardware::tv::tuner::V1_0::ITuner; using ::android::hardware::tv::tuner::V1_0::LnbEventType; using ::android::hardware::tv::tuner::V1_0::LnbId; @@ -127,6 +128,14 @@ struct Filter : public RefBase { jweak mFilterObj; }; +struct TimeFilter : public RefBase { + TimeFilter(sp<ITimeFilter> sp, jweak obj); + ~TimeFilter(); + sp<ITimeFilter> getITimeFilter(); + sp<ITimeFilter> mTimeFilterSp; + jweak mTimeFilterObj; +}; + struct JTuner : public RefBase { JTuner(JNIEnv *env, jobject thiz); sp<ITuner> getTunerService(); @@ -142,6 +151,7 @@ struct JTuner : public RefBase { jobject getLnbIds(); jobject openLnbById(int id); jobject openFilter(DemuxFilterType type, int bufferSize); + jobject openTimeFilter(); jobject openDescrambler(); jobject openDvr(DvrType type, int bufferSize); diff --git a/media/native/Android.bp b/media/native/Android.bp deleted file mode 100644 index b44c2960127f..000000000000 --- a/media/native/Android.bp +++ /dev/null @@ -1 +0,0 @@ -subdirs = ["*"] diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java index c9ac765dfc1d..873d7d7975e0 100644 --- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java +++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java @@ -25,8 +25,11 @@ import android.app.Dialog; import android.app.KeyguardManager; import android.car.Car; import android.car.media.CarAudioManager; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.Color; @@ -38,6 +41,7 @@ import android.os.Debug; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.UserHandle; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; @@ -171,6 +175,17 @@ public class CarVolumeDialogImpl implements VolumeDialog { mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback); }; + private final BroadcastReceiver mHomeButtonPressedBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!intent.getAction().equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) { + return; + } + + dismissH(Events.DISMISS_REASON_VOLUME_CONTROLLER); + } + }; + public CarVolumeDialogImpl(Context context) { mContext = context; mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); @@ -204,12 +219,18 @@ public class CarVolumeDialogImpl implements VolumeDialog { @Override public void init(int windowType, Callback callback) { initDialog(); + + mContext.registerReceiverAsUser(mHomeButtonPressedBroadcastReceiver, UserHandle.CURRENT, + new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), /* broadcastPermission= */ + null, /* scheduler= */ null); } @Override public void destroy() { mHandler.removeCallbacksAndMessages(/* token= */ null); + mContext.unregisterReceiver(mHomeButtonPressedBroadcastReceiver); + cleanupAudioManager(); } diff --git a/packages/DynamicSystemInstallationService/res/values/strings.xml b/packages/DynamicSystemInstallationService/res/values/strings.xml index 25b7fc1b5ce2..e124be605cc7 100644 --- a/packages/DynamicSystemInstallationService/res/values/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values/strings.xml @@ -27,10 +27,11 @@ <string name="notification_action_cancel">Cancel</string> <!-- Action on notification: Discard installation [CHAR LIMIT=16] --> <string name="notification_action_discard">Discard</string> - <!-- Action on notification: Uninstall Dynamic System [CHAR LIMIT=16] --> - <string name="notification_action_uninstall">Uninstall</string> <!-- Action on notification: Restart to Dynamic System [CHAR LIMIT=16] --> <string name="notification_action_reboot_to_dynsystem">Restart</string> + <!-- Action on notification: Restart to original Android version [CHAR LIMIT=16] --> + <string name="notification_action_reboot_to_origin">Restart</string> + <!-- Toast when installed Dynamic System is discarded [CHAR LIMIT=64] --> <string name="toast_dynsystem_discarded">Discarded dynamic system</string> diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index 7affe8888628..37a77be52983 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -462,7 +462,7 @@ public class DynamicSystemInstallationService extends Service .setStyle(new Notification.BigTextStyle().bigText(msgInUse)); builder.addAction(new Notification.Action.Builder( - null, getString(R.string.notification_action_uninstall), + null, getString(R.string.notification_action_reboot_to_origin), createPendingIntent(ACTION_REBOOT_TO_NORMAL)).build()); break; diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 59881e7ba13d..d25e3e2ade01 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -886,6 +886,11 @@ <!-- UI debug setting: enable gpu debug layers summary [CHAR LIMIT=50] --> <string name="enable_gpu_debug_layers_summary">Allow loading GPU debug layers for debug apps</string> + <!-- UI debug setting: enable verbose vendor logging [CHAR LIMIT=30] --> + <string name="enable_verbose_vendor_logging">Enable verbose vendor logging</string> + <!-- UI debug setting: enable verbose vendor logging summary [CHAR LIMIT=100] --> + <string name="enable_verbose_vendor_logging_summary">Allow additional vendor logs to be included in bug reports, may contain private information</string> + <!-- UI debug setting: scaling factor for window animations [CHAR LIMIT=25] --> <string name="window_animation_scale_title">Window animation scale</string> @@ -1260,9 +1265,12 @@ <!-- The notice header of Third-party licenses. not translatable --> <string name="notice_header" translatable="false"></string> - <!-- Name of the this device. [CHAR LIMIT=30] --> - <string name="media_transfer_this_device_name">This device</string> + <!-- Name of the phone device. [CHAR LIMIT=30] --> + <string name="media_transfer_this_device_name">Phone speaker</string> <!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] --> <string name="profile_connect_timeout_subtext">Problem connecting. Turn device off & back on</string> + + <!-- Name of the 3.5mm audio device. [CHAR LIMIT=40] --> + <string name="media_transfer_wired_device_name">Wired audio device</string> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java index e0ca1ab0c07c..a38091debb64 100644 --- a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java @@ -97,7 +97,7 @@ public class DisplayDensityUtils { final Resources res = context.getResources(); final DisplayMetrics metrics = new DisplayMetrics(); - context.getDisplay().getRealMetrics(metrics); + context.getDisplayNoVerify().getRealMetrics(metrics); final int currentDensity = metrics.densityDpi; int currentDensityIndex = -1; diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index de174b13a459..e0662309f571 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -86,7 +86,9 @@ filegroup { android_library { name: "SystemUI-tests", - manifest: "tests/AndroidManifest.xml", + manifest: "tests/AndroidManifest-base.xml", + additional_manifests: ["tests/AndroidManifest.xml"], + resource_dirs: [ "tests/res", "res-product", diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 5458676e1061..e2410fec4dc2 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -696,8 +696,7 @@ <provider android:name="com.android.keyguard.clock.ClockOptionsProvider" android:authorities="com.android.keyguard.clock" - android:enabled="false" - android:exported="false" + android:exported="true" android:grantUriPermissions="true"> </provider> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index e475ef1d9761..6f06f6986c00 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -51,7 +51,7 @@ import javax.inject.Named; public class KeyguardClockSwitch extends RelativeLayout { private static final String TAG = "KeyguardClockSwitch"; - private static final boolean CUSTOM_CLOCKS_ENABLED = false; + private static final boolean CUSTOM_CLOCKS_ENABLED = true; /** * Animation fraction when text is transitioned to/from bold. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 571c4ae0e386..11bf24d27170 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -72,11 +72,12 @@ public class KeyguardDisplayManager { @Override public void onDisplayChanged(int displayId) { if (displayId == DEFAULT_DISPLAY) return; - final Display display = mDisplayService.getDisplay(displayId); - if (display != null && mShowing) { - final Presentation presentation = mPresentations.get(displayId); - if (presentation != null && !presentation.getDisplay().equals(display)) { - hidePresentation(displayId); + final Presentation presentation = mPresentations.get(displayId); + if (presentation != null && mShowing) { + hidePresentation(displayId); + // update DisplayInfo. + final Display display = mDisplayService.getDisplay(displayId); + if (display != null) { showPresentation(display); } } @@ -266,6 +267,11 @@ public class KeyguardDisplayManager { } @Override + public void cancel() { + // Do not allow anything to cancel KeyguardPresetation except KeyguardDisplayManager. + } + + @Override public void onDetachedFromWindow() { mClock.removeCallbacks(mMoveTextRunnable); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 61caf3bc5d8f..241f96e19e38 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -149,7 +149,7 @@ public class KeyguardStatusView extends GridLayout implements new WindowlessWindowManager(context.getResources().getConfiguration(), surfaceControl, input); mUniversalSmartspaceViewHost = new SurfaceControlViewHost(context, - context.getDisplay(), windowlessWindowManager); + context.getDisplayNoVerify(), windowlessWindowManager); WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( surfaceControl.getWidth(), diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 9cd4aec4617d..03674648d1e4 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -149,8 +149,6 @@ public final class ClockManager { LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context)); addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor)); - addBuiltinClock(() -> new BubbleClockController(res, layoutInflater, colorExtractor)); - addBuiltinClock(() -> new AnalogClockController(res, layoutInflater, colorExtractor)); // Store the size of the display for generation of clock preview. DisplayMetrics dm = res.getDisplayMetrics(); @@ -211,7 +209,8 @@ public final class ClockManager { return mContentObserver; } - private void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) { + @VisibleForTesting + void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) { ClockPlugin plugin = pluginSupplier.get(); mPreviewClocks.addClockPlugin(plugin); mBuiltinClocks.add(pluginSupplier); diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index c9104dccce80..6ce6353147fb 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -96,6 +96,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { 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; private DisplayManager mDisplayManager; private boolean mIsRegistered; @@ -454,6 +455,9 @@ public class ScreenDecorations extends SystemUI implements Tunable { private void updateColorInversion(int colorsInvertedValue) { int tint = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK; + if (DEBUG_COLOR) { + tint = Color.RED; + } ColorStateList tintList = ColorStateList.valueOf(tint); if (mOverlays == null) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index 2daefbde45a8..56cdff43e255 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -137,7 +137,8 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, try { mLastImeTarget = ActivityTaskManager.getTaskOrganizerController() .getImeTarget(displayId); - mShouldAdjustForIme = !mSplitLayout.mDisplayLayout.isLandscape() + mShouldAdjustForIme = mLastImeTarget != null + && !mSplitLayout.mDisplayLayout.isLandscape() && (mLastImeTarget.asBinder() == mSplits.mSecondary.token.asBinder()); } catch (RemoteException e) { diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt index f4157f21e158..8625d63a3c7e 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt @@ -126,6 +126,13 @@ class PhysicsAnimator<T> private constructor (val target: T) { internal var startAction: () -> Unit = ::startInternal /** + * Action to run when [cancel] is called. This can be changed by + * [PhysicsAnimatorTestUtils.prepareForTest] to cancel animations from the main thread, which + * is required. + */ + internal var cancelAction: (Set<FloatPropertyCompat<in T>>) -> Unit = ::cancelInternal + + /** * Springs a property to the given value, using the provided configuration settings. * * Springs are used when you know the exact value to which you want to animate. They can be @@ -429,10 +436,13 @@ class PhysicsAnimator<T> private constructor (val target: T) { max = max(currentValue, this.max) } - // Apply the configuration and start the animation. Since flings can't be - // redirected while in motion, cancel it first. + // Flings can't be updated to a new position while maintaining velocity, because + // we're using the explicitly provided start velocity. Cancel any flings (or + // springs) on this property before flinging. + cancel(animatedProperty) + + // Apply the configuration and start the animation. getFlingAnimation(animatedProperty) - .also { it.cancel() } .also { flingConfig.applyToAnimation(it) } .start() } @@ -707,11 +717,26 @@ class PhysicsAnimator<T> private constructor (val target: T) { return springConfigs.keys.union(flingConfigs.keys) } + /** + * Cancels the given properties. This is typically called immediately by [cancel], unless this + * animator is under test. + */ + internal fun cancelInternal(properties: Set<FloatPropertyCompat<in T>>) { + for (property in properties) { + flingAnimations[property]?.cancel() + springAnimations[property]?.cancel() + } + } + /** Cancels all in progress animations on all properties. */ fun cancel() { - for (dynamicAnim in flingAnimations.values.union(springAnimations.values)) { - dynamicAnim.cancel() - } + cancelAction(flingAnimations.keys) + cancelAction(springAnimations.keys) + } + + /** Cancels in progress animations on the provided properties only. */ + fun cancel(vararg properties: FloatPropertyCompat<in T>) { + cancelAction(properties.toSet()) } /** diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt index 965decd255a0..c50eeac80d7a 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt @@ -363,8 +363,12 @@ object PhysicsAnimatorTestUtils { private val testEndListeners = ArrayList<PhysicsAnimator.EndListener<T>>() private val testUpdateListeners = ArrayList<PhysicsAnimator.UpdateListener<T>>() + /** Whether we're currently in the middle of executing startInternal(). */ + private var currentlyRunningStartInternal = false + init { animator.startAction = ::startForTest + animator.cancelAction = ::cancelForTest } internal fun addTestEndListener(listener: PhysicsAnimator.EndListener<T>) { @@ -437,7 +441,29 @@ object PhysicsAnimatorTestUtils { } }) + currentlyRunningStartInternal = true animator.startInternal() + currentlyRunningStartInternal = false + unblockLatch.countDown() + } + + unblockLatch.await(timeoutMs, TimeUnit.MILLISECONDS) + } + + private fun cancelForTest(properties: Set<FloatPropertyCompat<in T>>) { + // If this was called from startInternal, we are already on the animation thread, and + // should just call cancelInternal rather than posting it. If we post it, the + // cancellation will occur after the rest of startInternal() and we'll immediately + // cancel the animation we worked so hard to start! + if (currentlyRunningStartInternal) { + animator.cancelInternal(properties) + return + } + + val unblockLatch = CountDownLatch(1) + + animationThreadHandler.post { + animator.cancelInternal(properties) unblockLatch.countDown() } diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt new file mode 100644 index 000000000000..2276ba19e432 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt @@ -0,0 +1,618 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.util.magnetictarget + +import android.annotation.SuppressLint +import android.content.Context +import android.database.ContentObserver +import android.graphics.PointF +import android.os.Handler +import android.os.UserHandle +import android.os.VibrationEffect +import android.os.Vibrator +import android.provider.Settings +import android.view.MotionEvent +import android.view.VelocityTracker +import android.view.View +import androidx.dynamicanimation.animation.DynamicAnimation +import androidx.dynamicanimation.animation.FloatPropertyCompat +import androidx.dynamicanimation.animation.SpringForce +import com.android.systemui.util.animation.PhysicsAnimator +import kotlin.math.hypot + +/** + * Utility class for creating 'magnetized' objects that are attracted to one or more magnetic + * targets. Magnetic targets attract objects that are dragged near them, and hold them there unless + * they're moved away or released. Releasing objects inside a magnetic target typically performs an + * action on the object. + * + * MagnetizedObject also supports flinging to targets, which will result in the object being pulled + * into the target and released as if it was dragged into it. + * + * To use this class, either construct an instance with an object of arbitrary type, or use the + * [MagnetizedObject.magnetizeView] shortcut method if you're magnetizing a view. Then, set + * [magnetListener] to receive event callbacks. In your touch handler, pass all MotionEvents + * that move this object to [maybeConsumeMotionEvent]. If that method returns true, consider the + * event consumed by the MagnetizedObject and don't move the object unless it begins returning false + * again. + * + * @param context Context, used to retrieve a Vibrator instance for vibration effects. + * @param underlyingObject The actual object that we're magnetizing. + * @param xProperty Property that sets the x value of the object's position. + * @param yProperty Property that sets the y value of the object's position. + */ +abstract class MagnetizedObject<T : Any>( + val context: Context, + + /** The actual object that is animated. */ + val underlyingObject: T, + + /** Property that gets/sets the object's X value. */ + val xProperty: FloatPropertyCompat<in T>, + + /** Property that gets/sets the object's Y value. */ + val yProperty: FloatPropertyCompat<in T> +) { + + /** Return the width of the object. */ + abstract fun getWidth(underlyingObject: T): Float + + /** Return the height of the object. */ + abstract fun getHeight(underlyingObject: T): Float + + /** + * Fill the provided array with the location of the top-left of the object, relative to the + * entire screen. Compare to [View.getLocationOnScreen]. + */ + abstract fun getLocationOnScreen(underlyingObject: T, loc: IntArray) + + /** Methods for listening to events involving a magnetized object. */ + interface MagnetListener { + + /** + * Called when touch events move within the magnetic field of a target, causing the + * object to animate to the target and become 'stuck' there. The animation happens + * automatically here - you should not move the object. You can, however, change its state + * to indicate to the user that it's inside the target and releasing it will have an effect. + * + * [maybeConsumeMotionEvent] is now returning true and will continue to do so until a call + * to [onUnstuckFromTarget] or [onReleasedInTarget]. + * + * @param target The target that the object is now stuck to. + */ + fun onStuckToTarget(target: MagneticTarget) + + /** + * Called when the object is no longer stuck to a target. This means that either touch + * events moved outside of the magnetic field radius, or that a forceful fling out of the + * target was detected. + * + * The object won't be automatically animated out of the target, since you're responsible + * for moving the object again. You should move it (or animate it) using your own + * movement/animation logic. + * + * Reverse any effects applied in [onStuckToTarget] here. + * + * If [wasFlungOut] is true, [maybeConsumeMotionEvent] returned true for the ACTION_UP event + * that concluded the fling. If [wasFlungOut] is false, that means a drag gesture is ongoing + * and [maybeConsumeMotionEvent] is now returning false. + * + * @param target The target that this object was just unstuck from. + * @param velX The X velocity of the touch gesture when it exited the magnetic field. + * @param velY The Y velocity of the touch gesture when it exited the magnetic field. + * @param wasFlungOut Whether the object was unstuck via a fling gesture. This means that + * an ACTION_UP event was received, and that the gesture velocity was sufficient to conclude + * that the user wants to un-stick the object despite no touch events occurring outside of + * the magnetic field radius. + */ + fun onUnstuckFromTarget( + target: MagneticTarget, + velX: Float, + velY: Float, + wasFlungOut: Boolean + ) + + /** + * Called when the object is released inside a target, or flung towards it with enough + * velocity to reach it. + * + * @param target The target that the object was released in. + */ + fun onReleasedInTarget(target: MagneticTarget) + } + + private val animator: PhysicsAnimator<T> = PhysicsAnimator.getInstance(underlyingObject) + private val objectLocationOnScreen = IntArray(2) + + /** + * Targets that have been added to this object. These will all be considered when determining + * magnetic fields and fling trajectories. + */ + private val associatedTargets = ArrayList<MagneticTarget>() + + private val velocityTracker: VelocityTracker = VelocityTracker.obtain() + private val vibrator: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + + /** Whether touch events are presently occurring within the magnetic field area of a target. */ + val objectStuckToTarget: Boolean + get() = targetObjectIsStuckTo != null + + /** The target the object is stuck to, or null if the object is not stuck to any target. */ + private var targetObjectIsStuckTo: MagneticTarget? = null + + /** + * Sets the listener to receive events. This must be set, or [maybeConsumeMotionEvent] + * will always return false and no magnetic effects will occur. + */ + lateinit var magnetListener: MagnetizedObject.MagnetListener + + /** + * Sets whether forcefully flinging the object vertically towards a target causes it to be + * attracted to the target and then released immediately, despite never being dragged within the + * magnetic field. + */ + var flingToTargetEnabled = true + + /** + * If fling to target is enabled, forcefully flinging the object towards a target will cause + * it to be attracted to the target and then released immediately, despite never being dragged + * within the magnetic field. + * + * This sets the width of the area considered 'near' enough a target to be considered a fling, + * in terms of percent of the target view's width. For example, setting this to 3f means that + * flings towards a 100px-wide target will be considered 'near' enough if they're towards the + * 300px-wide area around the target. + * + * Flings whose trajectory intersects the area will be attracted and released - even if the + * target view itself isn't intersected: + * + * | | + * | 0 | + * | / | + * | / | + * | X / | + * |.....###.....| + * + * + * Flings towards the target whose trajectories do not intersect the area will be treated as + * normal flings and the magnet will leave the object alone: + * + * | | + * | | + * | 0 | + * | / | + * | / X | + * |.....###.....| + * + */ + var flingToTargetWidthPercent = 3f + + /** + * Sets the minimum velocity (in pixels per second) required to fling an object to the target + * without dragging it into the magnetic field. + */ + var flingToTargetMinVelocity = 4000f + + /** + * Sets the minimum velocity (in pixels per second) required to fling un-stuck an object stuck + * to the target. If this velocity is reached, the object will be freed even if it wasn't moved + * outside the magnetic field radius. + */ + var flingUnstuckFromTargetMinVelocity = 1000f + + /** + * Sets the maximum velocity above which the object will not stick to the target. Even if the + * object is dragged through the magnetic field, it will not stick to the target until the + * velocity is below this value. + */ + var stickToTargetMaxVelocity = 2000f + + /** + * Enable or disable haptic vibration effects when the object interacts with the magnetic field. + * + * If you're experiencing crashes when the object enters targets, ensure that you have the + * android.permission.VIBRATE permission! + */ + var hapticsEnabled = true + + /** Whether the HAPTIC_FEEDBACK_ENABLED setting is true. */ + private var systemHapticsEnabled = false + + /** Default spring configuration to use for animating the object into a target. */ + var springConfig = PhysicsAnimator.SpringConfig( + SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_NO_BOUNCY) + + /** + * Spring configuration to use to spring the object into a target specifically when it's flung + * towards (rather than dragged near) it. + */ + var flungIntoTargetSpringConfig = springConfig + + init { + val hapticSettingObserver = + object : ContentObserver(Handler.getMain()) { + override fun onChange(selfChange: Boolean) { + systemHapticsEnabled = + Settings.System.getIntForUser( + context.contentResolver, + Settings.System.HAPTIC_FEEDBACK_ENABLED, + 0, + UserHandle.USER_CURRENT) != 0 + } + } + + context.contentResolver.registerContentObserver( + Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_ENABLED), + true /* notifyForDescendants */, hapticSettingObserver) + + // Trigger the observer once to initialize systemHapticsEnabled. + hapticSettingObserver.onChange(false /* selfChange */) + } + + /** + * Adds the provided MagneticTarget to this object. The object will now be attracted to the + * target if it strays within its magnetic field or is flung towards it. + * + * If this target (or its magnetic field) overlaps another target added to this object, the + * prior target will take priority. + */ + fun addTarget(target: MagneticTarget) { + associatedTargets.add(target) + target.updateLocationOnScreen() + } + + /** + * Shortcut that accepts a View and a magnetic field radius and adds it as a magnetic target. + * + * @return The MagneticTarget instance for the given View. This can be used to change the + * target's magnetic field radius after it's been added. It can also be added to other + * magnetized objects. + */ + fun addTarget(target: View, magneticFieldRadiusPx: Int): MagneticTarget { + return MagneticTarget(target, magneticFieldRadiusPx).also { addTarget(it) } + } + + /** + * Removes the given target from this object. The target will no longer attract the object. + */ + fun removeTarget(target: MagneticTarget) { + associatedTargets.remove(target) + } + + /** + * Provide this method with all motion events that move the magnetized object. If the + * location of the motion events moves within the magnetic field of a target, or indicate a + * fling-to-target gesture, this method will return true and you should not move the object + * yourself until it returns false again. + * + * Note that even when this method returns true, you should continue to pass along new motion + * events so that we know when the events move back outside the magnetic field area. + * + * This method will always return false if you haven't set a [magnetListener]. + */ + fun maybeConsumeMotionEvent(ev: MotionEvent): Boolean { + // Short-circuit if we don't have a listener or any targets, since those are required. + if (associatedTargets.size == 0) { + return false + } + + // When a gesture begins, recalculate target views' positions on the screen in case they + // have changed. Also, clear state. + if (ev.action == MotionEvent.ACTION_DOWN) { + updateTargetViewLocations() + + // Clear the velocity tracker and assume we're not stuck to a target yet. + velocityTracker.clear() + targetObjectIsStuckTo = null + } + + addMovement(ev) + + val targetObjectIsInMagneticFieldOf = associatedTargets.firstOrNull { target -> + val distanceFromTargetCenter = hypot( + ev.rawX - target.centerOnScreen.x, + ev.rawY - target.centerOnScreen.y) + distanceFromTargetCenter < target.magneticFieldRadiusPx + } + + // If we aren't currently stuck to a target, and we're in the magnetic field of a target, + // we're newly stuck. + val objectNewlyStuckToTarget = + !objectStuckToTarget && targetObjectIsInMagneticFieldOf != null + + // If we are currently stuck to a target, we're in the magnetic field of a target, and that + // target isn't the one we're currently stuck to, then touch events have moved into a + // adjacent target's magnetic field. + val objectMovedIntoDifferentTarget = + objectStuckToTarget && + targetObjectIsInMagneticFieldOf != null && + targetObjectIsStuckTo != targetObjectIsInMagneticFieldOf + + if (objectNewlyStuckToTarget || objectMovedIntoDifferentTarget) { + velocityTracker.computeCurrentVelocity(1000) + val velX = velocityTracker.xVelocity + val velY = velocityTracker.yVelocity + + // If the object is moving too quickly within the magnetic field, do not stick it. This + // only applies to objects newly stuck to a target. If the object is moved into a new + // target, it wasn't moving at all (since it was stuck to the previous one). + if (objectNewlyStuckToTarget && hypot(velX, velY) > stickToTargetMaxVelocity) { + return false + } + + // This touch event is newly within the magnetic field - let the listener know, and + // animate sticking to the magnet. + targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf + cancelAnimations() + magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!) + animateStuckToTarget(targetObjectIsInMagneticFieldOf!!, velX, velY, false) + + vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK) + } else if (targetObjectIsInMagneticFieldOf == null && objectStuckToTarget) { + velocityTracker.computeCurrentVelocity(1000) + + // This touch event is newly outside the magnetic field - let the listener know. It will + // move the object out of the target using its own movement logic. + cancelAnimations() + magnetListener.onUnstuckFromTarget( + targetObjectIsStuckTo!!, velocityTracker.xVelocity, velocityTracker.yVelocity, + wasFlungOut = false) + targetObjectIsStuckTo = null + + vibrateIfEnabled(VibrationEffect.EFFECT_TICK) + } + + // First, check for relevant gestures concluding with an ACTION_UP. + if (ev.action == MotionEvent.ACTION_UP) { + + velocityTracker.computeCurrentVelocity(1000 /* units */) + val velX = velocityTracker.xVelocity + val velY = velocityTracker.yVelocity + + // Cancel the magnetic animation since we might still be springing into the magnetic + // target, but we're about to fling away or release. + cancelAnimations() + + if (objectStuckToTarget) { + if (hypot(velX, velY) > flingUnstuckFromTargetMinVelocity) { + // If the object is stuck, but it was forcefully flung away from the target, + // tell the listener so the object can be animated out of the target. + magnetListener.onUnstuckFromTarget( + targetObjectIsStuckTo!!, velX, velY, wasFlungOut = true) + } else { + // If the object is stuck and not flung away, it was released inside the target. + magnetListener.onReleasedInTarget(targetObjectIsStuckTo!!) + vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK) + } + + // Either way, we're no longer stuck. + targetObjectIsStuckTo = null + return true + } + + // The target we're flinging towards, or null if we're not flinging towards any target. + val flungToTarget = associatedTargets.firstOrNull { target -> + isForcefulFlingTowardsTarget(target, ev.rawX, ev.rawY, velX, velY) + } + + if (flungToTarget != null) { + // If this is a fling-to-target, animate the object to the magnet and then release + // it. + magnetListener.onStuckToTarget(flungToTarget) + targetObjectIsStuckTo = flungToTarget + + animateStuckToTarget(flungToTarget, velX, velY, true) { + targetObjectIsStuckTo = null + magnetListener.onReleasedInTarget(flungToTarget) + vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK) + } + + return true + } + + // If it's not either of those things, we are not interested. + return false + } + + return objectStuckToTarget // Always consume touch events if the object is stuck. + } + + /** Plays the given vibration effect if haptics are enabled. */ + @SuppressLint("MissingPermission") + private fun vibrateIfEnabled(effect: Int) { + if (hapticsEnabled && systemHapticsEnabled) { + vibrator.vibrate(effect.toLong()) + } + } + + /** Adds the movement to the velocity tracker using raw coordinates. */ + private fun addMovement(event: MotionEvent) { + // Add movement to velocity tracker using raw screen X and Y coordinates instead + // of window coordinates because the window frame may be moving at the same time. + val deltaX = event.rawX - event.x + val deltaY = event.rawY - event.y + event.offsetLocation(deltaX, deltaY) + velocityTracker.addMovement(event) + event.offsetLocation(-deltaX, -deltaY) + } + + /** Animates sticking the object to the provided target with the given start velocities. */ + private fun animateStuckToTarget( + target: MagneticTarget, + velX: Float, + velY: Float, + flung: Boolean, + after: (() -> Unit)? = null + ) { + target.updateLocationOnScreen() + getLocationOnScreen(underlyingObject, objectLocationOnScreen) + + // Calculate the difference between the target's center coordinates and the object's. + // Animating the object's x/y properties by these values will center the object on top + // of the magnetic target. + val xDiff = target.centerOnScreen.x - + getWidth(underlyingObject) / 2f - objectLocationOnScreen[0] + val yDiff = target.centerOnScreen.y - + getHeight(underlyingObject) / 2f - objectLocationOnScreen[1] + + val springConfig = if (flung) flungIntoTargetSpringConfig else springConfig + + cancelAnimations() + + // Animate to the center of the target. + animator + .spring(xProperty, xProperty.getValue(underlyingObject) + xDiff, velX, + springConfig) + .spring(yProperty, yProperty.getValue(underlyingObject) + yDiff, velY, + springConfig) + + if (after != null) { + animator.withEndActions(after) + } + + animator.start() + } + + /** + * Whether or not the provided values match a 'fast fling' towards the provided target. If it + * does, we consider it a fling-to-target gesture. + */ + private fun isForcefulFlingTowardsTarget( + target: MagneticTarget, + rawX: Float, + rawY: Float, + velX: Float, + velY: Float + ): Boolean { + if (!flingToTargetEnabled) { + return false + } + + // Whether velocity is sufficient, depending on whether we're flinging into a target at the + // top or the bottom of the screen. + val velocitySufficient = + if (rawY < target.centerOnScreen.y) velY > flingToTargetMinVelocity + else velY < flingToTargetMinVelocity + + if (!velocitySufficient) { + return false + } + + // Whether the trajectory of the fling intersects the target area. + var targetCenterXIntercept = rawX + + // Only do math if the X velocity is non-zero, otherwise X won't change. + if (velX != 0f) { + // Rise over run... + val slope = velY / velX + // ...y = mx + b, b = y / mx... + val yIntercept = rawY - slope * rawX + + // ...calculate the x value when y = the target's y-coordinate. + targetCenterXIntercept = (target.centerOnScreen.y - yIntercept) / slope + } + + // The width of the area we're looking for a fling towards. + val targetAreaWidth = target.targetView.width * flingToTargetWidthPercent + + // Velocity was sufficient, so return true if the intercept is within the target area. + return targetCenterXIntercept > target.centerOnScreen.x - targetAreaWidth / 2 && + targetCenterXIntercept < target.centerOnScreen.x + targetAreaWidth / 2 + } + + /** Cancel animations on this object's x/y properties. */ + internal fun cancelAnimations() { + animator.cancel(xProperty, yProperty) + } + + /** Updates the locations on screen of all of the [associatedTargets]. */ + internal fun updateTargetViewLocations() { + associatedTargets.forEach { it.updateLocationOnScreen() } + } + + /** + * Represents a target view with a magnetic field radius and cached center-on-screen + * coordinates. + * + * Instances of MagneticTarget are passed to a MagnetizedObject's [addTarget], and can then + * attract the object if it's dragged near or flung towards it. MagneticTargets can be added to + * multiple objects. + */ + class MagneticTarget( + internal val targetView: View, + var magneticFieldRadiusPx: Int + ) { + internal val centerOnScreen = PointF() + + private val tempLoc = IntArray(2) + + fun updateLocationOnScreen() { + targetView.getLocationOnScreen(tempLoc) + + // Add half of the target size to get the center, and subtract translation since the + // target could be animating in while we're doing this calculation. + centerOnScreen.set( + tempLoc[0] + targetView.width / 2f - targetView.translationX, + tempLoc[1] + targetView.height / 2f - targetView.translationY) + } + } + + companion object { + + /** + * Magnetizes the given view. Magnetized views are attracted to one or more magnetic + * targets. Magnetic targets attract objects that are dragged near them, and hold them there + * unless they're moved away or released. Releasing objects inside a magnetic target + * typically performs an action on the object. + * + * Magnetized views can also be flung to targets, which will result in the view being pulled + * into the target and released as if it was dragged into it. + * + * To use the returned MagnetizedObject<View> instance, first set [magnetListener] to + * receive event callbacks. In your touch handler, pass all MotionEvents that move this view + * to [maybeConsumeMotionEvent]. If that method returns true, consider the event consumed by + * MagnetizedObject and don't move the view unless it begins returning false again. + * + * The view will be moved via translationX/Y properties, and its + * width/height will be determined via getWidth()/getHeight(). If you are animating + * something other than a view, or want to position your view using properties other than + * translationX/Y, implement an instance of [MagnetizedObject]. + * + * Note that the magnetic library can't re-order your view automatically. If the view + * renders on top of the target views, it will obscure the target when it sticks to it. + * You'll want to bring the view to the front in [MagnetListener.onStuckToTarget]. + */ + @JvmStatic + fun <T : View> magnetizeView(view: T): MagnetizedObject<T> { + return object : MagnetizedObject<T>( + view.context, + view, + DynamicAnimation.TRANSLATION_X, + DynamicAnimation.TRANSLATION_Y) { + override fun getWidth(underlyingObject: T): Float { + return underlyingObject.width.toFloat() + } + + override fun getHeight(underlyingObject: T): Float { + return underlyingObject.height.toFloat() } + + override fun getLocationOnScreen(underlyingObject: T, loc: IntArray) { + underlyingObject.getLocationOnScreen(loc) + } + } + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index e5f56d43f4d7..38da21e016ec 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -46,6 +46,9 @@ LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui # UI it doesn't own. This is necessary to allow screenshots to be taken LOCAL_CERTIFICATE := platform +LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest.xml +LOCAL_MANIFEST_FILE := AndroidManifest-base.xml + # Provide jack a list of classes to exclude from code coverage. # This is needed because the SystemUITests compile SystemUI source directly, rather than using # LOCAL_INSTRUMENTATION_FOR := SystemUI. diff --git a/packages/SystemUI/tests/AndroidManifest-base.xml b/packages/SystemUI/tests/AndroidManifest-base.xml new file mode 100644 index 000000000000..f08d3d2f48d4 --- /dev/null +++ b/packages/SystemUI/tests/AndroidManifest-base.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:sharedUserId="android.uid.system" + package="com.android.systemui.tests" /> diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index c51624b0994b..12c704881909 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -18,7 +18,7 @@ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:tools="http://schemas.android.com/tools" android:sharedUserId="android.uid.system" - package="com.android.systemui.tests"> + package="com.android.systemui" > <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" /> @@ -55,11 +55,6 @@ <application android:debuggable="true" android:largeHeap="true"> <uses-library android:name="android.test.runner" /> - <activity android:name="com.android.systemui.screenshot.ScreenshotStubActivity" /> - - <service - android:name="com.android.systemui.qs.external.TileLifecycleManagerTests$FakeTileService" - android:process=":killable" /> <receiver android:name="com.android.systemui.SliceBroadcastRelayHandlerTest$Receiver"> <intent-filter> diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java index b6ca8d8e4afb..7231b8a143d0 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java @@ -41,8 +41,8 @@ public class KeyguardPresentationTest extends SysuiTestCase { InjectionInflationController inflationController = new InjectionInflationController( SystemUIFactory.getInstance().getRootComponent()); Context context = getContext(); - KeyguardPresentation keyguardPresentation = - new KeyguardPresentation(context, context.getDisplay(), inflationController); + KeyguardPresentation keyguardPresentation = new KeyguardPresentation(context, + context.getDisplayNoVerify(), inflationController); keyguardPresentation.onCreate(null /*savedInstanceState */); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java index 3330d1e6d0a8..61991816a407 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java @@ -93,6 +93,8 @@ public final class ClockManagerTest extends SysuiTestCase { mMockPluginManager, mMockColorExtractor, mMockContentResolver, mMockCurrentUserObserable, mMockSettingsWrapper, mFakeDockManager); + mClockManager.addBuiltinClock(() -> new BubbleClockController( + getContext().getResources(), inflater, mMockColorExtractor)); mClockManager.addOnClockChangedListener(mMockListener1); mClockManager.addOnClockChangedListener(mMockListener2); reset(mMockListener1, mMockListener2); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt index 8e330c6f5049..45c51d42c250 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt @@ -42,6 +42,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) @@ -52,18 +53,20 @@ class NotificationRankingManagerTest : SysuiTestCase() { } private lateinit var personNotificationIdentifier: PeopleNotificationIdentifier private lateinit var rankingManager: TestableNotificationRankingManager + private lateinit var sectionsManager: NotificationSectionsFeatureManager @Before fun setup() { personNotificationIdentifier = mock(PeopleNotificationIdentifier::class.java) + sectionsManager = mock(NotificationSectionsFeatureManager::class.java) rankingManager = TestableNotificationRankingManager( lazyMedia, mock(NotificationGroupManager::class.java), mock(HeadsUpManager::class.java), mock(NotificationFilter::class.java), mock(NotificationEntryManagerLogger::class.java), - mock(NotificationSectionsFeatureManager::class.java), + sectionsManager, personNotificationIdentifier, HighPriorityProvider(personNotificationIdentifier) ) @@ -146,39 +149,42 @@ class NotificationRankingManagerTest : SysuiTestCase() { @Test fun testSort_importantPeople() { + whenever(sectionsManager.isFilteringEnabled()).thenReturn(true) val aN = Notification.Builder(mContext, "test") .setStyle(Notification.MessagingStyle("")) .build() - val aC = NotificationChannel("test", "", IMPORTANCE_DEFAULT) - aC.setConversationId("parent", "convo") val a = NotificationEntryBuilder() .setImportance(IMPORTANCE_HIGH) .setPkg("pkg") .setOpPkg("pkg") .setTag("tag") .setNotification(aN) - .setChannel(aC) + .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) .setUser(mContext.getUser()) .setOverrideGroupKey("") .build() + whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking)) + .thenReturn(false) + whenever(personNotificationIdentifier.isPeopleNotification(a.sbn, a.ranking)) + .thenReturn(true) val bN = Notification.Builder(mContext, "test") .setStyle(Notification.MessagingStyle("")) .build() - val bC = NotificationChannel("test", "", IMPORTANCE_DEFAULT) - bC.setConversationId("parent", "convo") - bC.setImportantConversation(true) val b = NotificationEntryBuilder() .setImportance(IMPORTANCE_HIGH) .setPkg("pkg2") .setOpPkg("pkg2") .setTag("tag") .setNotification(bN) - .setChannel(bC) + .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) .setUser(mContext.getUser()) .setOverrideGroupKey("") .build() - + whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking)) + .thenReturn(false) + whenever(personNotificationIdentifier.isPeopleNotification(a.sbn, a.ranking)) + .thenReturn(true) assertEquals( listOf(b, a), diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt new file mode 100644 index 000000000000..f1672b1c644d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.util.magnetictarget + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.MotionEvent +import android.view.View +import androidx.dynamicanimation.animation.FloatPropertyCompat +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.animation.PhysicsAnimatorTestUtils +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.ArgumentMatchers.anyFloat +import org.mockito.Mockito +import org.mockito.Mockito.`when` +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions + +@TestableLooper.RunWithLooper +@RunWith(AndroidTestingRunner::class) +@SmallTest +class MagnetizedObjectTest : SysuiTestCase() { + /** Incrementing value for fake MotionEvent timestamps. */ + private var time = 0L + + /** Value to add to each new MotionEvent's timestamp. */ + private var timeStep = 100 + + private val underlyingObject = this + + private lateinit var targetView: View + + private val targetSize = 200 + private val targetCenterX = 500 + private val targetCenterY = 900 + private val magneticFieldRadius = 200 + + private var objectX = 0f + private var objectY = 0f + private val objectSize = 50f + + private lateinit var magneticTarget: MagnetizedObject.MagneticTarget + private lateinit var magnetizedObject: MagnetizedObject<*> + private lateinit var magnetListener: MagnetizedObject.MagnetListener + + private val xProperty = object : FloatPropertyCompat<MagnetizedObjectTest>("") { + override fun setValue(target: MagnetizedObjectTest?, value: Float) { + objectX = value + } + override fun getValue(target: MagnetizedObjectTest?): Float { + return objectX + } + } + + private val yProperty = object : FloatPropertyCompat<MagnetizedObjectTest>("") { + override fun setValue(target: MagnetizedObjectTest?, value: Float) { + objectY = value + } + + override fun getValue(target: MagnetizedObjectTest?): Float { + return objectY + } + } + + @Before + fun setup() { + PhysicsAnimatorTestUtils.prepareForTest() + + // Mock the view since a real view's getLocationOnScreen() won't work unless it's attached + // to a real window (it'll always return x = 0, y = 0). + targetView = mock(View::class.java) + `when`(targetView.context).thenReturn(context) + + // The mock target view will pretend that it's 200x200, and at (400, 800). This means it's + // occupying the bounds (400, 800, 600, 1000) and it has a center of (500, 900). + `when`(targetView.width).thenReturn(targetSize) // width = 200 + `when`(targetView.height).thenReturn(targetSize) // height = 200 + doAnswer { invocation -> + (invocation.arguments[0] as IntArray).also { location -> + // Return the top left of the target. + location[0] = targetCenterX - targetSize / 2 // x = 400 + location[1] = targetCenterY - targetSize / 2 // y = 800 + } + }.`when`(targetView).getLocationOnScreen(ArgumentMatchers.any()) + `when`(targetView.context).thenReturn(context) + + magneticTarget = MagnetizedObject.MagneticTarget(targetView, magneticFieldRadius) + + magnetListener = mock(MagnetizedObject.MagnetListener::class.java) + magnetizedObject = object : MagnetizedObject<MagnetizedObjectTest>( + context, underlyingObject, xProperty, yProperty) { + override fun getWidth(underlyingObject: MagnetizedObjectTest): Float { + return objectSize + } + + override fun getHeight(underlyingObject: MagnetizedObjectTest): Float { + return objectSize + } + + override fun getLocationOnScreen( + underlyingObject: MagnetizedObjectTest, + loc: IntArray + ) { + loc[0] = objectX.toInt() + loc[1] = objectY.toInt() } + } + + magnetizedObject.magnetListener = magnetListener + magnetizedObject.addTarget(magneticTarget) + + timeStep = 100 + } + + @Test + fun testMotionEventConsumption() { + // Start at (0, 0). No magnetic field here. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = 0, y = 0, action = MotionEvent.ACTION_DOWN))) + + // Move to (400, 400), which is solidly outside the magnetic field. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = 200, y = 200))) + + // Move to (305, 705). This would be in the magnetic field radius if magnetic fields were + // square. It's not, because they're not. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = targetCenterX - magneticFieldRadius + 5, + y = targetCenterY - magneticFieldRadius + 5))) + + // Move to (400, 800). That's solidly in the radius so the magnetic target should begin + // consuming events. + assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = targetCenterX - 100, + y = targetCenterY - 100))) + + // Release at (400, 800). Since we're in the magnetic target, it should return true and + // consume the ACTION_UP. + assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = 400, y = 800, action = MotionEvent.ACTION_UP))) + + // ACTION_DOWN outside the field. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = 200, y = 200, action = MotionEvent.ACTION_DOWN))) + + // Move to the center. We absolutely should consume events there. + assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = targetCenterX, + y = targetCenterY))) + + // Drag out to (0, 0) and we should be returning false again. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = 0, y = 0))) + + // The ACTION_UP event shouldn't be consumed either since it's outside the field. + assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = 0, y = 0, action = MotionEvent.ACTION_UP))) + } + + @Test + fun testMotionEventConsumption_downInMagneticField() { + // We should consume DOWN events if they occur in the field. + assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_DOWN))) + } + + @Test + fun testMoveIntoAroundAndOutOfMagneticField() { + // Move around but don't touch the magnetic field. + dispatchMotionEvents( + getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN), + getMotionEvent(x = 100, y = 100), + getMotionEvent(x = 200, y = 200)) + + // You can't become unstuck if you were never stuck in the first place. + verify(magnetListener, never()).onStuckToTarget(magneticTarget) + verify(magnetListener, never()).onUnstuckFromTarget( + eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(), + eq(false)) + + // Move into and then around inside the magnetic field. + dispatchMotionEvents( + getMotionEvent(x = targetCenterX - 100, y = targetCenterY - 100), + getMotionEvent(x = targetCenterX, y = targetCenterY), + getMotionEvent(x = targetCenterX + 100, y = targetCenterY + 100)) + + // We should only have received one call to onStuckToTarget and none to unstuck. + verify(magnetListener, times(1)).onStuckToTarget(magneticTarget) + verify(magnetListener, never()).onUnstuckFromTarget( + eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(), + eq(false)) + + // Move out of the field and then release. + dispatchMotionEvents( + getMotionEvent(x = 100, y = 100), + getMotionEvent(x = 100, y = 100, action = MotionEvent.ACTION_UP)) + + // We should have received one unstuck call and no more stuck calls. We also should never + // have received an onReleasedInTarget call. + verify(magnetListener, times(1)).onUnstuckFromTarget( + eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(), + eq(false)) + verifyNoMoreInteractions(magnetListener) + } + + @Test + fun testMoveIntoOutOfAndBackIntoMagneticField() { + // Move into the field + dispatchMotionEvents( + getMotionEvent( + x = targetCenterX - magneticFieldRadius, + y = targetCenterY - magneticFieldRadius, + action = MotionEvent.ACTION_DOWN), + getMotionEvent( + x = targetCenterX, y = targetCenterY)) + + verify(magnetListener, times(1)).onStuckToTarget(magneticTarget) + verify(magnetListener, never()).onReleasedInTarget(magneticTarget) + + // Move back out. + dispatchMotionEvents( + getMotionEvent( + x = targetCenterX - magneticFieldRadius, + y = targetCenterY - magneticFieldRadius)) + + verify(magnetListener, times(1)).onUnstuckFromTarget( + eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(), + eq(false)) + verify(magnetListener, never()).onReleasedInTarget(magneticTarget) + + // Move in again and release in the magnetic field. + dispatchMotionEvents( + getMotionEvent(x = targetCenterX - 100, y = targetCenterY - 100), + getMotionEvent(x = targetCenterX + 50, y = targetCenterY + 50), + getMotionEvent(x = targetCenterX, y = targetCenterY), + getMotionEvent( + x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_UP)) + + verify(magnetListener, times(2)).onStuckToTarget(magneticTarget) + verify(magnetListener).onReleasedInTarget(magneticTarget) + verifyNoMoreInteractions(magnetListener) + } + + @Test + fun testFlingTowardsTarget_towardsTarget() { + timeStep = 10 + + // Forcefully fling the object towards the target (but never touch the magnetic field). + dispatchMotionEvents( + getMotionEvent( + x = targetCenterX, + y = 0, + action = MotionEvent.ACTION_DOWN), + getMotionEvent( + x = targetCenterX, + y = targetCenterY / 2), + getMotionEvent( + x = targetCenterX, + y = targetCenterY - magneticFieldRadius * 2, + action = MotionEvent.ACTION_UP)) + + // Nevertheless it should have ended up stuck to the target. + verify(magnetListener, times(1)).onStuckToTarget(magneticTarget) + } + + @Test + fun testFlingTowardsTarget_towardsButTooSlow() { + // Very, very slowly fling the object towards the target (but never touch the magnetic + // field). This value is only used to create MotionEvent timestamps, it will not block the + // test for 10 seconds. + timeStep = 10000 + dispatchMotionEvents( + getMotionEvent( + x = targetCenterX, + y = 0, + action = MotionEvent.ACTION_DOWN), + getMotionEvent( + x = targetCenterX, + y = targetCenterY / 2), + getMotionEvent( + x = targetCenterX, + y = targetCenterY - magneticFieldRadius * 2, + action = MotionEvent.ACTION_UP)) + + // No sticking should have occurred. + verifyNoMoreInteractions(magnetListener) + } + + @Test + fun testFlingTowardsTarget_missTarget() { + timeStep = 10 + // Forcefully fling the object down, but not towards the target. + dispatchMotionEvents( + getMotionEvent( + x = 0, + y = 0, + action = MotionEvent.ACTION_DOWN), + getMotionEvent( + x = 0, + y = targetCenterY / 2), + getMotionEvent( + x = 0, + y = targetCenterY - magneticFieldRadius * 2, + action = MotionEvent.ACTION_UP)) + + verifyNoMoreInteractions(magnetListener) + } + + @Test + fun testMagnetAnimation() { + // Make sure the object starts at (0, 0). + assertEquals(0f, objectX) + assertEquals(0f, objectY) + + // Trigger the magnet animation, and block the test until it ends. + PhysicsAnimatorTestUtils.setAllAnimationsBlock(true) + magnetizedObject.maybeConsumeMotionEvent(getMotionEvent( + x = targetCenterX, + y = targetCenterY, + action = MotionEvent.ACTION_DOWN)) + + // The object's (top-left) position should now position it centered over the target. + assertEquals(targetCenterX - objectSize / 2, objectX) + assertEquals(targetCenterY - objectSize / 2, objectY) + } + + @Test + fun testMultipleTargets() { + val secondMagneticTarget = getSecondMagneticTarget() + + // Drag into the second target. + dispatchMotionEvents( + getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN), + getMotionEvent(x = 100, y = 900)) + + // Verify that we received an onStuck for the second target, and no others. + verify(magnetListener).onStuckToTarget(secondMagneticTarget) + verifyNoMoreInteractions(magnetListener) + + // Drag into the original target. + dispatchMotionEvents( + getMotionEvent(x = 0, y = 0), + getMotionEvent(x = 500, y = 900)) + + // We should have unstuck from the second one and stuck into the original one. + verify(magnetListener).onUnstuckFromTarget( + eq(secondMagneticTarget), anyFloat(), anyFloat(), eq(false)) + verify(magnetListener).onStuckToTarget(magneticTarget) + verifyNoMoreInteractions(magnetListener) + } + + @Test + fun testMultipleTargets_flingIntoSecond() { + val secondMagneticTarget = getSecondMagneticTarget() + + timeStep = 10 + + // Fling towards the second target. + dispatchMotionEvents( + getMotionEvent(x = 100, y = 0, action = MotionEvent.ACTION_DOWN), + getMotionEvent(x = 100, y = 350), + getMotionEvent(x = 100, y = 650, action = MotionEvent.ACTION_UP)) + + // Verify that we received an onStuck for the second target. + verify(magnetListener).onStuckToTarget(secondMagneticTarget) + + // Fling towards the first target. + dispatchMotionEvents( + getMotionEvent(x = 300, y = 0, action = MotionEvent.ACTION_DOWN), + getMotionEvent(x = 400, y = 350), + getMotionEvent(x = 500, y = 650, action = MotionEvent.ACTION_UP)) + + // Verify that we received onStuck for the original target. + verify(magnetListener).onStuckToTarget(magneticTarget) + } + + private fun getSecondMagneticTarget(): MagnetizedObject.MagneticTarget { + // The first target view is at bounds (400, 800, 600, 1000) and it has a center of + // (500, 900). We'll add a second one at bounds (0, 800, 200, 1000) with center (100, 900). + val secondTargetView = mock(View::class.java) + var secondTargetCenterX = 100 + var secondTargetCenterY = 900 + + `when`(secondTargetView.context).thenReturn(context) + `when`(secondTargetView.width).thenReturn(targetSize) // width = 200 + `when`(secondTargetView.height).thenReturn(targetSize) // height = 200 + doAnswer { invocation -> + (invocation.arguments[0] as IntArray).also { location -> + // Return the top left of the target. + location[0] = secondTargetCenterX - targetSize / 2 // x = 0 + location[1] = secondTargetCenterY - targetSize / 2 // y = 800 + } + }.`when`(secondTargetView).getLocationOnScreen(ArgumentMatchers.any()) + + return magnetizedObject.addTarget(secondTargetView, magneticFieldRadius) + } + + /** + * Return a MotionEvent at the given coordinates, with the given action (or MOVE by default). + * The event's time fields will be incremented by 10ms each time this is called, so tha + * VelocityTracker works. + */ + private fun getMotionEvent( + x: Int, + y: Int, + action: Int = MotionEvent.ACTION_MOVE + ): MotionEvent { + return MotionEvent.obtain(time, time, action, x.toFloat(), y.toFloat(), 0) + .also { time += timeStep } + } + + /** Dispatch all of the provided events to the target view. */ + private fun dispatchMotionEvents(vararg events: MotionEvent) { + events.forEach { magnetizedObject.maybeConsumeMotionEvent(it) } + } + + /** Prevents Kotlin from being mad that eq() is nullable. */ + private fun <T> eq(value: T): T = Mockito.eq(value) ?: value +}
\ No newline at end of file diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp index cb0de7a860ac..1a3d5b659ee3 100644 --- a/packages/Tethering/common/TetheringLib/Android.bp +++ b/packages/Tethering/common/TetheringLib/Android.bp @@ -83,7 +83,6 @@ java_library { name: "framework-tethering-stubs", srcs: [":framework-tethering-stubs-sources"], libs: ["framework-all"], - static_libs: ["tethering-aidl-interfaces-java"], sdk_version: "core_platform", } diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java index df87ac994d42..a18f5da60cad 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java @@ -33,6 +33,9 @@ import android.os.ResultReceiver; */ @SystemApi(client = MODULE_LIBRARIES) public class TetheringConstants { + /** An explicit private class to avoid exposing constructor.*/ + private TetheringConstants() { } + /** * Extra used for communicating with the TetherService. Includes the type of tethering to * enable if any. diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 6261def6a939..72fe95b227a2 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -314,9 +314,13 @@ public class Tethering { startStateMachineUpdaters(mHandler); startTrackDefaultNetwork(); - getWifiManager().registerSoftApCallback( - mHandler::post /* executor */, - new TetheringSoftApCallback()); + + final WifiManager wifiManager = getWifiManager(); + if (wifiManager != null) { + wifiManager.registerSoftApCallback( + mHandler::post /* executor */, + new TetheringSoftApCallback()); + } } private void startStateMachineUpdaters(Handler handler) { diff --git a/packages/WallpaperCropper/src/com/android/photos/views/TiledImageRenderer.java b/packages/WallpaperCropper/src/com/android/photos/views/TiledImageRenderer.java index fcb113e90720..210fdc662bed 100644 --- a/packages/WallpaperCropper/src/com/android/photos/views/TiledImageRenderer.java +++ b/packages/WallpaperCropper/src/com/android/photos/views/TiledImageRenderer.java @@ -163,7 +163,7 @@ public class TiledImageRenderer { private static boolean isHighResolution(Context context) { DisplayMetrics metrics = new DisplayMetrics(); - context.getDisplay().getMetrics(metrics); + context.getDisplayNoVerify().getMetrics(metrics); return metrics.heightPixels > 2048 || metrics.widthPixels > 2048; } diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index eda3fb9c8698..cff55046fc2b 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -252,6 +252,9 @@ message SystemMessage { // Package: android NOTE_ID_WIFI_SIM_REQUIRED = 60; + // Inform the user a foreground service while-in-use permission is restricted. + NOTE_FOREGROUND_SERVICE_WHILE_IN_USE_PERMISSION = 61; + // ADD_NEW_IDS_ABOVE_THIS_LINE // Legacy IDs with arbitrary values appear below // Legacy IDs existed as stable non-conflicting constants prior to the O release diff --git a/services/Android.bp b/services/Android.bp index c77e75da66ba..db6e21a62ff3 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -114,9 +114,10 @@ droidstubs { name: "services-stubs.sources", srcs: [":services-all-sources"], installable: false, - // TODO: remove the --hide options below args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)" + " --hide-annotation android.annotation.Hide" + + " --hide InternalClasses" + // com.android.* classes are okay in this interface + // TODO: remove the --hide options below " --hide-package com.google.android.startop.iorap" + " --hide ReferencesHidden" + " --hide DeprecationMismatch" + diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS index 265674a74b7e..c6f42f719caa 100644 --- a/services/accessibility/OWNERS +++ b/services/accessibility/OWNERS @@ -1,3 +1,4 @@ svetoslavganov@google.com pweaver@google.com rhedjao@google.com +qasid@google.com diff --git a/services/api/current.txt b/services/api/current.txt index 8a82e610c233..26a65f21ed83 100644 --- a/services/api/current.txt +++ b/services/api/current.txt @@ -3,9 +3,9 @@ package com.android.permission.persistence { public interface RuntimePermissionsPersistence { method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance(); - method public void delete(@NonNull android.os.UserHandle); - method @Nullable public com.android.permission.persistence.RuntimePermissionsState read(@NonNull android.os.UserHandle); - method public void write(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle); + method public void deleteAsUser(@NonNull android.os.UserHandle); + method @Nullable public com.android.permission.persistence.RuntimePermissionsState readAsUser(@NonNull android.os.UserHandle); + method public void writeAsUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle); } public final class RuntimePermissionsState { @@ -30,9 +30,9 @@ package com.android.role.persistence { public interface RolesPersistence { method @NonNull public static com.android.role.persistence.RolesPersistence createInstance(); - method public void delete(@NonNull android.os.UserHandle); - method @Nullable public com.android.role.persistence.RolesState read(@NonNull android.os.UserHandle); - method public void write(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle); + method public void deleteAsUser(@NonNull android.os.UserHandle); + method @Nullable public com.android.role.persistence.RolesState readAsUser(@NonNull android.os.UserHandle); + method public void writeAsUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle); } public final class RolesState { diff --git a/services/api/lint-baseline.txt b/services/api/lint-baseline.txt index 0b8658cf469d..e985ddb5352b 100644 --- a/services/api/lint-baseline.txt +++ b/services/api/lint-baseline.txt @@ -1,35 +1,5 @@ // Baseline format: 1.0 -InternalClasses: com.android.permission.persistence.RuntimePermissionsPersistence: - Internal classes must not be exposed -InternalClasses: com.android.permission.persistence.RuntimePermissionsState: - Internal classes must not be exposed -InternalClasses: com.android.permission.persistence.RuntimePermissionsState.PermissionState: - Internal classes must not be exposed -InternalClasses: com.android.role.persistence.RolesPersistence: - Internal classes must not be exposed -InternalClasses: com.android.role.persistence.RolesState: - Internal classes must not be exposed -InternalClasses: com.android.server.SystemService: - Internal classes must not be exposed -InternalClasses: com.android.server.SystemService.TargetUser: - Internal classes must not be exposed - - ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder): Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder)} ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder, boolean): Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder,boolean)} - - -UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#delete(android.os.UserHandle): - Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `delete` -UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#read(android.os.UserHandle): - Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `read` -UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#write(com.android.permission.persistence.RuntimePermissionsState, android.os.UserHandle): - Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `write` -UserHandleName: com.android.role.persistence.RolesPersistence#delete(android.os.UserHandle): - Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `delete` -UserHandleName: com.android.role.persistence.RolesPersistence#read(android.os.UserHandle): - Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `read` -UserHandleName: com.android.role.persistence.RolesPersistence#write(com.android.role.persistence.RolesState, android.os.UserHandle): - Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `write` diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 7151d2b86e83..a8a27916f56a 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -281,7 +281,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } private void computeMaximumWidgetBitmapMemory() { - Display display = mContext.getDisplay(); + Display display = mContext.getDisplayNoVerify(); Point size = new Point(); display.getRealSize(size); // Cap memory usage at 1.5 times the size of the display diff --git a/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java b/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java index 813fc8d5f561..14bd7d78f3bf 100644 --- a/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java +++ b/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java @@ -75,7 +75,7 @@ public class CustomScrollView extends ScrollView { final TypedValue typedValue = new TypedValue(); final Point point = new Point(); final Context context = getContext(); - context.getDisplay().getSize(point); + context.getDisplayNoVerify().getSize(point); context.getTheme().resolveAttribute(R.attr.autofillSaveCustomSubtitleMaxHeight, typedValue, true); final View child = getChildAt(0); diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 5dc43ef8ad56..344b92f43089 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -165,7 +165,7 @@ final class FillUi { // In full screen we only initialize size once assuming screen size never changes if (mFullScreen) { final Point outPoint = mTempPoint; - mContext.getDisplay().getSize(outPoint); + mContext.getDisplayNoVerify().getSize(outPoint); // full with of screen and half height of screen mContentWidth = LayoutParams.MATCH_PARENT; mContentHeight = outPoint.y / 2; @@ -559,7 +559,7 @@ final class FillUi { } private static void resolveMaxWindowSize(Context context, Point outPoint) { - context.getDisplay().getSize(outPoint); + context.getDisplayNoVerify().getSize(outPoint); final TypedValue typedValue = sTempTypedValue; context.getTheme().resolveAttribute(R.attr.autofillDatasetPickerMaxWidth, typedValue, true); diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 3c37f737f8be..e434be648dcb 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -76,9 +76,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; @@ -802,6 +800,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { final int size = in.getDataSize(); if (excludedKeysForPackage != null && excludedKeysForPackage.contains(key)) { + Slog.i(TAG, "Skipping blocked key " + key); in.skipEntityData(); continue; } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index f7eabac3b21f..e48ef5a528be 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1534,7 +1534,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override - public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) { + public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser( + int userId, String callingPackageName) { // The basic principle is: if an app's traffic could possibly go over a // network, without the app doing anything multinetwork-specific, // (hence, by "default"), then include that network's capabilities in @@ -1556,7 +1557,10 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkAgentInfo nai = getDefaultNetwork(); NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai); if (nc != null) { - result.put(nai.network, nc); + result.put( + nai.network, + maybeSanitizeLocationInfoForCaller( + nc, Binder.getCallingUid(), callingPackageName)); } synchronized (mVpns) { @@ -1566,10 +1570,12 @@ public class ConnectivityService extends IConnectivityManager.Stub Network[] networks = vpn.getUnderlyingNetworks(); if (networks != null) { for (Network network : networks) { - nai = getNetworkAgentInfoForNetwork(network); - nc = getNetworkCapabilitiesInternal(nai); + nc = getNetworkCapabilitiesInternal(network); if (nc != null) { - result.put(network, nc); + result.put( + network, + maybeSanitizeLocationInfoForCaller( + nc, Binder.getCallingUid(), callingPackageName)); } } } @@ -1636,20 +1642,26 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private NetworkCapabilities getNetworkCapabilitiesInternal(Network network) { + return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network)); + } + private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) { if (nai == null) return null; synchronized (nai) { if (nai.networkCapabilities == null) return null; return networkCapabilitiesRestrictedForCallerPermissions( - nai.networkCapabilities, - Binder.getCallingPid(), Binder.getCallingUid()); + nai.networkCapabilities, Binder.getCallingPid(), Binder.getCallingUid()); } } @Override - public NetworkCapabilities getNetworkCapabilities(Network network) { + public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) { + mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName); enforceAccessPermission(); - return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network)); + return maybeSanitizeLocationInfoForCaller( + getNetworkCapabilitiesInternal(network), + Binder.getCallingUid(), callingPackageName); } @VisibleForTesting @@ -1665,20 +1677,34 @@ public class ConnectivityService extends IConnectivityManager.Stub } newNc.setAdministratorUids(Collections.EMPTY_LIST); - maybeSanitizeLocationInfoForCaller(newNc, callerUid); - return newNc; } - private void maybeSanitizeLocationInfoForCaller( - NetworkCapabilities nc, int callerUid) { - // TODO(b/142072839): Conditionally reset the owner UID if the following - // conditions are not met: - // 1. The destination app is the network owner - // 2. The destination app has the ACCESS_COARSE_LOCATION permission granted - // if target SDK<29 or otherwise has the ACCESS_FINE_LOCATION permission granted - // 3. The user's location toggle is on - nc.setOwnerUid(INVALID_UID); + @VisibleForTesting + @Nullable + NetworkCapabilities maybeSanitizeLocationInfoForCaller( + @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) { + if (nc == null) { + return null; + } + final NetworkCapabilities newNc = new NetworkCapabilities(nc); + if (callerUid != newNc.getOwnerUid()) { + newNc.setOwnerUid(INVALID_UID); + return newNc; + } + + Binder.withCleanCallingIdentity( + () -> { + if (!mLocationPermissionChecker.checkLocationPermission( + callerPkgName, null /* featureId */, callerUid, null /* message */)) { + // Caller does not have the requisite location permissions. Reset the + // owner's UID in the NetworkCapabilities. + newNc.setOwnerUid(INVALID_UID); + } + } + ); + + return newNc; } private LinkProperties linkPropertiesRestrictedForCallerPermissions( @@ -1753,7 +1779,7 @@ public class ConnectivityService extends IConnectivityManager.Stub public boolean isActiveNetworkMetered() { enforceAccessPermission(); - final NetworkCapabilities caps = getNetworkCapabilities(getActiveNetwork()); + final NetworkCapabilities caps = getNetworkCapabilitiesInternal(getActiveNetwork()); if (caps != null) { return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); } else { @@ -5330,8 +5356,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } public String toString() { - return "uid/pid:" + mUid + "/" + mPid + " " + request + - (mPendingIntent == null ? "" : " to trigger " + mPendingIntent); + return "uid/pid:" + mUid + "/" + mPid + " " + request + + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent); } } @@ -6408,8 +6434,13 @@ public class ConnectivityService extends IConnectivityManager.Stub } switch (notificationType) { case ConnectivityManager.CALLBACK_AVAILABLE: { - putParcelable(bundle, networkCapabilitiesRestrictedForCallerPermissions( - networkAgent.networkCapabilities, nri.mPid, nri.mUid)); + final NetworkCapabilities nc = + networkCapabilitiesRestrictedForCallerPermissions( + networkAgent.networkCapabilities, nri.mPid, nri.mUid); + putParcelable( + bundle, + maybeSanitizeLocationInfoForCaller( + nc, nri.mUid, nri.request.getRequestorPackageName())); putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions( networkAgent.linkProperties, nri.mPid, nri.mUid)); // For this notification, arg1 contains the blocked status. @@ -6422,9 +6453,13 @@ public class ConnectivityService extends IConnectivityManager.Stub } case ConnectivityManager.CALLBACK_CAP_CHANGED: { // networkAgent can't be null as it has been accessed a few lines above. - final NetworkCapabilities nc = networkCapabilitiesRestrictedForCallerPermissions( - networkAgent.networkCapabilities, nri.mPid, nri.mUid); - putParcelable(bundle, nc); + final NetworkCapabilities netCap = + networkCapabilitiesRestrictedForCallerPermissions( + networkAgent.networkCapabilities, nri.mPid, nri.mUid); + putParcelable( + bundle, + maybeSanitizeLocationInfoForCaller( + netCap, nri.mUid, nri.request.getRequestorPackageName())); break; } case ConnectivityManager.CALLBACK_IP_CHANGED: { diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 5db5115b4afe..d515332815d6 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -862,10 +862,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - public void requestSetAllowed(boolean allowed) { - mProvider.requestSetAllowed(allowed); - } - public void onUserStarted(int userId) { synchronized (mLock) { // clear the user's enabled state in order to force a reevalution of whether the @@ -2931,18 +2927,6 @@ public class LocationManagerService extends ILocationManager.Stub { private class LocalService extends LocationManagerInternal { @Override - public void requestSetProviderAllowed(String provider, boolean allowed) { - Preconditions.checkArgument(provider != null, "invalid null provider"); - - synchronized (mLock) { - LocationProviderManager manager = getLocationProviderManager(provider); - if (manager != null) { - manager.requestSetAllowed(allowed); - } - } - } - - @Override public boolean isProviderEnabledForUser(@NonNull String provider, int userId) { synchronized (mLock) { LocationProviderManager manager = getLocationProviderManager(provider); diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index a1ccd8459c69..8900eee6f50f 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -100,7 +100,7 @@ public class Watchdog extends Thread { "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service "media.swcodec", // /apex/com.android.media.swcodec/bin/mediaswcodec "com.android.bluetooth", // Bluetooth service - "/system/bin/statsd", // Stats daemon + "/apex/com.android.os.statsd/bin/statsd", // Stats daemon }; public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList( diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 50f43b5c1bae..2bcb28de5d9c 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -62,7 +62,6 @@ import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; -import android.content.res.Resources; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -91,7 +90,6 @@ import android.util.SparseArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.webkit.WebViewZygote; -import android.widget.Toast; import com.android.internal.R; import com.android.internal.app.procstats.ServiceState; @@ -4687,20 +4685,35 @@ public final class ActiveServices { } // TODO: remove this toast after feature development is done - private void showWhileInUsePermissionInFgsBlockedToastLocked(String callingPackage) { - final Resources res = mAm.mContext.getResources(); - final String toastMsg = res.getString( - R.string.allow_while_in_use_permission_in_fgs, callingPackage); - mAm.mUiHandler.post(() -> { - Toast.makeText(mAm.mContext, toastMsg, Toast.LENGTH_LONG).show(); - }); + private void showWhileInUsePermissionInFgsBlockedNotificationLocked(String callingPackage, + String detailInfo) { + final Context context = mAm.mContext; + final String title = "Foreground Service While-in-use Permission Restricted"; + final String content = "App affected:" + callingPackage + ", please file a bug report"; + Notification.Builder n = + new Notification.Builder(context, + SystemNotificationChannels.ALERTS) + .setSmallIcon(R.drawable.stat_sys_vitals) + .setWhen(0) + .setColor(context.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setTicker(title) + .setContentTitle(title) + .setContentText(content) + .setStyle(new Notification.BigTextStyle().bigText(detailInfo)); + final NotificationManager notificationManager = + (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE); + notificationManager.notifyAsUser(null, + SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICE_WHILE_IN_USE_PERMISSION, + n.build(), UserHandle.ALL); } // TODO: remove this toast after feature development is done // show a toast message to ask user to file a bugreport so we know how many apps are impacted by // the new background started foreground service while-in-use permission restriction. - void showWhileInUseDebugToastLocked(int uid, int op, int mode) { - StringBuilder sb = new StringBuilder(); + void showWhileInUseDebugNotificationLocked(int uid, int op, int mode) { + StringBuilder packageNameBuilder = new StringBuilder(); + StringBuilder detailInfoBuilder = new StringBuilder(); for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) { ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i); if (pr.uid != uid) { @@ -4713,17 +4726,22 @@ public final class ActiveServices { } if (!r.mAllowWhileInUsePermissionInFgs && r.mInfoDenyWhileInUsePermissionInFgs != null) { - Slog.wtf(TAG, r.mInfoDenyWhileInUsePermissionInFgs - + " affected while-use-permission:" + AppOpsManager.opToPublicName(op)); - sb.append(r.mRecentCallingPackage + " "); + final String msg = r.mInfoDenyWhileInUsePermissionInFgs + + " affected while-in-use permission:" + + AppOpsManager.opToPublicName(op); + Slog.wtf(TAG, msg); + packageNameBuilder.append(r.mRecentCallingPackage + " "); + detailInfoBuilder.append(msg); + detailInfoBuilder.append("\n"); } } } - final String callingPackageStr = sb.toString(); + final String callingPackageStr = packageNameBuilder.toString(); if (mAm.mConstants.mFlagForegroundServiceStartsLoggingEnabled && !callingPackageStr.isEmpty()) { - showWhileInUsePermissionInFgsBlockedToastLocked(callingPackageStr); + showWhileInUsePermissionInFgsBlockedNotificationLocked(callingPackageStr, + detailInfoBuilder.toString()); } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 6bf5aa3f19d8..a529f24eff1c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -7271,7 +7271,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Wait for the provider to be published... final long timeout = - SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS; + SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS; boolean timedOut = false; synchronized (cpr) { while (cpr.provider == null) { @@ -14610,6 +14610,11 @@ public class ActivityManagerService extends IActivityManager.Stub if (index < 0) { ProcessList.remove(app.pid); } + + // Remove provider publish timeout because we will start a new timeout when the + // restarted process is attaching (if the process contains launching providers). + mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, app); + mProcessList.addProcessNameLocked(app); app.pendingStart = false; mProcessList.startProcessLocked(app, @@ -19338,7 +19343,8 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void showWhileInUseDebugToast(int uid, int op, int mode) { synchronized (ActivityManagerService.this) { - ActivityManagerService.this.mServices.showWhileInUseDebugToastLocked(uid, op, mode); + ActivityManagerService.this.mServices.showWhileInUseDebugNotificationLocked( + uid, op, mode); } } } diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index a03f0bb4e399..48ceba976502 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -16,6 +16,7 @@ package com.android.server.am; +import android.annotation.NonNull; import android.app.ActivityThread; import android.content.Context; import android.database.ContentObserver; @@ -32,6 +33,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; /** * Helper class for watching a set of core settings which the framework @@ -42,16 +44,20 @@ import java.util.Map; final class CoreSettingsObserver extends ContentObserver { private static final String LOG_TAG = CoreSettingsObserver.class.getSimpleName(); - private static class DeviceConfigEntry { + private static class DeviceConfigEntry<T> { String namespace; String flag; String coreSettingKey; - Class<?> type; - DeviceConfigEntry(String namespace, String flag, String coreSettingKey, Class<?> type) { + Class<T> type; + T defaultValue; + + DeviceConfigEntry(String namespace, String flag, String coreSettingKey, Class<T> type, + @NonNull T defaultValue) { this.namespace = namespace; this.flag = flag; this.coreSettingKey = coreSettingKey; this.type = type; + this.defaultValue = Objects.requireNonNull(defaultValue); } } @@ -105,24 +111,34 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, String.class); // add other global settings here... - sDeviceConfigEntries.add(new DeviceConfigEntry( - DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_CURSOR_CONTROL, - WidgetFlags.KEY_ENABLE_CURSOR_CONTROL, boolean.class)); - sDeviceConfigEntries.add(new DeviceConfigEntry( + sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>( + DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE, + WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, boolean.class, + WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT)); + sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>( + DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES, + WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES, boolean.class, + WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES_DEFAULT)); + sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>( DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.INSERTION_HANDLE_DELTA_HEIGHT, - WidgetFlags.KEY_INSERTION_HANDLE_DELTA_HEIGHT, int.class)); - sDeviceConfigEntries.add(new DeviceConfigEntry( + WidgetFlags.KEY_INSERTION_HANDLE_DELTA_HEIGHT, int.class, + WidgetFlags.INSERTION_HANDLE_DELTA_HEIGHT_DEFAULT)); + sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>( DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.INSERTION_HANDLE_OPACITY, - WidgetFlags.KEY_INSERTION_HANDLE_OPACITY, int.class)); - sDeviceConfigEntries.add(new DeviceConfigEntry( + WidgetFlags.KEY_INSERTION_HANDLE_OPACITY, int.class, + WidgetFlags.INSERTION_HANDLE_OPACITY_DEFAULT)); + sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>( DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_NEW_MAGNIFIER, - WidgetFlags.KEY_ENABLE_NEW_MAGNIFIER, boolean.class)); - sDeviceConfigEntries.add(new DeviceConfigEntry( + WidgetFlags.KEY_ENABLE_NEW_MAGNIFIER, boolean.class, + WidgetFlags.ENABLE_NEW_MAGNIFIER_DEFAULT)); + sDeviceConfigEntries.add(new DeviceConfigEntry<Float>( DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.MAGNIFIER_ZOOM_FACTOR, - WidgetFlags.KEY_MAGNIFIER_ZOOM_FACTOR, float.class)); - sDeviceConfigEntries.add(new DeviceConfigEntry( + WidgetFlags.KEY_MAGNIFIER_ZOOM_FACTOR, float.class, + WidgetFlags.MAGNIFIER_ZOOM_FACTOR_DEFAULT)); + sDeviceConfigEntries.add(new DeviceConfigEntry<Float>( DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.MAGNIFIER_ASPECT_RATIO, - WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO, float.class)); + WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO, float.class, + WidgetFlags.MAGNIFIER_ASPECT_RATIO_DEFAULT)); // add other device configs here... } @@ -216,23 +232,29 @@ final class CoreSettingsObserver extends ContentObserver { } } + @SuppressWarnings("unchecked") private void populateSettingsFromDeviceConfig() { - for (DeviceConfigEntry entry : sDeviceConfigEntries) { + for (DeviceConfigEntry<?> entry : sDeviceConfigEntries) { if (entry.type == String.class) { + String defaultValue = ((DeviceConfigEntry<String>) entry).defaultValue; mCoreSettings.putString(entry.coreSettingKey, - DeviceConfig.getString(entry.namespace, entry.flag, "")); + DeviceConfig.getString(entry.namespace, entry.flag, defaultValue)); } else if (entry.type == int.class) { + int defaultValue = ((DeviceConfigEntry<Integer>) entry).defaultValue; mCoreSettings.putInt(entry.coreSettingKey, - DeviceConfig.getInt(entry.namespace, entry.flag, 0)); + DeviceConfig.getInt(entry.namespace, entry.flag, defaultValue)); } else if (entry.type == float.class) { + float defaultValue = ((DeviceConfigEntry<Float>) entry).defaultValue; mCoreSettings.putFloat(entry.coreSettingKey, - DeviceConfig.getFloat(entry.namespace, entry.flag, 0)); + DeviceConfig.getFloat(entry.namespace, entry.flag, defaultValue)); } else if (entry.type == long.class) { + long defaultValue = ((DeviceConfigEntry<Long>) entry).defaultValue; mCoreSettings.putLong(entry.coreSettingKey, - DeviceConfig.getLong(entry.namespace, entry.flag, 0)); + DeviceConfig.getLong(entry.namespace, entry.flag, defaultValue)); } else if (entry.type == boolean.class) { + boolean defaultValue = ((DeviceConfigEntry<Boolean>) entry).defaultValue; mCoreSettings.putInt(entry.coreSettingKey, - DeviceConfig.getBoolean(entry.namespace, entry.flag, false) ? 1 : 0); + DeviceConfig.getBoolean(entry.namespace, entry.flag, defaultValue) ? 1 : 0); } } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 0dc44f79d19f..22559c426348 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -345,6 +345,14 @@ public final class ProcessList { @EnabledAfter(targetSdkVersion = VersionCodes.Q) private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id. + /** + * Apps have no access to the private data directories of any other app, even if the other + * app has made them world-readable. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = VersionCodes.Q) + private static final long APP_DATA_DIRECTORY_ISOLATION = 143937733; // See b/143937733 + ActivityManagerService mService = null; // To kill process groups asynchronously @@ -2070,7 +2078,14 @@ public final class ProcessList { } final int minTargetSdk = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, ANDROID_APP_DATA_ISOLATION_MIN_SDK, Build.VERSION_CODES.R); - return app.info.targetSdkVersion >= minTargetSdk; + if (app.info.targetSdkVersion < minTargetSdk) { + return false; + } + + // TODO(b/147266020): Remove non-standard gating above & switch to isChangeEnabled. + mPlatformCompat.reportChange(APP_DATA_DIRECTORY_ISOLATION, app.info); + + return true; } private Map<String, Pair<String, Long>> getPackageAppDataInfoMap(PackageManagerInternal pmInt, diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 8bbeabf779d2..7cb84585a57c 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -73,7 +73,7 @@ public class DisplayModeDirector { private static final int GLOBAL_ID = -1; // The tolerance within which we consider something approximately equals. - private static final float EPSILON = 0.01f; + private static final float FLOAT_TOLERANCE = 0.01f; private final Object mLock = new Object(); private final Context mContext; @@ -267,8 +267,8 @@ public class DisplayModeDirector { // Some refresh rates are calculated based on frame timings, so they aren't *exactly* // equal to expected refresh rate. Given that, we apply a bit of tolerance to this // comparison. - if (refreshRate < (minRefreshRate - EPSILON) - || refreshRate > (maxRefreshRate + EPSILON)) { + if (refreshRate < (minRefreshRate - FLOAT_TOLERANCE) + || refreshRate > (maxRefreshRate + FLOAT_TOLERANCE)) { if (DEBUG) { Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", outside refresh rate bounds" @@ -487,12 +487,18 @@ public class DisplayModeDirector { public RefreshRateRange() {} public RefreshRateRange(float min, float max) { - if (min < 0 || max < 0 || min > max) { + if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) { Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : " + min + " " + max); this.min = this.max = 0; return; } + if (min > max) { + // Min and max are within epsilon of each other, but in the wrong order. + float t = min; + min = max; + max = t; + } this.min = min; this.max = max; } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index d7cb192fdd36..87262a844b18 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1388,6 +1388,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + private static final class UserSwitchHandlerTask implements Runnable { + final InputMethodManagerService mService; + + @UserIdInt + final int mToUserId; + + @Nullable + IInputMethodClient mClientToBeReset; + + UserSwitchHandlerTask(InputMethodManagerService service, @UserIdInt int toUserId, + @Nullable IInputMethodClient clientToBeReset) { + mService = service; + mToUserId = toUserId; + mClientToBeReset = clientToBeReset; + } + + @Override + public void run() { + synchronized (mService.mMethodMap) { + if (mService.mUserSwitchHandlerTask != this) { + // This task was already canceled before it is handled here. So do nothing. + return; + } + mService.switchUserOnHandlerLocked(mService.mUserSwitchHandlerTask.mToUserId, + mClientToBeReset); + mService.mUserSwitchHandlerTask = null; + } + } + } + + /** + * When non-{@code null}, this represents pending user-switch task, which is to be executed as + * a handler callback. This needs to be set and unset only within the lock. + */ + @Nullable + @GuardedBy("mMethodMap") + private UserSwitchHandlerTask mUserSwitchHandlerTask; + public static final class Lifecycle extends SystemService { private InputMethodManagerService mService; @@ -1406,8 +1444,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void onSwitchUser(@UserIdInt int userHandle) { // Called on ActivityManager thread. - // TODO: Dispatch this to a worker thread as needed. - mService.onSwitchUser(userHandle); + synchronized (mService.mMethodMap) { + mService.scheduleSwitchUserTaskLocked(userHandle, null /* clientToBeReset */); + } } @Override @@ -1447,10 +1486,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - void onSwitchUser(@UserIdInt int userId) { - synchronized (mMethodMap) { - switchUserLocked(userId); + @GuardedBy("mMethodMap") + void scheduleSwitchUserTaskLocked(@UserIdInt int userId, + @Nullable IInputMethodClient clientToBeReset) { + if (mUserSwitchHandlerTask != null) { + if (mUserSwitchHandlerTask.mToUserId == userId) { + mUserSwitchHandlerTask.mClientToBeReset = clientToBeReset; + return; + } + mHandler.removeCallbacks(mUserSwitchHandlerTask); } + final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId, + clientToBeReset); + mUserSwitchHandlerTask = task; + mHandler.post(task); } public InputMethodManagerService(Context context) { @@ -1538,7 +1587,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @GuardedBy("mMethodMap") - private void switchUserLocked(int newUserId) { + private void switchUserOnHandlerLocked(@UserIdInt int newUserId, + IInputMethodClient clientToBeReset) { if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId + " currentUserId=" + mSettings.getCurrentUserId()); @@ -1589,6 +1639,18 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + " selectedIme=" + mSettings.getSelectedInputMethod()); mLastSwitchUserId = newUserId; + + if (mIsInteractive && clientToBeReset != null) { + final ClientState cs = mClients.get(clientToBeReset.asBinder()); + if (cs == null) { + // The client is already gone. + return; + } + try { + cs.client.scheduleStartInputIfNecessary(mInFullscreenMode); + } catch (RemoteException e) { + } + } } void updateCurrentProfileIds() { @@ -3072,6 +3134,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return InputBindResult.NOT_IME_TARGET_WINDOW; } + if (mUserSwitchHandlerTask != null) { + // There is already an on-going pending user switch task. + final int nextUserId = mUserSwitchHandlerTask.mToUserId; + if (userId == nextUserId) { + scheduleSwitchUserTaskLocked(userId, cs.client); + return InputBindResult.USER_SWITCHING; + } + for (int profileId : mUserManager.getProfileIdsWithDisabled(nextUserId)) { + if (profileId == userId) { + scheduleSwitchUserTaskLocked(userId, cs.client); + return InputBindResult.USER_SWITCHING; + } + } + return InputBindResult.INVALID_USER; + } + // cross-profile access is always allowed here to allow profile-switching. if (!mSettings.isCurrentProfile(userId)) { Slog.w(TAG, "A background user is requesting window. Hiding IME."); @@ -3083,8 +3161,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (userId != mSettings.getCurrentUserId()) { - switchUserLocked(userId); + scheduleSwitchUserTaskLocked(userId, cs.client); + return InputBindResult.USER_SWITCHING; } + // Master feature flag that overrides other conditions and forces IME preRendering. if (DEBUG) { Slog.v(TAG, "IME PreRendering MASTER flag: " diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java index 433ec435c8d1..e9d94a5430fe 100644 --- a/services/core/java/com/android/server/location/AbstractLocationProvider.java +++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java @@ -366,21 +366,6 @@ public abstract class AbstractLocationProvider { protected abstract void onExtraCommand(int uid, int pid, String command, Bundle extras); /** - * Requests a provider to enable itself for the given user id. - */ - public final void requestSetAllowed(boolean allowed) { - // all calls into the provider must be moved onto the provider thread to prevent deadlock - mExecutor.execute( - obtainRunnable(AbstractLocationProvider::onRequestSetAllowed, this, allowed) - .recycleOnUse()); - } - - /** - * Always invoked on the provider executor. - */ - protected abstract void onRequestSetAllowed(boolean allowed); - - /** * Dumps debug or log information. May be invoked from any thread. */ public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args); diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 5f44e042b5e9..685fb9eb38f5 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -1229,11 +1229,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } } - @Override - protected void onRequestSetAllowed(boolean allowed) { - // do nothing - the gnss provider is always allowed - } - private void deleteAidingData(Bundle extras) { int flags; diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java index 96ffaa6c0bff..87208a7f36f5 100644 --- a/services/core/java/com/android/server/location/LocationProviderProxy.java +++ b/services/core/java/com/android/server/location/LocationProviderProxy.java @@ -205,14 +205,6 @@ public class LocationProviderProxy extends AbstractLocationProvider { } @Override - public void onRequestSetAllowed(boolean allowed) { - mServiceWatcher.runOnBinder(binder -> { - ILocationProvider service = ILocationProvider.Stub.asInterface(binder); - service.requestSetAllowed(allowed); - }); - } - - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mServiceWatcher.dump(fd, pw, args); } diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java index b45b66017062..5ec06ca25581 100644 --- a/services/core/java/com/android/server/location/MockProvider.java +++ b/services/core/java/com/android/server/location/MockProvider.java @@ -64,11 +64,6 @@ public class MockProvider extends AbstractLocationProvider { protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {} @Override - protected void onRequestSetAllowed(boolean allowed) { - setAllowed(allowed); - } - - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("last mock location=" + mLocation); } diff --git a/services/core/java/com/android/server/location/MockableLocationProvider.java b/services/core/java/com/android/server/location/MockableLocationProvider.java index f43669e488d1..0f358e97bc98 100644 --- a/services/core/java/com/android/server/location/MockableLocationProvider.java +++ b/services/core/java/com/android/server/location/MockableLocationProvider.java @@ -224,15 +224,6 @@ public class MockableLocationProvider extends AbstractLocationProvider { } } - @Override - protected void onRequestSetAllowed(boolean allowed) { - synchronized (mOwnerLock) { - if (mProvider != null) { - mProvider.onRequestSetAllowed(allowed); - } - } - } - /** * Dumps the current provider implementation. */ diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java index 54dffff8b1df..1ba38cc4d999 100644 --- a/services/core/java/com/android/server/location/PassiveProvider.java +++ b/services/core/java/com/android/server/location/PassiveProvider.java @@ -79,10 +79,5 @@ public class PassiveProvider extends AbstractLocationProvider { protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {} @Override - protected void onRequestSetAllowed(boolean allowed) { - // do nothing - the passive provider is always allowed - } - - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {} } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a464ca717690..92507e5a0e25 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -104,6 +104,9 @@ import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; import static com.android.internal.util.ArrayUtils.emptyIfNull; import static com.android.internal.util.ArrayUtils.filter; +import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME; +import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME; +import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME; import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet; @@ -3000,8 +3003,11 @@ public class PackageManagerService extends IPackageManager.Stub + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount) + " , cached: " + cachedSystemApps); if (mIsUpgrade && systemPackagesCount > 0) { - MetricsLogger.histogram(null, "ota_package_manager_system_app_avg_scan_time", - ((int) systemScanTime) / systemPackagesCount); + //CHECKSTYLE:OFF IndentationCheck + FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, + BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME, + systemScanTime / systemPackagesCount); + //CHECKSTYLE:ON IndentationCheck } if (!mOnlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, @@ -3128,8 +3134,12 @@ public class PackageManagerService extends IPackageManager.Stub + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount) + " , cached: " + cachedNonSystemApps); if (mIsUpgrade && dataPackagesCount > 0) { - MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time", - ((int) dataScanTime) / dataPackagesCount); + //CHECKSTYLE:OFF IndentationCheck + FrameworkStatsLog.write( + FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, + BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME, + dataScanTime / dataPackagesCount); + //CHECKSTYLE:OFF IndentationCheck } } mExpectingBetter.clear(); @@ -3390,8 +3400,10 @@ public class PackageManagerService extends IPackageManager.Stub } mDexManager.load(userPackages); if (mIsUpgrade) { - MetricsLogger.histogram(null, "ota_package_manager_init_time", - (int) (SystemClock.uptimeMillis() - startTime)); + FrameworkStatsLog.write( + FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, + BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME, + SystemClock.uptimeMillis() - startTime); } } // synchronized (mLock) } // synchronized (mInstallLock) diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 6c2ace8b4163..42b2eebd63a7 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -5407,7 +5407,7 @@ public final class Settings { packagePermissions, sharedUserPermissions); } - mPersistence.write(runtimePermissions, UserHandle.of(userId)); + mPersistence.writeAsUser(runtimePermissions, UserHandle.of(userId)); } @NonNull @@ -5461,12 +5461,13 @@ public final class Settings { } public void deleteUserRuntimePermissionsFile(int userId) { - mPersistence.delete(UserHandle.of(userId)); + mPersistence.deleteAsUser(UserHandle.of(userId)); } @GuardedBy("Settings.this.mLock") public void readStateForUserSyncLPr(int userId) { - RuntimePermissionsState runtimePermissions = mPersistence.read(UserHandle.of(userId)); + RuntimePermissionsState runtimePermissions = mPersistence.readAsUser(UserHandle.of( + userId)); if (runtimePermissions == null) { readLegacyStateForUserSyncLPr(userId); writePermissionsForUserAsyncLPr(userId); diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index c37ceb3bc1b0..2de9858d91f9 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -310,6 +310,60 @@ class ShortcutPackage extends ShortcutPackageItem { } /** + * Push a shortcut. If the max number of dynamic shortcuts is already reached, remove the + * shortcut with the lowest rank before adding the new shortcut. + */ + public boolean pushDynamicShortcut(@NonNull ShortcutInfo newShortcut) { + Preconditions.checkArgument(newShortcut.isEnabled(), + "pushDynamicShortcuts() cannot publish disabled shortcuts"); + + newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); + + final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId()); + boolean wasPinned = false; + + if (oldShortcut == null) { + final ShortcutService service = mShortcutUser.mService; + final int maxShortcuts = service.getMaxActivityShortcuts(); + + final ArrayMap<ComponentName, ArrayList<ShortcutInfo>> all = + sortShortcutsToActivities(); + final ArrayList<ShortcutInfo> activityShortcuts = all.get(newShortcut.getActivity()); + + if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) { + // Max has reached. Delete the shortcut with lowest rank. + + // Sort by isManifestShortcut() and getRank(). + Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator); + + final ShortcutInfo shortcut = activityShortcuts.get(maxShortcuts - 1); + if (shortcut.isManifestShortcut()) { + // All shortcuts are manifest shortcuts and cannot be removed. + Slog.e(TAG, "Failed to remove manifest shortcut while pushing dynamic shortcut " + + newShortcut.getId()); + return false; + } + + deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true); + } + } else { + // It's an update case. + // Make sure the target is updatable. (i.e. should be mutable.) + oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false); + + wasPinned = oldShortcut.isPinned(); + } + + // If it was originally pinned, the new one should be pinned too. + if (wasPinned) { + newShortcut.addFlags(ShortcutInfo.FLAG_PINNED); + } + + forceReplaceShortcutInner(newShortcut); + return true; + } + + /** * Remove all shortcuts that aren't pinned, cached nor dynamic. */ private void removeOrphans() { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 12f7d5c27459..54f9f761241f 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -133,6 +133,7 @@ import java.lang.annotation.RetentionPolicy; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -1946,6 +1947,50 @@ public class ShortcutService extends IShortcutService.Stub { } @Override + public void pushDynamicShortcut(String packageName, ShortcutInfo shortcut, + @UserIdInt int userId) { + verifyCaller(packageName, userId); + verifyShortcutInfoPackage(packageName, shortcut); + + final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( + injectBinderCallingPid(), injectBinderCallingUid()); + + synchronized (mLock) { + throwIfUserLockedL(userId); + + final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); + + ps.ensureNotImmutable(shortcut.getId(), /*ignoreInvisible=*/ true); + fillInDefaultActivity(Arrays.asList(shortcut)); + + if (!shortcut.hasRank()) { + shortcut.setRank(0); + } + // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). + ps.clearAllImplicitRanks(); + shortcut.setImplicitRank(0); + + // Validate the shortcut. + fixUpIncomingShortcutInfo(shortcut, /* forUpdate= */ false); + + // When ranks are changing, we need to insert between ranks, so set the + // "rank changed" flag. + shortcut.setRankChanged(); + + // Push it. + if (!ps.pushDynamicShortcut(shortcut)) { + return; + } + + // Lastly, adjust the ranks. + ps.adjustRanks(); + } + packageShortcutsChanged(packageName, userId); + + verifyStates(); + } + + @Override public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut, IntentSender resultIntent, int userId) { Objects.requireNonNull(shortcut); diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java index 9f4ca3c6c4ea..97ce6bd7f369 100644 --- a/services/core/java/com/android/server/role/RoleUserState.java +++ b/services/core/java/com/android/server/role/RoleUserState.java @@ -364,12 +364,12 @@ public class RoleUserState { (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked()); } - mPersistence.write(roles, UserHandle.of(mUserId)); + mPersistence.writeAsUser(roles, UserHandle.of(mUserId)); } private void readFile() { synchronized (mLock) { - RolesState roles = mPersistence.read(UserHandle.of(mUserId)); + RolesState roles = mPersistence.readAsUser(UserHandle.of(mUserId)); if (roles == null) { readLegacyFileLocked(); scheduleWriteFileLocked(); @@ -545,7 +545,7 @@ public class RoleUserState { throw new IllegalStateException("This RoleUserState has already been destroyed"); } mWriteHandler.removeCallbacksAndMessages(null); - mPersistence.delete(UserHandle.of(mUserId)); + mPersistence.deleteAsUser(UserHandle.of(mUserId)); mDestroyed = true; } } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 47a26f576949..e7345280603c 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -41,6 +41,7 @@ import android.app.AppOpsManager.HistoricalPackageOps; import android.app.AppOpsManager.HistoricalUidOps; import android.app.INotificationManager; import android.app.ProcessMemoryState; +import android.app.RuntimeAppOpAccessMessage; import android.app.StatsManager; import android.app.StatsManager.PullAtomMetadata; import android.bluetooth.BluetoothActivityEnergyInfo; @@ -377,6 +378,8 @@ public class StatsPullAtomService extends SystemService { return pullFaceSettings(atomTag, data); case FrameworkStatsLog.APP_OPS: return pullAppOps(atomTag, data); + case FrameworkStatsLog.RUNTIME_APP_OP_ACCESS: + return pullRuntimeAppOpAccessMessage(atomTag, data); case FrameworkStatsLog.NOTIFICATION_REMOTE_VIEWS: return pullNotificationRemoteViews(atomTag, data); case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED: @@ -539,6 +542,7 @@ public class StatsPullAtomService extends SystemService { registerAppsOnExternalStorageInfo(); registerFaceSettings(); registerAppOps(); + registerRuntimeAppOpAccessMessage(); registerNotificationRemoteViews(); registerDangerousPermissionState(); registerDangerousPermissionStateSampled(); @@ -2834,6 +2838,17 @@ public class StatsPullAtomService extends SystemService { } + private void registerRuntimeAppOpAccessMessage() { + int tagId = FrameworkStatsLog.RUNTIME_APP_OP_ACCESS; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + BackgroundThread.getExecutor(), + mStatsCallbackImpl + ); + + } + int pullAppOps(int atomTag, List<StatsEvent> pulledData) { final long token = Binder.clearCallingIdentity(); try { @@ -2894,6 +2909,41 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + int pullRuntimeAppOpAccessMessage(int atomTag, List<StatsEvent> pulledData) { + final long token = Binder.clearCallingIdentity(); + try { + AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); + + RuntimeAppOpAccessMessage message = appOps.collectRuntimeAppOpAccessMessage(); + if (message == null) { + Slog.i(TAG, "No runtime appop access message collected"); + return StatsManager.PULL_SUCCESS; + } + + StatsEvent.Builder e = StatsEvent.newBuilder(); + e.setAtomId(atomTag); + e.writeInt(message.getUid()); + e.writeString(message.getPackageName()); + e.writeString(message.getOp()); + if (message.getFeatureId() == null) { + e.writeString(""); + } else { + e.writeString(message.getFeatureId()); + } + e.writeString(message.getMessage()); + e.writeInt(message.getSamplingStrategy()); + + pulledData.add(e.build()); + } catch (Throwable t) { + // TODO: catch exceptions at a more granular level + Slog.e(TAG, "Could not read runtime appop access message", t); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } + static void unpackStreamedData(int atomTag, List<StatsEvent> pulledData, List<ParcelFileDescriptor> statsFiles) throws IOException { InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0)); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 4cc4851164ee..04fae976e2f4 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1163,8 +1163,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // An activity is considered to be in multi-window mode if its task isn't fullscreen. final boolean inMultiWindowMode = inMultiWindowMode(); if (inMultiWindowMode != mLastReportedMultiWindowMode) { - mLastReportedMultiWindowMode = inMultiWindowMode; - scheduleMultiWindowModeChanged(getConfiguration()); + if (!inMultiWindowMode && mLastReportedPictureInPictureMode) { + updatePictureInPictureMode(null, false); + } else { + mLastReportedMultiWindowMode = inMultiWindowMode; + scheduleMultiWindowModeChanged(getConfiguration()); + } } } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 688f47475d8b..2f1cc0532ccc 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -658,8 +658,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } @Override - public void resolveOverrideConfiguration(Configuration newParentConfig) { - super.resolveOverrideConfiguration(newParentConfig); + public void resolveTileOverrideConfiguration(Configuration newParentConfig) { + super.resolveTileOverrideConfiguration(newParentConfig); if (mTile != null) { // If this is a virtual child of a tile, simulate the parent-child relationship mTile.updateResolvedConfig(getResolvedOverrideConfiguration()); @@ -742,8 +742,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget { setBounds(newBounds); } else if (overrideWindowingMode != WINDOWING_MODE_PINNED) { // For pinned stack, resize is now part of the {@link WindowContainerTransaction} - resize(new Rect(newBounds), null /* tempTaskBounds */, - null /* tempTaskInsetBounds */, PRESERVE_WINDOWS, true /* deferResume */); + resize(new Rect(newBounds), null /* configBounds */, + PRESERVE_WINDOWS, true /* deferResume */); } } if (prevIsAlwaysOnTop != isAlwaysOnTop()) { @@ -952,8 +952,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) { - resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */, - false /* preserveWindows */, true /* deferResume */); + resize(mTmpRect2, null /*configBounds*/, + false /*preserveWindows*/, true /*deferResume*/); } } finally { if (showRecents && !alreadyInSplitScreenMode && isOnHomeDisplay() @@ -1118,20 +1118,15 @@ class ActivityStack extends Task implements BoundsAnimationTarget { return r.getTask().mTaskId != taskId && r.appToken != notTop && r.canBeTopRunning(); } - ActivityRecord isInStackLocked(IBinder token) { - final ActivityRecord r = ActivityRecord.forTokenLocked(token); - return isInStackLocked(r); - } - ActivityRecord isInStackLocked(ActivityRecord r) { if (r == null) { return null; } - final Task task = r.getTask(); - final ActivityStack stack = r.getRootTask(); - if (stack != null && task.mChildren.contains(r) && mChildren.contains(task)) { - if (stack != this) Slog.w(TAG, - "Illegal state! task does not point to stack it is in."); + final Task task = r.getRootTask(); + if (task != null && r.isDescendantOf(task)) { + if (task != this) Slog.w(TAG, "Illegal state! task does not point to stack it is in. " + + "stack=" + this + " task=" + task + " r=" + r + + " callers=" + Debug.getCallers(15, "\n")); return r; } return null; @@ -1207,7 +1202,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } getDisplay().positionStackAtBottom(this, reason); - if (task != null) { + if (task != null && task != this) { positionChildAtBottom(task); } @@ -1251,12 +1246,12 @@ class ActivityStack extends Task implements BoundsAnimationTarget { mCurrentUser = userId; super.switchUser(userId); - forAllTasks((t) -> { - if (t.showToCurrentUser()) { + forAllLeafTasks((t) -> { + if (t.showToCurrentUser() && t != this) { mChildren.remove(t); mChildren.add(t); } - }, true /* traverseTopToBottom */, this); + }, true /* traverseTopToBottom */); } void minimalResumeActivityLocked(ActivityRecord r) { @@ -2450,16 +2445,16 @@ class ActivityStack extends Task implements BoundsAnimationTarget { boolean newTask, boolean keepCurTransition, ActivityOptions options) { Task rTask = r.getTask(); final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront(); - final boolean hasTask = hasChild(rTask); + final boolean isOrhasTask = rTask == this || hasChild(rTask); // mLaunchTaskBehind tasks get placed at the back of the task stack. - if (!r.mLaunchTaskBehind && allowMoveToFront && (!hasTask || newTask)) { + if (!r.mLaunchTaskBehind && allowMoveToFront && (!isOrhasTask || newTask)) { // Last activity in task had been removed or ActivityManagerService is reusing task. // Insert or replace. // Might not even be in. positionChildAtTop(rTask); } Task task = null; - if (!newTask && hasTask) { + if (!newTask && isOrhasTask) { final ActivityRecord occludingActivity = getActivity( (ar) -> !ar.finishing && ar.occludesParent(), true, rTask); if (occludingActivity != null) { @@ -2717,7 +2712,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { void finishVoiceTask(IVoiceInteractionSession session) { final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::finishIfVoiceTask, PooledLambda.__(Task.class), session.asBinder()); - forAllTasks(c, true /* traverseTopToBottom */, this); + forAllLeafTasks(c, true /* traverseTopToBottom */); c.recycle(); } @@ -2818,8 +2813,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { return false; } final Task task = srec.getTask(); - - if (!mChildren.contains(task) || !task.hasChild(srec)) { + if (!srec.isDescendantOf(this)) { return false; } @@ -2932,20 +2926,20 @@ class ActivityStack extends Task implements BoundsAnimationTarget { getDisplay().mDisplayContent.prepareAppTransition(transit, false); } - final void moveTaskToFrontLocked(Task tr, boolean noAnimation, ActivityOptions options, + final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options, AppTimeTracker timeTracker, String reason) { - moveTaskToFrontLocked(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason); + moveTaskToFront(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason); } - final void moveTaskToFrontLocked(Task tr, boolean noAnimation, ActivityOptions options, + final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options, AppTimeTracker timeTracker, boolean deferResume, String reason) { if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr); final ActivityStack topStack = getDisplay().getTopStack(); - final ActivityRecord topActivity = topStack != null ? topStack.getTopNonFinishingActivity() : null; - final int numTasks = getChildCount(); - final int index = mChildren.indexOf(tr); - if (numTasks == 0 || index < 0) { + final ActivityRecord topActivity = topStack != null + ? topStack.getTopNonFinishingActivity() : null; + + if (tr != this && !tr.isDescendantOf(this)) { // nothing to do! if (noAnimation) { ActivityOptions.abort(options); @@ -3099,24 +3093,30 @@ class ActivityStack extends Task implements BoundsAnimationTarget { // TODO: Can only be called from special methods in ActivityStackSupervisor. // Need to consolidate those calls points into this resize method so anyone can call directly. - void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds, - boolean preserveWindows, boolean deferResume) { - if (!updateBoundsAllowed(bounds)) { + void resize(Rect displayedBounds, Rect configBounds, boolean preserveWindows, + boolean deferResume) { + if (!updateBoundsAllowed(displayedBounds)) { return; } Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + getRootTaskId()); mAtmService.deferWindowLayout(); try { + // TODO: Why not just set this on the stack directly vs. on each tasks? // Update override configurations of all tasks in the stack. - final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds; final PooledConsumer c = PooledLambda.obtainConsumer( ActivityStack::processTaskResizeBounds, PooledLambda.__(Task.class), - taskBounds, tempTaskInsetBounds); - forAllTasks(c, true /* traverseTopToBottom */, this); + displayedBounds, configBounds); + forAllTasks(c, true /* traverseTopToBottom */); c.recycle(); - setBounds(bounds); + if (mBoundsAnimating) { + // Force to update task surface bounds and relayout windows, since configBounds + // remains unchanged during bounds animation. + updateSurfaceBounds(); + getDisplay().setLayoutNeeded(); + mWmService.requestTraversal(); + } if (!deferResume) { ensureVisibleActivitiesConfiguration(topRunningActivity(), preserveWindows); @@ -3127,15 +3127,16 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } } - private static void processTaskResizeBounds(Task task, Rect bounds, Rect insetBounds) { + private static void processTaskResizeBounds( + Task task, Rect displayedBounds, Rect configBounds) { if (!task.isResizeable()) return; - if (insetBounds != null && !insetBounds.isEmpty()) { - task.setOverrideDisplayedBounds(bounds); - task.setBounds(insetBounds); + if (configBounds != null && !configBounds.isEmpty()) { + task.setOverrideDisplayedBounds(displayedBounds); + task.setBounds(configBounds); } else { task.setOverrideDisplayedBounds(null); - task.setBounds(bounds); + task.setBounds(displayedBounds); } } @@ -3150,7 +3151,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskBounds, PooledLambda.__(Task.class), bounds); - forAllTasks(c, true /* traverseTopToBottom */, this); + forAllLeafTasks(c, true /* traverseTopToBottom */); c.recycle(); } @@ -3166,7 +3167,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskDisplayedBounds, PooledLambda.__(Task.class), bounds); - forAllTasks(c, true /* traverseTopToBottom */, this); + forAllLeafTasks(c, true /* traverseTopToBottom */); c.recycle(); } @@ -3262,7 +3263,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { return false; } final String prefix = " "; - forAllTasks((task) -> { + forAllLeafTasks((task) -> { if (needSep) { pw.println(""); } @@ -3280,7 +3281,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { false /* traverseTopToBottom */); dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient, dumpPackage, false, null, task); - }, true /* traverseTopToBottom */, this); + }, true /* traverseTopToBottom */); return true; } @@ -3331,19 +3332,33 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } } - Task createTask(int taskId, ActivityInfo info, Intent intent, boolean toTop) { - return createTask(taskId, info, intent, null /*voiceSession*/, null /*voiceInteractor*/, + Task reuseOrCreateTask(ActivityInfo info, Intent intent, boolean toTop) { + return reuseOrCreateTask(info, intent, null /*voiceSession*/, null /*voiceInteractor*/, toTop, null /*activity*/, null /*source*/, null /*options*/); } + // TODO: Can be removed once we change callpoints creating stacks to be creating tasks. + /** Either returns this current task to be re-used or creates a new child task. */ + Task reuseOrCreateTask(ActivityInfo info, Intent intent, IVoiceInteractionSession voiceSession, + IVoiceInteractor voiceInteractor, boolean toTop, ActivityRecord activity, + ActivityRecord source, ActivityOptions options) { + + Task task; + if (DisplayContent.alwaysCreateStack(getWindowingMode(), getActivityType())) { + // This stack will only contain one task, so just return itself since all stacks ara now + // tasks and all tasks are now stacks. + task = reuseAsLeafTask(voiceSession, voiceInteractor, info, activity); + } else { + // Create child task since this stack can contain multiple tasks. + final int taskId = activity != null + ? mStackSupervisor.getNextTaskIdForUser(activity.mUserId) + : mStackSupervisor.getNextTaskIdForUser(); + task = Task.create( + mAtmService, taskId, info, intent, voiceSession, voiceInteractor, this); + + // add the task to stack first, mTaskPositioner might need the stack association + addChild(task, toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0); + } - Task createTask(int taskId, ActivityInfo info, Intent intent, - IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, - boolean toTop, ActivityRecord activity, ActivityRecord source, - ActivityOptions options) { - final Task task = Task.create( - mAtmService, taskId, info, intent, voiceSession, voiceInteractor, this); - // add the task to stack first, mTaskPositioner might need the stack association - addChild(task, toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0); int displayId = getDisplayId(); if (displayId == INVALID_DISPLAY) displayId = DEFAULT_DISPLAY; final boolean isLockscreenShown = mAtmService.mStackSupervisor.getKeyguardController() @@ -3353,6 +3368,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { && !matchParentBounds() && task.isResizeable() && !isLockscreenShown) { task.setBounds(getRequestedOverrideBounds()); } + return task; } @@ -3559,10 +3575,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { "Can't exit pinned mode if it's not pinned already."); } - if (mChildren.size() != 1) { - throw new RuntimeException("There should be only one task in a pinned stack."); - } - // give pinned stack a chance to save current bounds, this should happen before reparent. final ActivityRecord top = topRunningNonOverlayTaskActivity(); if (top != null && top.isVisible()) { @@ -3592,12 +3604,12 @@ class ActivityStack extends Task implements BoundsAnimationTarget { final PooledConsumer c = PooledLambda.obtainConsumer( ActivityStackSupervisor::updatePictureInPictureMode, mStackSupervisor, PooledLambda.__(Task.class), targetStackBounds, forceUpdate); - forAllTasks(c, true /* traverseTopToBottom */, this); + forAllLeafTasks(c, true /* traverseTopToBottom */); c.recycle(); } void prepareFreezingTaskBounds() { - forAllTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */, this); + forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */); } /** @@ -3629,7 +3641,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { final PooledConsumer c = PooledLambda.obtainConsumer(Task::alignToAdjustedBounds, PooledLambda.__(Task.class), adjusted ? mAdjustedBounds : getRawBounds(), insetBounds, alignBottom); - forAllTasks(c, true /* traverseTopToBottom */, this); + forAllLeafTasks(c, true /* traverseTopToBottom */); c.recycle(); } @@ -3902,6 +3914,12 @@ class ActivityStack extends Task implements BoundsAnimationTarget { return; } + if (child == this) { + // TODO: Fix call-points + moveToFront("positionChildAtTop"); + return; + } + positionChildAt(POSITION_TOP, child, true /* includingParents */); child.updateTaskMovement(true); @@ -4316,19 +4334,19 @@ class ActivityStack extends Task implements BoundsAnimationTarget { * to the list of to be drawn windows the service is waiting for. */ void beginImeAdjustAnimation() { - forAllTasks((t) -> { + forAllLeafTasks((t) -> { if (t.hasContentToDisplay()) { t.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER); t.setWaitingForDrawnIfResizingChanged(); } - }, true /* traverseTopToBottom */, this); + }, true /* traverseTopToBottom */); } /** Resets the resizing state of all windows. */ void endImeAdjustAnimation() { - forAllTasks((t) -> { + forAllLeafTasks((t) -> { t.setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER); - }, true /* traverseTopToBottom */, this); + }, true /* traverseTopToBottom */); } private int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) { @@ -4572,19 +4590,15 @@ class ActivityStack extends Task implements BoundsAnimationTarget { return task != null; } - public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) { + public boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds) { // Hold the lock since this is called from the BoundsAnimator running on the UiThread synchronized (mWmService.mGlobalLock) { if (mCancelCurrentBoundsAnimation) { return false; } + mStackSupervisor.resizePinnedStack(displayedBounds, configBounds); } - try { - mWmService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds); - } catch (RemoteException e) { - // I don't believe you. - } return true; } @@ -4730,7 +4744,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { /** Called immediately prior to resizing the tasks at the end of the pinned stack animation. */ void onPipAnimationEndResize() { mBoundsAnimating = false; - forAllTasks(Task::clearPreserveNonFloatingState, false /* traverseTopToBottom */, this); + forAllLeafTasks(Task::clearPreserveNonFloatingState, false /* traverseTopToBottom */); mWmService.requestTraversal(); } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 70cd01b44f05..97b638820a8a 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -419,14 +419,29 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mTopTask = fromStack.getTopMostTask(); final PooledConsumer c = PooledLambda.obtainConsumer( - MoveTaskToFullscreenHelper::processTask, this, PooledLambda.__(Task.class)); - fromStack.forAllTasks(c, false /* traverseTopToBottom */, fromStack); + MoveTaskToFullscreenHelper::processLeafTask, this, PooledLambda.__(Task.class)); + fromStack.forAllLeafTasks(c, false /* traverseTopToBottom */); c.recycle(); mToDisplay = null; mTopTask = null; } - private void processTask(Task task) { + private void processLeafTask(Task task) { + // This is a one level task that we don't need to create stack for reparenting to. + if (task.isRootTask() && DisplayContent.alwaysCreateStack(WINDOWING_MODE_FULLSCREEN, + task.getActivityType())) { + final ActivityStack stack = (ActivityStack) task; + stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + if (mToDisplay.getDisplayId() != stack.getDisplayId()) { + mToDisplay.moveStackToDisplay(stack, mOnTop); + } else if (mOnTop) { + mToDisplay.positionStackAtTop(stack, false /* includingParents */); + } else { + mToDisplay.positionStackAtBottom(stack); + } + return; + } + final ActivityStack toStack = mToDisplay.getOrCreateStack( null, mTmpOptions, task, task.getActivityType(), mOnTop); @@ -1428,8 +1443,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // still need moveTaskToFrontLocked() below for any transition settings. } if (stack.shouldResizeStackWithLaunchBounds()) { - stack.resize(bounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */, - !PRESERVE_WINDOWS, !DEFER_RESUME); + stack.resize(bounds, null /* configBounds */, !PRESERVE_WINDOWS, !DEFER_RESUME); } else { // WM resizeTask must be done after the task is moved to the correct stack, // because Task's setBounds() also updates dim layer's bounds, but that has @@ -1443,7 +1457,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } final ActivityRecord r = task.getTopNonFinishingActivity(); - currentStack.moveTaskToFrontLocked(task, false /* noAnimation */, options, + currentStack.moveTaskToFront(task, false /* noAnimation */, options, r == null ? null : r.appTimeTracker, reason); if (DEBUG_STACK) Slog.d(TAG_STACK, @@ -1589,7 +1603,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { false /* deferResume */); } - void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds, + void resizeDockedStackLocked(Rect displayedBounds, Rect tempDockedTaskBounds, Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows, boolean deferResume) { @@ -1607,7 +1621,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { if (mDockedStackResizing) { mHasPendingDockedBounds = true; - mPendingDockedBounds = copyOrNull(dockedBounds); + mPendingDockedBounds = copyOrNull(displayedBounds); mPendingTempDockedTaskBounds = copyOrNull(tempDockedTaskBounds); mPendingTempDockedTaskInsetBounds = copyOrNull(tempDockedTaskInsetBounds); mPendingTempOtherTaskBounds = copyOrNull(tempOtherTaskBounds); @@ -1620,13 +1634,13 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Don't allow re-entry while resizing. E.g. due to docked stack detaching. mAllowDockedStackResize = false; ActivityRecord r = stack.topRunningActivity(); - stack.resize(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds, + stack.resize(displayedBounds, tempDockedTaskBounds, !PRESERVE_WINDOWS, DEFER_RESUME); // TODO: Checking for isAttached might not be needed as if the user passes in null // dockedBounds then they want the docked stack to be dismissed. if (stack.getWindowingMode() == WINDOWING_MODE_FULLSCREEN - || (dockedBounds == null && !stack.isAttached())) { + || (displayedBounds == null && !stack.isAttached())) { // The dock stack either was dismissed or went fullscreen, which is kinda the same. // In this case we make all other static stacks fullscreen and move all // docked stack tasks to the fullscreen stack. @@ -1654,7 +1668,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // interaction. continue; } - current.getStackDockedModeBounds(dockedBounds, + current.getStackDockedModeBounds(displayedBounds, tempOtherTaskBounds /* currentTempTaskBounds */, tempRect /* outStackBounds */, otherTaskRect /* outTempTaskBounds */); @@ -1669,9 +1683,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { + " non-fullscreen stack"); } - current.resize(tempRect, - !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds, - tempOtherTaskInsetBounds, preserveWindows, deferResume); + current.resize(tempRect, tempOtherTaskBounds, preserveWindows, deferResume); } } if (!deferResume) { @@ -1684,7 +1696,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } } - void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) { + void resizePinnedStack(Rect displayedBounds, Rect inConfigBounds) { // TODO(multi-display): The display containing the stack should be passed in. final ActivityStack stack = mRootWindowContainer.getDefaultDisplay().getRootPinnedTask(); @@ -1696,23 +1708,22 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack"); mService.deferWindowLayout(); try { - Rect insetBounds = null; - if (tempPinnedTaskBounds != null && stack.isAnimatingBoundsToFullscreen()) { + Rect configBounds = null; + if (inConfigBounds != null) { // Use 0,0 as the position for the inset rect because we are headed for fullscreen. - insetBounds = tempRect; - insetBounds.top = 0; - insetBounds.left = 0; - insetBounds.right = tempPinnedTaskBounds.width(); - insetBounds.bottom = tempPinnedTaskBounds.height(); + configBounds = tempRect; + configBounds.top = 0; + configBounds.left = 0; + configBounds.right = inConfigBounds.width(); + configBounds.bottom = inConfigBounds.height(); } - if (pinnedBounds != null && tempPinnedTaskBounds == null) { + if (displayedBounds != null && inConfigBounds == null) { // We have finished the animation into PiP, and are resizing the tasks to match the // stack bounds, while layouts are deferred, update any task state as a part of // transitioning it from fullscreen into a floating state. stack.onPipAnimationEndResize(); } - stack.resize(pinnedBounds, tempPinnedTaskBounds, insetBounds, !PRESERVE_WINDOWS, - !DEFER_RESUME); + stack.resize(displayedBounds, configBounds, !PRESERVE_WINDOWS, !DEFER_RESUME); } finally { mService.continueWindowLayout(); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -1742,7 +1753,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } else { final PooledConsumer c = PooledLambda.obtainConsumer( ActivityStackSupervisor::processRemoveTask, this, PooledLambda.__(Task.class)); - stack.forAllTasks(c, true /* traverseTopToBottom */, stack); + stack.forAllLeafTasks(c, true /* traverseTopToBottom */); c.recycle(); } } @@ -2755,6 +2766,10 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { deferUpdateRecentsHomeStackBounds(); // TODO(multi-display): currently recents animation only support default display. mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false); + // TODO(task-hierarchy): Remove when tiles are in hierarchy. + // Unset launching windowing mode to prevent creating split-screen-primary stack + // in RWC#anyTaskForId() below. + activityOptions.setLaunchWindowingMode(WINDOWING_MODE_UNDEFINED); } task = mRootWindowContainer.anyTaskForId(taskId, @@ -2764,6 +2779,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mWindowManager.executeAppTransition(); throw new IllegalArgumentException( "startActivityFromRecents: Task " + taskId + " not found."); + } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + && task.getWindowingMode() != windowingMode) { + mService.moveTaskToSplitScreenPrimaryTile(task, true /* toTop */); } if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 2a2ab4b79166..600a125cdf43 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2366,7 +2366,7 @@ class ActivityStarter { // task on top there. // Defer resuming the top activity while moving task to top, since the // current task-top activity may not be the activity that should be resumed. - mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions, + mTargetStack.moveTaskToFront(intentTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, DEFER_RESUME, "bringingFoundTaskToFront"); mMovedToFront = !isSplitScreenTopStack; @@ -2396,8 +2396,7 @@ class ActivityStarter { private void setNewTask(Task taskToAffiliate) { final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront; - final Task task = mTargetStack.createTask( - mSupervisor.getNextTaskIdForUser(mStartActivity.mUserId), + final Task task = mTargetStack.reuseOrCreateTask( mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info, mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession, mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 882d5c70de25..344d4a58c749 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2760,9 +2760,17 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move" + " non-standard task " + taskId + " to split-screen windowing mode"); } + if (!task.supportsSplitScreenWindowingMode()) { + return false; + } final int prevMode = task.getWindowingMode(); - final ActivityStack stack = task.getStack(); + moveTaskToSplitScreenPrimaryTile(task, toTop); + return prevMode != task.getWindowingMode(); + } + + void moveTaskToSplitScreenPrimaryTile(Task task, boolean toTop) { + ActivityStack stack = task.getStack(); TaskTile tile = null; for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) { tile = stack.getDisplay().getStackAt(i).asTile(); @@ -2776,7 +2784,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { WindowContainerTransaction wct = new WindowContainerTransaction(); wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop); mTaskOrganizerController.applyContainerTransaction(wct, null); - return prevMode != task.getWindowingMode(); } /** @@ -3247,8 +3254,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final ActivityStack stack = r.getRootTask(); - final Task task = stack.createTask( - mStackSupervisor.getNextTaskIdForUser(r.mUserId), ainfo, intent, !ON_TOP); + final Task task = stack.getDisplay().createStack(stack.getWindowingMode(), + stack.getActivityType(), !ON_TOP, ainfo, intent); + if (!mRecentTasks.addToBottom(task)) { // The app has too many tasks already and we can't add any more stack.removeChild(task, "addAppTask"); @@ -4426,12 +4434,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) { + public void resizePinnedStack(Rect displayedBounds, Rect configBounds) { enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()"); final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - mStackSupervisor.resizePinnedStackLocked(pinnedBounds, tempPinnedTaskBounds); + mStackSupervisor.resizePinnedStack(displayedBounds, configBounds); } } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java index 9f54e49e0022..b1d535904fab 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java @@ -52,7 +52,7 @@ interface BoundsAnimationTarget { * animation is now invalid and not required. In such a case, the cancel will trigger the * animation end callback as well, but will not send any further size changes. */ - boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds); + boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds); /** Sets the alpha of the animation target */ boolean setPinnedStackAlpha(float alpha); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e60ab3fdc31c..0029dc8c3c25 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4415,7 +4415,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo ArrayList<Task> getVisibleTasks() { final ArrayList<Task> visibleTasks = new ArrayList<>(); forAllTasks(task -> { - if (!task.isRootTask() && task.isVisible()) { + if (task.isLeafTask() && task.isVisible()) { visibleTasks.add(task); } }); @@ -4537,6 +4537,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo true /* includingParents */); } + child.updateTaskMovement(moveToTop); + setLayoutNeeded(); } @@ -5790,7 +5792,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return null; } - boolean alwaysCreateStack(int windowingMode, int activityType) { + static boolean alwaysCreateStack(int windowingMode, int activityType) { // Always create a stack for fullscreen, freeform, and split-screen-secondary windowing // modes so that we can manage visual ordering and return types correctly. return activityType == ACTIVITY_TYPE_STANDARD diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index e443fe42a82e..f02a9dd61107 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -131,7 +131,6 @@ import android.graphics.Rect; import android.graphics.Region; import android.hardware.input.InputManager; import android.hardware.power.V1_0.PowerHint; -import android.os.Binder; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -304,6 +303,8 @@ public class DisplayPolicy { private boolean mIsFreeformWindowOverlappingWithNavBar; + private boolean mLastImmersiveMode; + private final StatusBarController mStatusBarController; private final BarController mNavigationBarController; @@ -3182,8 +3183,8 @@ public class DisplayPolicy { if (nb) mNavigationBarController.showTransient(); updateSystemUiVisibilityLw(); } - mImmersiveModeConfirmation.confirmCurrentPrompt(); } + mImmersiveModeConfirmation.confirmCurrentPrompt(); } } @@ -3210,7 +3211,7 @@ public class DisplayPolicy { updateSystemUiVisibilityLw(); } - private int updateSystemUiVisibilityLw() { + int updateSystemUiVisibilityLw() { // If there is no window focused, there will be nobody to handle the events // anyway, so just hang on in whatever state we're in until things settle down. WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow @@ -3566,9 +3567,11 @@ public class DisplayPolicy { vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis); // update navigation bar - boolean oldImmersiveMode = isImmersiveMode(oldVis); - boolean newImmersiveMode = isImmersiveMode(vis); + boolean newInsetsMode = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL; + boolean oldImmersiveMode = newInsetsMode ? mLastImmersiveMode : isImmersiveMode(oldVis); + boolean newImmersiveMode = newInsetsMode ? isImmersiveMode(win) : isImmersiveMode(vis); if (oldImmersiveMode != newImmersiveMode) { + mLastImmersiveMode = newImmersiveMode; final String pkg = win.getOwningPackage(); mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode, mService.mPolicy.isUserSetupComplete(), @@ -3673,6 +3676,7 @@ public class DisplayPolicy { } } + // TODO(b/118118435): Remove this after migration private boolean isImmersiveMode(int vis) { final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; return mNavigationBar != null @@ -3681,6 +3685,16 @@ public class DisplayPolicy { && canHideNavigationBar(); } + private boolean isImmersiveMode(WindowState win) { + final int behavior = win.mAttrs.insetsFlags.behavior; + return mNavigationBar != null + && canHideNavigationBar() + && (behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE + || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) + && getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR) + && win != getNotificationShade(); + } + /** * @return whether the navigation bar can be hidden, e.g. the device has a navigation bar */ diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 64c5faa24454..57babb0eeaa0 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -558,16 +558,14 @@ public class DisplayRotation { @VisibleForTesting boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) { - final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow(); - if (w == null) { - return false; - } // Display doesn't need to be frozen because application has been started in correct // rotation already, so the rest of the windows can use seamless rotation. - if (w.mToken.hasFixedRotationTransform()) { + if (mDisplayContent.mFixedRotationLaunchingApp != null) { return true; } - if (w != mDisplayContent.mCurrentFocus) { + + final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow(); + if (w == null || w != mDisplayContent.mCurrentFocus) { return false; } // We only enable seamless rotation if the top window has requested it and is in the diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java index 9d985d7048e5..c92de2b84df6 100644 --- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java +++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java @@ -76,7 +76,7 @@ class EnsureActivitiesVisibleHelper { // to be visible (such as performing Recents animation). final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind && mContiner.isTopActivityFocusable() - && mContiner.isInStackLocked(starting) == null; + && (starting == null || !starting.isDescendantOf(mContiner)); final PooledConsumer f = PooledLambda.obtainConsumer( EnsureActivitiesVisibleHelper::setActivityVisibilityState, this, diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index a8fe34953cee..b3890cd84196 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -182,6 +182,7 @@ class InsetsStateController { } if (changed) { notifyInsetsChanged(); + mDisplayContent.getDisplayPolicy().updateSystemUiVisibilityLw(); } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index b0492be5f542..e92bbaae71d3 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -334,7 +334,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, if (sendUserLeaveHint) { // Setting this allows the previous app to PiP. mStackSupervisor.mUserLeaving = true; - targetStack.moveTaskToFrontLocked(targetActivity.getTask(), + targetStack.moveTaskToFront(targetActivity.getTask(), true /* noAnimation */, null /* activityOptions */, targetActivity.appTimeTracker, "RecentsAnimation.onAnimationFinished()"); diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index e923e6413546..57c877fcdfef 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -376,7 +376,7 @@ public class RecentsAnimationController implements DeathRecipient { final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) -> { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class), visibleTasks); - targetStack.forAllTasks(c, true /* traverseTopToBottom */, targetStack); + targetStack.forAllLeafTasks(c, true /* traverseTopToBottom */); c.recycle(); } diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java index 63346b9a3e5b..45f8a15979f4 100644 --- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java +++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java @@ -67,7 +67,7 @@ class ResetTargetTaskHelper { final PooledConsumer c = PooledLambda.obtainConsumer( ResetTargetTaskHelper::processTask, this, PooledLambda.__(Task.class)); - targetTask.mWmService.mRoot.forAllTasks(c, true /*traverseTopToBottom*/, mTargetStack); + targetTask.mWmService.mRoot.forAllLeafTasks(c, true /*traverseTopToBottom*/); c.recycle(); processPendingReparentActivities(); @@ -245,9 +245,8 @@ class ResetTargetTaskHelper { if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + r + " out to bottom task " + targetTask); } else { - targetTask = mTargetStack.createTask( - atmService.mStackSupervisor.getNextTaskIdForUser(r.mUserId), r.info, - null /* intent */, false /* toTop */); + targetTask = mTargetStack.reuseOrCreateTask( + r.info, null /*intent*/, false /*toTop*/); targetTask.affinityIntent = r.intent; createdTasks.add(targetTask); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 2596452eb46a..aa6bdfd1aa5b 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2137,10 +2137,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> r.getActivityType(), ON_TOP, r.info, r.intent); // There are multiple activities in the task and moving the top activity should // reveal/leave the other activities in their original task. - - Task newTask = stack.createTask(mStackSupervisor.getNextTaskIdForUser(r.mUserId), - r.info, r.intent, true); - r.reparent(newTask, MAX_VALUE, "moveActivityToStack"); + r.reparent(stack, MAX_VALUE, "moveActivityToStack"); } stack.setWindowingMode(WINDOWING_MODE_PINNED); @@ -2407,7 +2404,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final PooledConsumer c = PooledLambda.obtainConsumer( RootWindowContainer::processTaskForStackInfo, PooledLambda.__(Task.class), info, currentIndex); - stack.forAllTasks(c, false /* traverseTopToBottom */, stack); + stack.forAllLeafTasks(c, false /* traverseTopToBottom */); c.recycle(); final ActivityRecord top = stack.topRunningActivity(); @@ -3302,7 +3299,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final PooledConsumer c = PooledLambda.obtainConsumer( RootWindowContainer::taskTopActivityIsUser, this, PooledLambda.__(Task.class), userId); - forAllTasks(c); + forAllLeafTasks(c, true /* traverseTopToBottom */); c.recycle(); } finally { mService.continueWindowLayout(); @@ -3321,14 +3318,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * @return {@code true} if the top activity looks like it belongs to {@param userId}. */ private void taskTopActivityIsUser(Task task, @UserIdInt int userId) { - // TODO(b/80414790): having utilities to loop for all leaf tasks from caller vs. checking - // leaf tasks here. - if (!task.isLeafTask()) { - // No op if not a leaf task since we don't want to report root tasks to - // TaskStackListeners. - return; - } - // To handle the case that work app is in the task but just is not the top one. final ActivityRecord activityRecord = task.getTopNonFinishingActivity(); final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null); @@ -3430,18 +3419,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } ActivityRecord isInAnyStack(IBinder token) { - int numDisplays = getChildCount(); - for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { - final DisplayContent display = getChildAt(displayNdx); - for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getStackAt(stackNdx); - final ActivityRecord r = stack.isInStackLocked(token); - if (r != null) { - return r; - } - } - } - return null; + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + return (r != null && r.isDescendantOf(this)) ? r : null; } @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 6ebbf776feba..9593ea070509 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -76,7 +76,7 @@ class RunningTasks { final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this, PooledLambda.__(Task.class)); - root.forAllTasks(c, false); + root.forAllLeafTasks(c, false); c.recycle(); // Take the first {@param maxNum} tasks and create running task infos for them @@ -93,9 +93,6 @@ class RunningTasks { } private void processTask(Task task) { - if (task.isRootTask()) { - return; - } if (task.getTopNonFinishingActivity() == null) { // Skip if there are no activities in the task return; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c7f2cc7f3692..f93e39282dd6 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -225,8 +225,8 @@ class Task extends WindowContainer<WindowContainer> { String affinity; // The affinity name for this task, or null; may change identity. String rootAffinity; // Initial base affinity, or null; does not change from initial root. - final IVoiceInteractionSession voiceSession; // Voice interaction session driving task - final IVoiceInteractor voiceInteractor; // Associated interactor to provide to app + IVoiceInteractionSession voiceSession; // Voice interaction session driving task + IVoiceInteractor voiceInteractor; // Associated interactor to provide to app Intent intent; // The original intent that started the task. Note that this value can // be null. Intent affinityIntent; // Intent of affinity-moved activity that started this task. @@ -422,6 +422,8 @@ class Task extends WindowContainer<WindowContainer> { /** When set, will force the task to report as invisible. */ boolean mForceHidden = false; + SurfaceControl.Transaction mMainWindowSizeChangeTransaction; + private final FindRootHelper mFindRootHelper = new FindRootHelper(); private class FindRootHelper { private ActivityRecord mRoot; @@ -571,6 +573,15 @@ class Task extends WindowContainer<WindowContainer> { mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity); } + Task reuseAsLeafTask(IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, + ActivityInfo info, ActivityRecord activity) { + voiceSession = _voiceSession; + voiceInteractor = _voiceInteractor; + setIntent(activity); + setMinDimensions(info); + return this; + } + private void cleanUpResourcesForDestroy(ConfigurationContainer oldParent) { if (hasChild()) { return; @@ -1004,7 +1015,7 @@ class Task extends WindowContainer<WindowContainer> { } /** Sets the original minimal width and height. */ - private void setMinDimensions(ActivityInfo info) { + void setMinDimensions(ActivityInfo info) { if (info != null && info.windowLayout != null) { mMinWidth = info.windowLayout.minWidth; mMinHeight = info.windowLayout.minHeight; @@ -2176,16 +2187,20 @@ class Task extends WindowContainer<WindowContainer> { return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize); } + void resolveTileOverrideConfiguration(Configuration newParentConfig) { + super.resolveOverrideConfiguration(newParentConfig); + } + @Override void resolveOverrideConfiguration(Configuration newParentConfig) { - if (isRootTask()) { - super.resolveOverrideConfiguration(newParentConfig); + if (!isLeafTask()) { + resolveTileOverrideConfiguration(newParentConfig); return; } mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds()); - super.resolveOverrideConfiguration(newParentConfig); + resolveTileOverrideConfiguration(newParentConfig); int windowingMode = - getRequestedOverrideConfiguration().windowConfiguration.getWindowingMode(); + getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode(); if (windowingMode == WINDOWING_MODE_UNDEFINED) { windowingMode = newParentConfig.windowConfiguration.getWindowingMode(); } @@ -2392,7 +2407,7 @@ class Task extends WindowContainer<WindowContainer> { final int[] currentCount = {0}; final PooledConsumer c = PooledLambda.obtainConsumer((t, count) -> { count[0]++; }, PooledLambda.__(Task.class), currentCount); - forAllTasks(c, false /* traverseTopToBottom */, this); + forAllLeafTasks(c, false /* traverseTopToBottom */); c.recycle(); return currentCount[0]; } @@ -2614,6 +2629,7 @@ class Task extends WindowContainer<WindowContainer> { */ void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) { if (overrideDisplayedBounds != null) { + adjustForMinimalTaskDimensions(overrideDisplayedBounds, mOverrideDisplayedBounds); mOverrideDisplayedBounds.set(overrideDisplayedBounds); } else { mOverrideDisplayedBounds.setEmpty(); @@ -3074,16 +3090,33 @@ class Task extends WindowContainer<WindowContainer> { } @Override - void forAllTasks(Consumer<Task> callback, boolean traverseTopToBottom, Task excludedTask) { - super.forAllTasks(callback, traverseTopToBottom, excludedTask); - if (excludedTask != this) { - callback.accept(this); + void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) { + final int count = mChildren.size(); + boolean isLeafTask = true; + if (traverseTopToBottom) { + for (int i = count - 1; i >= 0; --i) { + final Task child = mChildren.get(i).asTask(); + if (child != null) { + isLeafTask = false; + child.forAllLeafTasks(callback, traverseTopToBottom); + } + } + } else { + for (int i = 0; i < count; i++) { + final Task child = mChildren.get(i).asTask(); + if (child != null) { + isLeafTask = false; + child.forAllLeafTasks(callback, traverseTopToBottom); + } + } } + if (isLeafTask) callback.accept(this); } @Override void forAllTasks(Consumer<Task> callback, boolean traverseTopToBottom) { - forAllTasks(callback, traverseTopToBottom, null /* excludedTask */); + super.forAllTasks(callback, traverseTopToBottom); + callback.accept(this); } @Override @@ -3265,7 +3298,7 @@ class Task extends WindowContainer<WindowContainer> { pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid); pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid); pw.print(" mUserSetupComplete="); pw.print(mUserSetupComplete); - pw.print(" mCallingPackage="); pw.println(mCallingPackage); + pw.print(" mCallingPackage="); pw.print(mCallingPackage); pw.print(" mCallingFeatureId="); pw.println(mCallingFeatureId); if (affinity != null || rootAffinity != null) { pw.print(prefix); pw.print("affinity="); pw.print(affinity); @@ -3979,4 +4012,17 @@ class Task extends WindowContainer<WindowContainer> { mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged( this, true /* force */); } + + /** + * See {@link WindowContainerTransaction#setBoundsChangeTransaction}. In short this + * transaction will be consumed by the next BASE_APPLICATION window within our hierarchy + * to resize, and it will defer the transaction until that resize frame completes. + */ + void setMainWindowSizeChangeTransaction(SurfaceControl.Transaction t) { + mMainWindowSizeChangeTransaction = t; + } + + SurfaceControl.Transaction getMainWindowSizeChangeTransaction() { + return mMainWindowSizeChangeTransaction; + } } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 4d5621cd5b32..6caa27c7fa6f 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -547,8 +547,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub final ActivityStack stack = (ActivityStack) container; if (stack.inPinnedWindowingMode()) { stack.resize(config.windowConfiguration.getBounds(), - null /* tempTaskBounds */, null /* tempTaskInsetBounds */, - PRESERVE_WINDOWS, true /* deferResume */); + null /* configBounds */, PRESERVE_WINDOWS, true /* deferResume */); } } } @@ -557,6 +556,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub WindowContainerTransaction.Change c) { int effects = sanitizeAndApplyChange(wc, c); + final SurfaceControl.Transaction t = c.getBoundsChangeTransaction(); + if (t != null) { + Task tr = (Task) wc; + tr.setMainWindowSizeChangeTransaction(t); + } + Rect enterPipBounds = c.getEnterPipBounds(); if (enterPipBounds != null) { Task tr = (Task) wc; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index da996dcce50d..7a4d0b0d9169 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -615,7 +615,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< void positionChildAt(int position, E child, boolean includingParents) { if (child.getParent() != this) { - throw new IllegalArgumentException("removeChild: container=" + child.getName() + throw new IllegalArgumentException("positionChildAt: container=" + child.getName() + " is not a child of container=" + getName() + " current parent=" + child.getParent()); } @@ -1459,15 +1459,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } } - void forAllTasks(Consumer<Task> callback, boolean traverseTopToBottom, Task excludedTask) { + void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) { final int count = mChildren.size(); if (traverseTopToBottom) { for (int i = count - 1; i >= 0; --i) { - mChildren.get(i).forAllTasks(callback, traverseTopToBottom, excludedTask); + mChildren.get(i).forAllLeafTasks(callback, traverseTopToBottom); } } else { for (int i = 0; i < count; i++) { - mChildren.get(i).forAllTasks(callback, traverseTopToBottom, excludedTask); + mChildren.get(i).forAllLeafTasks(callback, traverseTopToBottom); } } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 9552df7b5899..a1a9af680e92 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -873,6 +873,14 @@ class WindowStateAnimator { clipRect = mTmpClipRect; } + if (mSurfaceResized && (mAttrType == TYPE_BASE_APPLICATION) && + (task != null) && (task.getMainWindowSizeChangeTransaction() != null)) { + mSurfaceController.deferTransactionUntil(mWin.getDeferTransactionBarrier(), + mWin.getFrameNumber()); + SurfaceControl.mergeToGlobalTransaction(task.getMainWindowSizeChangeTransaction()); + task.setMainWindowSizeChangeTransaction(null); + } + float surfaceWidth = mSurfaceController.getWidth(); float surfaceHeight = mSurfaceController.getHeight(); diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 36ed9a5a383f..336934e5fb03 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -166,7 +166,10 @@ using android::hardware::gnss::V2_0::ElapsedRealtimeFlags; using MeasurementCorrections_V1_0 = android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections; using MeasurementCorrections_V1_1 = android::hardware::gnss::measurement_corrections::V1_1::MeasurementCorrections; -using android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection; +using SingleSatCorrection_V1_0 = + android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection; +using SingleSatCorrection_V1_1 = + android::hardware::gnss::measurement_corrections::V1_1::SingleSatCorrection; using android::hardware::gnss::measurement_corrections::V1_0::ReflectingPlane; using android::hidl::base::V1_0::IBase; @@ -3124,6 +3127,91 @@ static jboolean return JNI_FALSE; } +static SingleSatCorrection_V1_0 getSingleSatCorrection_1_0_withoutConstellation( + JNIEnv* env, jobject singleSatCorrectionObj) { + jint correctionFlags = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags); + jint satId = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId); + jfloat carrierFreqHz = + env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatCarrierFreq); + jfloat probSatIsLos = + env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatIsLosProb); + jfloat eplMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl); + jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEplUnc); + uint16_t corrFlags = static_cast<uint16_t>(correctionFlags); + jobject reflectingPlaneObj; + bool has_ref_plane = (corrFlags & GnssSingleSatCorrectionFlags::HAS_REFLECTING_PLANE) != 0; + if (has_ref_plane) { + reflectingPlaneObj = + env->CallObjectMethod(singleSatCorrectionObj, method_correctionSatRefPlane); + } + + ReflectingPlane reflectingPlane; + if (has_ref_plane) { + jdouble latitudeDegreesRefPlane = + env->CallDoubleMethod(reflectingPlaneObj, method_correctionPlaneLatDeg); + jdouble longitudeDegreesRefPlane = + env->CallDoubleMethod(reflectingPlaneObj, method_correctionPlaneLngDeg); + jdouble altitudeDegreesRefPlane = + env->CallDoubleMethod(reflectingPlaneObj, method_correctionPlaneAltDeg); + jdouble azimuthDegreeRefPlane = + env->CallDoubleMethod(reflectingPlaneObj, method_correctionPlaneAzimDeg); + reflectingPlane = { + .latitudeDegrees = latitudeDegreesRefPlane, + .longitudeDegrees = longitudeDegreesRefPlane, + .altitudeMeters = altitudeDegreesRefPlane, + .azimuthDegrees = azimuthDegreeRefPlane, + }; + } + + SingleSatCorrection_V1_0 singleSatCorrection = { + .singleSatCorrectionFlags = corrFlags, + .svid = static_cast<uint16_t>(satId), + .carrierFrequencyHz = carrierFreqHz, + .probSatIsLos = probSatIsLos, + .excessPathLengthMeters = eplMeters, + .excessPathLengthUncertaintyMeters = eplUncMeters, + .reflectingPlane = reflectingPlane, + }; + + return singleSatCorrection; +} + +static void getSingleSatCorrectionList_1_1(JNIEnv* env, jobject singleSatCorrectionList, + hidl_vec<SingleSatCorrection_V1_1>& list) { + for (uint16_t i = 0; i < list.size(); ++i) { + jobject singleSatCorrectionObj = + env->CallObjectMethod(singleSatCorrectionList, method_correctionListGet, i); + + SingleSatCorrection_V1_0 singleSatCorrection_1_0 = + getSingleSatCorrection_1_0_withoutConstellation(env, singleSatCorrectionObj); + + jint constType = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatConstType); + + SingleSatCorrection_V1_1 singleSatCorrection_1_1 = { + .v1_0 = singleSatCorrection_1_0, + .constellation = static_cast<GnssConstellationType_V2_0>(constType), + }; + + list[i] = singleSatCorrection_1_1; + } +} + +static void getSingleSatCorrectionList_1_0(JNIEnv* env, jobject singleSatCorrectionList, + hidl_vec<SingleSatCorrection_V1_0>& list) { + for (uint16_t i = 0; i < list.size(); ++i) { + jobject singleSatCorrectionObj = + env->CallObjectMethod(singleSatCorrectionList, method_correctionListGet, i); + + SingleSatCorrection_V1_0 singleSatCorrection = + getSingleSatCorrection_1_0_withoutConstellation(env, singleSatCorrectionObj); + + jint constType = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatConstType); + + singleSatCorrection.constellation = static_cast<GnssConstellationType_V1_0>(constType), + + list[i] = singleSatCorrection; + } +} static jboolean android_location_GnssMeasurementCorrectionsProvider_inject_gnss_measurement_corrections( JNIEnv* env, @@ -3146,64 +3234,6 @@ static jboolean ALOGI("Empty correction list injected....Returning with no HAL injection"); return JNI_TRUE; } - hidl_vec<SingleSatCorrection> list(len); - - for (uint16_t i = 0; i < len; ++i) { - jobject singleSatCorrectionObj = env->CallObjectMethod( - singleSatCorrectionList, method_correctionListGet, i); - - jint correctionFlags = - env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags); - jint constType = env->CallIntMethod(singleSatCorrectionObj, - method_correctionSatConstType); - jint satId = - env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId); - jfloat carrierFreqHz = env->CallFloatMethod( - singleSatCorrectionObj, method_correctionSatCarrierFreq); - jfloat probSatIsLos = env->CallFloatMethod(singleSatCorrectionObj, - method_correctionSatIsLosProb); - jfloat eplMeters = - env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl); - jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj, - method_correctionSatEplUnc); - uint16_t corrFlags = static_cast<uint16_t>(correctionFlags); - jobject reflectingPlaneObj; - bool has_ref_plane = (corrFlags & GnssSingleSatCorrectionFlags::HAS_REFLECTING_PLANE) != 0; - if (has_ref_plane) { - reflectingPlaneObj = env->CallObjectMethod( - singleSatCorrectionObj, method_correctionSatRefPlane); - } - - ReflectingPlane reflectingPlane; - if (has_ref_plane) { - jdouble latitudeDegreesRefPlane = env->CallDoubleMethod( - reflectingPlaneObj, method_correctionPlaneLatDeg); - jdouble longitudeDegreesRefPlane = env->CallDoubleMethod( - reflectingPlaneObj, method_correctionPlaneLngDeg); - jdouble altitudeDegreesRefPlane = env->CallDoubleMethod( - reflectingPlaneObj, method_correctionPlaneAltDeg); - jdouble azimuthDegreeRefPlane = env->CallDoubleMethod( - reflectingPlaneObj, method_correctionPlaneAzimDeg); - reflectingPlane = { - .latitudeDegrees = latitudeDegreesRefPlane, - .longitudeDegrees = longitudeDegreesRefPlane, - .altitudeMeters = altitudeDegreesRefPlane, - .azimuthDegrees = azimuthDegreeRefPlane, - }; - } - - SingleSatCorrection singleSatCorrection = { - .singleSatCorrectionFlags = corrFlags, - .constellation = static_cast<GnssConstellationType_V1_0>(constType), - .svid = static_cast<uint16_t>(satId), - .carrierFrequencyHz = carrierFreqHz, - .probSatIsLos = probSatIsLos, - .excessPathLengthMeters = eplMeters, - .excessPathLengthUncertaintyMeters = eplUncMeters, - .reflectingPlane = reflectingPlane, - }; - list[i] = singleSatCorrection; - } jdouble latitudeDegreesCorr = env->CallDoubleMethod( correctionsObj, method_correctionsGetLatitudeDegrees); @@ -3225,7 +3255,6 @@ static jboolean .horizontalPositionUncertaintyMeters = horizontalPositionUncertaintyMeters, .verticalPositionUncertaintyMeters = verticalPositionUncertaintyMeters, .toaGpsNanosecondsOfWeek = static_cast<uint64_t>(toaGpsNanosOfWeek), - .satCorrections = list, }; if (gnssCorrectionsIface_V1_1 != nullptr) { @@ -3237,17 +3266,25 @@ static jboolean jfloat environmentBearingUncertaintyDegreesCorr = env->CallFloatMethod( correctionsObj, method_correctionsGetEnvironmentBearingUncertaintyDegrees); + hidl_vec<SingleSatCorrection_V1_1> list(len); + getSingleSatCorrectionList_1_1(env, singleSatCorrectionList, list); + MeasurementCorrections_V1_1 measurementCorrections_1_1 = { - .v1_0 = measurementCorrections_1_0, - .hasEnvironmentBearing = static_cast<bool>(hasEnvironmentBearingCorr), - .environmentBearingDegrees = environmentBearingDegreesCorr, - .environmentBearingUncertaintyDegrees = environmentBearingUncertaintyDegreesCorr, + .v1_0 = measurementCorrections_1_0, + .hasEnvironmentBearing = static_cast<bool>(hasEnvironmentBearingCorr), + .environmentBearingDegrees = environmentBearingDegreesCorr, + .environmentBearingUncertaintyDegrees = environmentBearingUncertaintyDegreesCorr, + .satCorrections = list, }; auto result = gnssCorrectionsIface_V1_1->setCorrections_1_1(measurementCorrections_1_1); return checkHidlReturn(result, "IMeasurementCorrections 1.1 setCorrections() failed."); } + hidl_vec<SingleSatCorrection_V1_0> list(len); + getSingleSatCorrectionList_1_0(env, singleSatCorrectionList, list); + measurementCorrections_1_0.satCorrections = list; + auto result = gnssCorrectionsIface_V1_0->setCorrections(measurementCorrections_1_0); return checkHidlReturn(result, "IMeasurementCorrections 1.0 setCorrections() failed."); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 421bfa44c788..b192dbd93249 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -186,7 +186,6 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Color; import android.location.LocationManager; -import android.location.LocationManagerInternal; import android.media.AudioManager; import android.media.IAudioService; import android.net.ConnectivityManager; @@ -1073,7 +1072,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String TAG_SUSPEND_PERSONAL_APPS = "suspend-personal-apps"; private static final String TAG_PROFILE_MAXIMUM_TIME_OFF = "profile-max-time-off"; private static final String TAG_PROFILE_OFF_DEADLINE = "profile-off-deadline"; - + private static final String TAG_ALWAYS_ON_VPN_PACKAGE = "vpn-package"; + private static final String TAG_ALWAYS_ON_VPN_LOCKDOWN = "vpn-lockdown"; DeviceAdminInfo info; @@ -1202,6 +1202,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Time by which the profile should be turned on according to System.currentTimeMillis(). long mProfileOffDeadline = 0; + public String mAlwaysOnVpnPackage; + public boolean mAlwaysOnVpnLockdown; + ActiveAdmin(DeviceAdminInfo _info, boolean parent) { info = _info; @@ -1442,6 +1445,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (mProfileMaximumTimeOff != 0) { writeAttributeValueToXml(out, TAG_PROFILE_OFF_DEADLINE, mProfileOffDeadline); } + if (!TextUtils.isEmpty(mAlwaysOnVpnPackage)) { + writeAttributeValueToXml(out, TAG_ALWAYS_ON_VPN_PACKAGE, mAlwaysOnVpnPackage); + } + if (mAlwaysOnVpnLockdown) { + writeAttributeValueToXml(out, TAG_ALWAYS_ON_VPN_LOCKDOWN, mAlwaysOnVpnLockdown); + } } void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException { @@ -1687,6 +1696,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } else if (TAG_PROFILE_OFF_DEADLINE.equals(tag)) { mProfileOffDeadline = Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE)); + } else if (TAG_ALWAYS_ON_VPN_PACKAGE.equals(tag)) { + mAlwaysOnVpnPackage = parser.getAttributeValue(null, ATTR_VALUE); + } else if (TAG_ALWAYS_ON_VPN_LOCKDOWN.equals(tag)) { + mAlwaysOnVpnLockdown = Boolean.parseBoolean( + parser.getAttributeValue(null, ATTR_VALUE)); } else { Slog.w(LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -1919,6 +1933,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { pw.println(mProfileMaximumTimeOff); pw.print("mProfileOffDeadline="); pw.println(mProfileOffDeadline); + pw.print("mAlwaysOnVpnPackage="); + pw.println(mAlwaysOnVpnPackage); + pw.print("mAlwaysOnVpnLockdown="); + pw.println(mAlwaysOnVpnLockdown); } } @@ -2112,10 +2130,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mContext.getSystemService(LocationManager.class); } - LocationManagerInternal getLocationManagerInternal() { - return LocalServices.getService(LocationManagerInternal.class); - } - IWindowManager getIWindowManager() { return IWindowManager.Stub .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)); @@ -6781,10 +6795,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * @throws UnsupportedOperationException if the package does not support being set as always-on. */ @Override - public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage, boolean lockdown, + public boolean setAlwaysOnVpnPackage(ComponentName who, String vpnPackage, boolean lockdown, List<String> lockdownWhitelist) throws SecurityException { - enforceProfileOrDeviceOwner(admin); + enforceProfileOrDeviceOwner(who); final int userId = mInjector.userHandleGetCallingUserId(); mInjector.binderWithCleanCallingIdentity(() -> { @@ -6810,12 +6824,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_ALWAYS_ON_VPN_PACKAGE) - .setAdmin(admin) + .setAdmin(who) .setStrings(vpnPackage) .setBoolean(lockdown) .setInt(lockdownWhitelist != null ? lockdownWhitelist.size() : 0) .write(); }); + synchronized (getLockObject()) { + ActiveAdmin admin = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + if (!TextUtils.equals(vpnPackage, admin.mAlwaysOnVpnPackage) + || lockdown != admin.mAlwaysOnVpnLockdown) { + admin.mAlwaysOnVpnPackage = vpnPackage; + admin.mAlwaysOnVpnLockdown = lockdown; + saveSettingsLocked(userId); + } + } return true; } @@ -6829,6 +6853,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public String getAlwaysOnVpnPackageForUser(int userHandle) { + enforceSystemCaller("getAlwaysOnVpnPackageForUser"); + synchronized (getLockObject()) { + ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle); + return admin != null ? admin.mAlwaysOnVpnPackage : null; + } + } + + @Override public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException { enforceProfileOrDeviceOwner(admin); @@ -6838,6 +6871,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public boolean isAlwaysOnVpnLockdownEnabledForUser(int userHandle) { + enforceSystemCaller("isAlwaysOnVpnLockdownEnabledForUser"); + synchronized (getLockObject()) { + ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle); + return admin != null ? admin.mAlwaysOnVpnLockdown : null; + } + } + + @Override public List<String> getAlwaysOnVpnLockdownWhitelist(ComponentName admin) throws SecurityException { enforceProfileOrDeviceOwner(admin); @@ -8987,6 +9029,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } + /** + * Returns the ActiveAdmin associated wit the PO or DO on the given user. + * @param userHandle + * @return + */ + private @Nullable ActiveAdmin getDeviceOrProfileOwnerAdminLocked(int userHandle) { + ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle); + if (admin == null && getDeviceOwnerUserId() == userHandle) { + admin = getDeviceOwnerAdminLocked(); + } + return admin; + } + @GuardedBy("getLockObject()") ActiveAdmin getProfileOwnerOfOrganizationOwnedDeviceLocked(int userHandle) { return mInjector.binderWithCleanCallingIdentity(() -> { @@ -11643,17 +11698,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public void requestSetLocationProviderAllowed(ComponentName who, String provider, - boolean providerAllowed) { - Objects.requireNonNull(who, "ComponentName is null"); - enforceDeviceOwner(who); - - mInjector.binderWithCleanCallingIdentity( - () -> mInjector.getLocationManagerInternal().requestSetProviderAllowed(provider, - providerAllowed)); - } - - @Override public boolean setTime(ComponentName who, long millis) { Objects.requireNonNull(who, "ComponentName is null in setTime"); enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(who); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 569986c46186..b5d3d18b4350 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -44,7 +44,6 @@ import android.database.sqlite.SQLiteGlobal; import android.graphics.GraphicsStatsService; import android.hardware.display.DisplayManagerInternal; import android.net.ConnectivityModuleConnector; -import android.net.ITetheringConnector; import android.net.NetworkStackClient; import android.os.BaseBundle; import android.os.Binder; @@ -298,6 +297,9 @@ public final class SystemServer { "com.android.server.blob.BlobStoreManagerService"; private static final String APP_SEARCH_MANAGER_SERVICE_CLASS = "com.android.server.appsearch.AppSearchManagerService"; + + private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; + private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file"; @@ -2331,7 +2333,7 @@ public final class SystemServer { try { // TODO: hide implementation details, b/146312721. ConnectivityModuleConnector.getInstance().startModuleService( - ITetheringConnector.class.getName(), + TETHERING_CONNECTOR_CLASS, PERMISSION_MAINLINE_NETWORK_STACK, service -> { ServiceManager.addService(Context.TETHERING_SERVICE, service, false /* allowIsolated */, diff --git a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java index 203e9804bfa3..7672cd0040ec 100644 --- a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java +++ b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java @@ -51,15 +51,18 @@ import java.util.concurrent.TimeoutException; abstract class AbstractProtoDiskReadWriter<T> { private static final String TAG = AbstractProtoDiskReadWriter.class.getSimpleName(); + + // Common disk write delay that will be appropriate for most scenarios. + private static final long DEFAULT_DISK_WRITE_DELAY = 2L * DateUtils.MINUTE_IN_MILLIS; private static final long SHUTDOWN_DISK_WRITE_TIMEOUT = 5L * DateUtils.SECOND_IN_MILLIS; private final File mRootDir; private final ScheduledExecutorService mScheduledExecutorService; - private final long mWriteDelayMs; @GuardedBy("this") private ScheduledFuture<?> mScheduledFuture; + // File name -> data class @GuardedBy("this") private Map<String, T> mScheduledFileDataMap = new ArrayMap<>(); @@ -75,15 +78,15 @@ abstract class AbstractProtoDiskReadWriter<T> { */ abstract ProtoStreamReader<T> protoStreamReader(); - AbstractProtoDiskReadWriter(@NonNull File rootDir, long writeDelayMs, + AbstractProtoDiskReadWriter(@NonNull File rootDir, @NonNull ScheduledExecutorService scheduledExecutorService) { mRootDir = rootDir; - mWriteDelayMs = writeDelayMs; mScheduledExecutorService = scheduledExecutorService; } @WorkerThread - void delete(@NonNull String fileName) { + synchronized void delete(@NonNull String fileName) { + mScheduledFileDataMap.remove(fileName); final File file = getFile(fileName); if (!file.exists()) { return; @@ -174,7 +177,7 @@ abstract class AbstractProtoDiskReadWriter<T> { } mScheduledFuture = mScheduledExecutorService.schedule(this::flushScheduledData, - mWriteDelayMs, TimeUnit.MILLISECONDS); + DEFAULT_DISK_WRITE_DELAY, TimeUnit.MILLISECONDS); } /** @@ -183,7 +186,13 @@ abstract class AbstractProtoDiskReadWriter<T> { */ @MainThread synchronized void saveImmediately(@NonNull String fileName, @NonNull T data) { - if (mScheduledExecutorService.isShutdown()) { + mScheduledFileDataMap.put(fileName, data); + triggerScheduledFlushEarly(); + } + + @MainThread + private synchronized void triggerScheduledFlushEarly() { + if (mScheduledFileDataMap.isEmpty() || mScheduledExecutorService.isShutdown()) { return; } // Cancel existing future. @@ -194,7 +203,6 @@ abstract class AbstractProtoDiskReadWriter<T> { mScheduledFuture.cancel(true); } - mScheduledFileDataMap.put(fileName, data); // Submit flush and blocks until it completes. Blocking will prevent the device from // shutting down before flushing completes. Future<?> future = mScheduledExecutorService.submit(this::flushScheduledData); @@ -212,9 +220,10 @@ abstract class AbstractProtoDiskReadWriter<T> { return; } for (String fileName : mScheduledFileDataMap.keySet()) { - T data = mScheduledFileDataMap.remove(fileName); + T data = mScheduledFileDataMap.get(fileName); writeTo(fileName, data); } + mScheduledFileDataMap.clear(); mScheduledFuture = null; } diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java index 3afb209ae5cd..62e9da83869c 100644 --- a/services/people/java/com/android/server/people/data/ConversationStore.java +++ b/services/people/java/com/android/server/people/data/ConversationStore.java @@ -23,7 +23,6 @@ import android.annotation.WorkerThread; import android.content.LocusId; import android.net.Uri; import android.text.TextUtils; -import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.Slog; import android.util.proto.ProtoInputStream; @@ -50,8 +49,6 @@ class ConversationStore { private static final String CONVERSATIONS_FILE_NAME = "conversations"; - private static final long DISK_WRITE_DELAY = 2L * DateUtils.MINUTE_IN_MILLIS; - // Shortcut ID -> Conversation Info @GuardedBy("this") private final Map<String, ConversationInfo> mConversationInfoMap = new ArrayMap<>(); @@ -92,7 +89,7 @@ class ConversationStore { */ @MainThread void loadConversationsFromDisk() { - mScheduledExecutorService.submit(() -> { + mScheduledExecutorService.execute(() -> { synchronized (this) { ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter = getConversationInfosProtoDiskReadWriter(); @@ -194,6 +191,15 @@ class ConversationStore { return getConversation(mNotifChannelIdToShortcutIdMap.get(notifChannelId)); } + synchronized void onDestroy() { + mConversationInfoMap.clear(); + mContactUriToShortcutIdMap.clear(); + mLocusIdToShortcutIdMap.clear(); + mNotifChannelIdToShortcutIdMap.clear(); + mPhoneNumberToShortcutIdMap.clear(); + mConversationInfosProtoDiskReadWriter.deleteConversationsFile(); + } + @MainThread private synchronized void updateConversationsInMemory( @NonNull ConversationInfo conversationInfo) { @@ -239,8 +245,7 @@ class ConversationStore { } if (mConversationInfosProtoDiskReadWriter == null) { mConversationInfosProtoDiskReadWriter = new ConversationInfosProtoDiskReadWriter( - mPackageDir, CONVERSATIONS_FILE_NAME, DISK_WRITE_DELAY, - mScheduledExecutorService); + mPackageDir, CONVERSATIONS_FILE_NAME, mScheduledExecutorService); } return mConversationInfosProtoDiskReadWriter; } @@ -264,16 +269,16 @@ class ConversationStore { return conversationInfo; } - /** Reads and writes {@link ConversationInfo} on disk. */ - static class ConversationInfosProtoDiskReadWriter extends + /** Reads and writes {@link ConversationInfo}s on disk. */ + private static class ConversationInfosProtoDiskReadWriter extends AbstractProtoDiskReadWriter<List<ConversationInfo>> { private final String mConversationInfoFileName; - ConversationInfosProtoDiskReadWriter(@NonNull File baseDir, + ConversationInfosProtoDiskReadWriter(@NonNull File rootDir, @NonNull String conversationInfoFileName, - long writeDelayMs, @NonNull ScheduledExecutorService scheduledExecutorService) { - super(baseDir, writeDelayMs, scheduledExecutorService); + @NonNull ScheduledExecutorService scheduledExecutorService) { + super(rootDir, scheduledExecutorService); mConversationInfoFileName = conversationInfoFileName; } @@ -328,5 +333,10 @@ class ConversationStore { void saveConversationsImmediately(@NonNull List<ConversationInfo> conversationInfos) { saveImmediately(mConversationInfoFileName, conversationInfos); } + + @WorkerThread + void deleteConversationsFile() { + delete(mConversationInfoFileName); + } } } diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index 6b97c98b0029..27c169227637 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -51,7 +51,6 @@ import android.provider.Telephony.MmsSms; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.telecom.TelecomManager; -import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArraySet; import android.util.SparseArray; @@ -275,40 +274,35 @@ public class DataManager { mContext.getPackageName(), intentFilter, callingUserId); } - /** Reports the {@link AppTargetEvent} from App Prediction Manager. */ - public void reportAppTargetEvent(@NonNull AppTargetEvent event, + /** Reports the sharing related {@link AppTargetEvent} from App Prediction Manager. */ + public void reportShareTargetEvent(@NonNull AppTargetEvent event, @Nullable IntentFilter intentFilter) { AppTarget appTarget = event.getTarget(); - ShortcutInfo shortcutInfo = appTarget != null ? appTarget.getShortcutInfo() : null; - if (shortcutInfo == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) { - return; - } - PackageData packageData = getPackage(appTarget.getPackageName(), - appTarget.getUser().getIdentifier()); - if (packageData == null) { + if (appTarget == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) { return; } + UserData userData = getUnlockedUserData(appTarget.getUser().getIdentifier()); + PackageData packageData = userData.getOrCreatePackageData(appTarget.getPackageName()); + String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null; + @Event.EventType int eventType = mimeTypeToShareEventType(mimeType); + EventHistoryImpl eventHistory; if (ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE.equals(event.getLaunchLocation())) { - String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null; - String shortcutId = shortcutInfo.getId(); - if (packageData.getConversationStore().getConversation(shortcutId) == null - || TextUtils.isEmpty(mimeType)) { + // Direct share event + if (appTarget.getShortcutInfo() == null) { return; } - EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory( - EventStore.CATEGORY_SHORTCUT_BASED, shortcutInfo.getId()); - @Event.EventType int eventType; - if (mimeType.startsWith("text/")) { - eventType = Event.TYPE_SHARE_TEXT; - } else if (mimeType.startsWith("image/")) { - eventType = Event.TYPE_SHARE_IMAGE; - } else if (mimeType.startsWith("video/")) { - eventType = Event.TYPE_SHARE_VIDEO; - } else { - eventType = Event.TYPE_SHARE_OTHER; + String shortcutId = appTarget.getShortcutInfo().getId(); + if (packageData.getConversationStore().getConversation(shortcutId) == null) { + addOrUpdateConversationInfo(appTarget.getShortcutInfo()); } - eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType)); + eventHistory = packageData.getEventStore().getOrCreateEventHistory( + EventStore.CATEGORY_SHORTCUT_BASED, shortcutId); + } else { + // App share event + eventHistory = packageData.getEventStore().getOrCreateEventHistory( + EventStore.CATEGORY_CLASS_BASED, appTarget.getClassName()); } + eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType)); } /** Prunes the data for the specified user. */ @@ -319,12 +313,11 @@ public class DataManager { } pruneUninstalledPackageData(userData); - long currentTimeMillis = System.currentTimeMillis(); userData.forAllPackages(packageData -> { if (signal.isCanceled()) { return; } - packageData.getEventStore().pruneOldEvents(currentTimeMillis); + packageData.getEventStore().pruneOldEvents(); if (!packageData.isDefaultDialer()) { packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_CALL); } @@ -335,6 +328,17 @@ public class DataManager { }); } + private int mimeTypeToShareEventType(String mimeType) { + if (mimeType.startsWith("text/")) { + return Event.TYPE_SHARE_TEXT; + } else if (mimeType.startsWith("image/")) { + return Event.TYPE_SHARE_IMAGE; + } else if (mimeType.startsWith("video/")) { + return Event.TYPE_SHARE_VIDEO; + } + return Event.TYPE_SHARE_OTHER; + } + private void pruneUninstalledPackageData(@NonNull UserData userData) { Set<String> installApps = new ArraySet<>(); mPackageManagerInternal.forEachInstalledPackage( @@ -410,12 +414,13 @@ public class DataManager { EventStore.CATEGORY_SHORTCUT_BASED, shortcutId); } + private boolean isPersonShortcut(@NonNull ShortcutInfo shortcutInfo) { + return shortcutInfo.getPersons() != null && shortcutInfo.getPersons().length != 0; + } + @VisibleForTesting @WorkerThread - void onShortcutAddedOrUpdated(@NonNull ShortcutInfo shortcutInfo) { - if (shortcutInfo.getPersons() == null || shortcutInfo.getPersons().length == 0) { - return; - } + void addOrUpdateConversationInfo(@NonNull ShortcutInfo shortcutInfo) { UserData userData = getUnlockedUserData(shortcutInfo.getUserId()); if (userData == null) { return; @@ -431,24 +436,24 @@ public class DataManager { builder.setShortcutId(shortcutInfo.getId()); builder.setLocusId(shortcutInfo.getLocusId()); builder.setShortcutFlags(shortcutInfo.getFlags()); - - Person person = shortcutInfo.getPersons()[0]; - builder.setPersonImportant(person.isImportant()); - builder.setPersonBot(person.isBot()); - String contactUri = person.getUri(); - if (contactUri != null) { - ContactsQueryHelper helper = mInjector.createContactsQueryHelper(mContext); - if (helper.query(contactUri)) { - builder.setContactUri(helper.getContactUri()); - builder.setContactStarred(helper.isStarred()); - builder.setContactPhoneNumber(helper.getPhoneNumber()); + builder.setContactUri(null); + builder.setContactPhoneNumber(null); + builder.setContactStarred(false); + + if (shortcutInfo.getPersons() != null && shortcutInfo.getPersons().length != 0) { + Person person = shortcutInfo.getPersons()[0]; + builder.setPersonImportant(person.isImportant()); + builder.setPersonBot(person.isBot()); + String contactUri = person.getUri(); + if (contactUri != null) { + ContactsQueryHelper helper = mInjector.createContactsQueryHelper(mContext); + if (helper.query(contactUri)) { + builder.setContactUri(helper.getContactUri()); + builder.setContactStarred(helper.isStarred()); + builder.setContactPhoneNumber(helper.getPhoneNumber()); + } } - } else { - builder.setContactUri(null); - builder.setContactPhoneNumber(null); - builder.setContactStarred(false); } - conversationStore.addOrUpdate(builder.build()); } @@ -626,7 +631,9 @@ public class DataManager { List<ShortcutInfo> shortcuts = getShortcuts(packageName, userId, /*shortcutIds=*/ null); for (ShortcutInfo shortcut : shortcuts) { - onShortcutAddedOrUpdated(shortcut); + if (isPersonShortcut(shortcut)) { + addOrUpdateConversationInfo(shortcut); + } } }); } diff --git a/services/people/java/com/android/server/people/data/Event.java b/services/people/java/com/android/server/people/data/Event.java index 81411c00db51..a929f6f68427 100644 --- a/services/people/java/com/android/server/people/data/Event.java +++ b/services/people/java/com/android/server/people/data/Event.java @@ -20,7 +20,13 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.text.format.DateFormat; import android.util.ArraySet; +import android.util.Slog; +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoOutputStream; +import com.android.server.people.PeopleEventProto; + +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; @@ -29,6 +35,8 @@ import java.util.Set; /** An event representing the interaction with a specific conversation or app. */ public class Event { + private static final String TAG = Event.class.getSimpleName(); + public static final int TYPE_SHORTCUT_INVOCATION = 1; public static final int TYPE_NOTIFICATION_POSTED = 2; @@ -142,6 +150,36 @@ public class Event { return mDurationSeconds; } + /** Writes field members to {@link ProtoOutputStream}. */ + void writeToProto(@NonNull ProtoOutputStream protoOutputStream) { + protoOutputStream.write(PeopleEventProto.EVENT_TYPE, mType); + protoOutputStream.write(PeopleEventProto.TIME, mTimestamp); + protoOutputStream.write(PeopleEventProto.DURATION, mDurationSeconds); + } + + /** Reads from {@link ProtoInputStream} and constructs {@link Event}. */ + @NonNull + static Event readFromProto(@NonNull ProtoInputStream protoInputStream) throws IOException { + Event.Builder builder = new Event.Builder(); + while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (protoInputStream.getFieldNumber()) { + case (int) PeopleEventProto.EVENT_TYPE: + builder.setType(protoInputStream.readInt(PeopleEventProto.EVENT_TYPE)); + break; + case (int) PeopleEventProto.TIME: + builder.setTimestamp(protoInputStream.readLong(PeopleEventProto.TIME)); + break; + case (int) PeopleEventProto.DURATION: + builder.setDurationSeconds(protoInputStream.readInt(PeopleEventProto.DURATION)); + break; + default: + Slog.w(TAG, "Could not read undefined field: " + + protoInputStream.getFieldNumber()); + } + } + return builder.build(); + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -177,12 +215,14 @@ public class Event { /** Builder class for {@link Event} objects. */ static class Builder { - private final long mTimestamp; + private long mTimestamp; - private final int mType; + private int mType; private int mDurationSeconds; + private Builder() {} + Builder(long timestamp, @EventType int type) { mTimestamp = timestamp; mType = type; @@ -193,6 +233,16 @@ public class Event { return this; } + private Builder setTimestamp(long timestamp) { + mTimestamp = timestamp; + return this; + } + + private Builder setType(int type) { + mType = type; + return this; + } + Event build() { return new Event(this); } diff --git a/services/people/java/com/android/server/people/data/EventHistoryImpl.java b/services/people/java/com/android/server/people/data/EventHistoryImpl.java index 6bef1db5582e..85661c622fc2 100644 --- a/services/people/java/com/android/server/people/data/EventHistoryImpl.java +++ b/services/people/java/com/android/server/people/data/EventHistoryImpl.java @@ -16,42 +16,149 @@ package com.android.server.people.data; +import android.annotation.MainThread; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.WorkerThread; +import android.net.Uri; +import android.text.format.DateUtils; +import android.util.ArrayMap; +import android.util.Slog; import android.util.SparseArray; +import android.util.proto.ProtoInputStream; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.people.PeopleEventIndexesProto; +import com.android.server.people.PeopleEventsProto; +import com.android.server.people.TypedPeopleEventIndexProto; +import com.google.android.collect.Lists; + +import java.io.File; +import java.io.IOException; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; + class EventHistoryImpl implements EventHistory { + private static final long MAX_EVENTS_AGE = 4L * DateUtils.HOUR_IN_MILLIS; + private static final long PRUNE_OLD_EVENTS_DELAY = 15L * DateUtils.MINUTE_IN_MILLIS; + + private static final String EVENTS_DIR = "events"; + private static final String INDEXES_DIR = "indexes"; + private final Injector mInjector; + private final ScheduledExecutorService mScheduledExecutorService; + private final EventsProtoDiskReadWriter mEventsProtoDiskReadWriter; + private final EventIndexesProtoDiskReadWriter mEventIndexesProtoDiskReadWriter; // Event Type -> Event Index + @GuardedBy("this") private final SparseArray<EventIndex> mEventIndexArray = new SparseArray<>(); + @GuardedBy("this") private final EventList mRecentEvents = new EventList(); - EventHistoryImpl() { - mInjector = new Injector(); + private long mLastPruneTime; + + EventHistoryImpl(@NonNull File rootDir, + @NonNull ScheduledExecutorService scheduledExecutorService) { + this(new Injector(), rootDir, scheduledExecutorService); } @VisibleForTesting - EventHistoryImpl(Injector injector) { + EventHistoryImpl(@NonNull Injector injector, @NonNull File rootDir, + @NonNull ScheduledExecutorService scheduledExecutorService) { mInjector = injector; + mScheduledExecutorService = scheduledExecutorService; + mLastPruneTime = injector.currentTimeMillis(); + + File eventsDir = new File(rootDir, EVENTS_DIR); + mEventsProtoDiskReadWriter = new EventsProtoDiskReadWriter(eventsDir, + mScheduledExecutorService); + File indexesDir = new File(rootDir, INDEXES_DIR); + mEventIndexesProtoDiskReadWriter = new EventIndexesProtoDiskReadWriter(indexesDir, + scheduledExecutorService); + } + + @WorkerThread + @NonNull + static Map<String, EventHistoryImpl> eventHistoriesImplFromDisk(File categoryDir, + ScheduledExecutorService scheduledExecutorService) { + return eventHistoriesImplFromDisk(new Injector(), categoryDir, scheduledExecutorService); + } + + @VisibleForTesting + @NonNull + static Map<String, EventHistoryImpl> eventHistoriesImplFromDisk(Injector injector, + File categoryDir, ScheduledExecutorService scheduledExecutorService) { + Map<String, EventHistoryImpl> results = new ArrayMap<>(); + File[] keyDirs = categoryDir.listFiles(File::isDirectory); + if (keyDirs == null) { + return results; + } + for (File keyDir : keyDirs) { + File[] dirContents = keyDir.listFiles( + (dir, name) -> EVENTS_DIR.equals(name) || INDEXES_DIR.equals(name)); + if (dirContents != null && dirContents.length == 2) { + EventHistoryImpl eventHistory = new EventHistoryImpl(injector, keyDir, + scheduledExecutorService); + eventHistory.loadFromDisk(); + results.put(Uri.decode(keyDir.getName()), eventHistory); + } + } + return results; + } + + /** + * Loads recent events and indexes from disk to memory in a background thread. This should be + * called after the device powers on and the user has been unlocked. + */ + @VisibleForTesting + @MainThread + synchronized void loadFromDisk() { + mScheduledExecutorService.execute(() -> { + synchronized (this) { + EventList diskEvents = mEventsProtoDiskReadWriter.loadRecentEventsFromDisk(); + if (diskEvents != null) { + diskEvents.removeOldEvents(mInjector.currentTimeMillis() - MAX_EVENTS_AGE); + mRecentEvents.addAll(diskEvents.getAllEvents()); + } + + SparseArray<EventIndex> diskIndexes = + mEventIndexesProtoDiskReadWriter.loadIndexesFromDisk(); + if (diskIndexes != null) { + for (int i = 0; i < diskIndexes.size(); i++) { + mEventIndexArray.put(diskIndexes.keyAt(i), diskIndexes.valueAt(i)); + } + } + } + }); + } + + /** + * Flushes events and indexes immediately. This should be called when device is powering off. + */ + @MainThread + synchronized void saveToDisk() { + mEventsProtoDiskReadWriter.saveEventsImmediately(mRecentEvents); + mEventIndexesProtoDiskReadWriter.saveIndexesImmediately(mEventIndexArray); } @Override @NonNull - public EventIndex getEventIndex(@Event.EventType int eventType) { + public synchronized EventIndex getEventIndex(@Event.EventType int eventType) { EventIndex eventIndex = mEventIndexArray.get(eventType); return eventIndex != null ? new EventIndex(eventIndex) : mInjector.createEventIndex(); } @Override @NonNull - public EventIndex getEventIndex(Set<Integer> eventTypes) { + public synchronized EventIndex getEventIndex(Set<Integer> eventTypes) { EventIndex combined = mInjector.createEventIndex(); for (@Event.EventType int eventType : eventTypes) { EventIndex eventIndex = mEventIndexArray.get(eventType); @@ -64,29 +171,42 @@ class EventHistoryImpl implements EventHistory { @Override @NonNull - public List<Event> queryEvents(Set<Integer> eventTypes, long startTime, long endTime) { + public synchronized List<Event> queryEvents(Set<Integer> eventTypes, long startTime, + long endTime) { return mRecentEvents.queryEvents(eventTypes, startTime, endTime); } - void addEvent(Event event) { - EventIndex eventIndex = mEventIndexArray.get(event.getType()); - if (eventIndex == null) { - eventIndex = mInjector.createEventIndex(); - mEventIndexArray.put(event.getType(), eventIndex); - } - eventIndex.addEvent(event.getTimestamp()); - mRecentEvents.add(event); + synchronized void addEvent(Event event) { + pruneOldEvents(); + addEventInMemory(event); + mEventsProtoDiskReadWriter.scheduleEventsSave(mRecentEvents); + mEventIndexesProtoDiskReadWriter.scheduleIndexesSave(mEventIndexArray); } - void onDestroy() { + synchronized void onDestroy() { mEventIndexArray.clear(); mRecentEvents.clear(); - // TODO: STOPSHIP: Delete the data files. + mEventsProtoDiskReadWriter.deleteRecentEventsFile(); + mEventIndexesProtoDiskReadWriter.deleteIndexesFile(); } /** Deletes the events data that exceeds the retention period. */ - void pruneOldEvents(long currentTimeMillis) { - // TODO: STOPSHIP: Delete the old events data files. + synchronized void pruneOldEvents() { + long currentTime = mInjector.currentTimeMillis(); + if (currentTime - mLastPruneTime > PRUNE_OLD_EVENTS_DELAY) { + mRecentEvents.removeOldEvents(currentTime - MAX_EVENTS_AGE); + mLastPruneTime = currentTime; + } + } + + private synchronized void addEventInMemory(Event event) { + EventIndex eventIndex = mEventIndexArray.get(event.getType()); + if (eventIndex == null) { + eventIndex = mInjector.createEventIndex(); + mEventIndexArray.put(event.getType(), eventIndex); + } + eventIndex.addEvent(event.getTimestamp()); + mRecentEvents.add(event); } @VisibleForTesting @@ -95,5 +215,174 @@ class EventHistoryImpl implements EventHistory { EventIndex createEventIndex() { return new EventIndex(); } + + long currentTimeMillis() { + return System.currentTimeMillis(); + } + } + + /** Reads and writes {@link Event}s on disk. */ + private static class EventsProtoDiskReadWriter extends AbstractProtoDiskReadWriter<EventList> { + + private static final String TAG = EventsProtoDiskReadWriter.class.getSimpleName(); + + private static final String RECENT_FILE = "recent"; + + + EventsProtoDiskReadWriter(@NonNull File rootDir, + @NonNull ScheduledExecutorService scheduledExecutorService) { + super(rootDir, scheduledExecutorService); + rootDir.mkdirs(); + } + + @Override + ProtoStreamWriter<EventList> protoStreamWriter() { + return (protoOutputStream, data) -> { + for (Event event : data.getAllEvents()) { + long token = protoOutputStream.start(PeopleEventsProto.EVENTS); + event.writeToProto(protoOutputStream); + protoOutputStream.end(token); + } + }; + } + + @Override + ProtoStreamReader<EventList> protoStreamReader() { + return protoInputStream -> { + List<Event> results = Lists.newArrayList(); + try { + while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + if (protoInputStream.getFieldNumber() != (int) PeopleEventsProto.EVENTS) { + continue; + } + long token = protoInputStream.start(PeopleEventsProto.EVENTS); + Event event = Event.readFromProto(protoInputStream); + protoInputStream.end(token); + results.add(event); + } + } catch (IOException e) { + Slog.e(TAG, "Failed to read protobuf input stream.", e); + } + EventList eventList = new EventList(); + eventList.addAll(results); + return eventList; + }; + } + + @MainThread + void scheduleEventsSave(EventList recentEvents) { + scheduleSave(RECENT_FILE, recentEvents); + } + + @MainThread + void saveEventsImmediately(EventList recentEvents) { + saveImmediately(RECENT_FILE, recentEvents); + } + + /** + * Loads recent events from disk. This should be called when device is powered on. + */ + @WorkerThread + @Nullable + EventList loadRecentEventsFromDisk() { + return read(RECENT_FILE); + } + + @WorkerThread + void deleteRecentEventsFile() { + delete(RECENT_FILE); + } + } + + /** Reads and writes {@link EventIndex}s on disk. */ + private static class EventIndexesProtoDiskReadWriter extends + AbstractProtoDiskReadWriter<SparseArray<EventIndex>> { + + private static final String TAG = EventIndexesProtoDiskReadWriter.class.getSimpleName(); + + private static final String INDEXES_FILE = "index"; + + EventIndexesProtoDiskReadWriter(@NonNull File rootDir, + @NonNull ScheduledExecutorService scheduledExecutorService) { + super(rootDir, scheduledExecutorService); + rootDir.mkdirs(); + } + + @Override + ProtoStreamWriter<SparseArray<EventIndex>> protoStreamWriter() { + return (protoOutputStream, data) -> { + for (int i = 0; i < data.size(); i++) { + @Event.EventType int eventType = data.keyAt(i); + EventIndex index = data.valueAt(i); + long token = protoOutputStream.start(PeopleEventIndexesProto.TYPED_INDEXES); + protoOutputStream.write(TypedPeopleEventIndexProto.EVENT_TYPE, eventType); + long indexToken = protoOutputStream.start(TypedPeopleEventIndexProto.INDEX); + index.writeToProto(protoOutputStream); + protoOutputStream.end(indexToken); + protoOutputStream.end(token); + } + }; + } + + @Override + ProtoStreamReader<SparseArray<EventIndex>> protoStreamReader() { + return protoInputStream -> { + SparseArray<EventIndex> results = new SparseArray<>(); + try { + while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + if (protoInputStream.getFieldNumber() + != (int) PeopleEventIndexesProto.TYPED_INDEXES) { + continue; + } + long token = protoInputStream.start(PeopleEventIndexesProto.TYPED_INDEXES); + @Event.EventType int eventType = 0; + EventIndex index = EventIndex.EMPTY; + while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (protoInputStream.getFieldNumber()) { + case (int) TypedPeopleEventIndexProto.EVENT_TYPE: + eventType = protoInputStream.readInt( + TypedPeopleEventIndexProto.EVENT_TYPE); + break; + case (int) TypedPeopleEventIndexProto.INDEX: + long indexToken = protoInputStream.start( + TypedPeopleEventIndexProto.INDEX); + index = EventIndex.readFromProto(protoInputStream); + protoInputStream.end(indexToken); + break; + default: + Slog.w(TAG, "Could not read undefined field: " + + protoInputStream.getFieldNumber()); + } + } + results.append(eventType, index); + protoInputStream.end(token); + } + } catch (IOException e) { + Slog.e(TAG, "Failed to read protobuf input stream.", e); + } + return results; + }; + } + + @MainThread + void scheduleIndexesSave(SparseArray<EventIndex> indexes) { + scheduleSave(INDEXES_FILE, indexes); + } + + @MainThread + void saveIndexesImmediately(SparseArray<EventIndex> indexes) { + saveImmediately(INDEXES_FILE, indexes); + } + + @WorkerThread + @Nullable + SparseArray<EventIndex> loadIndexesFromDisk() { + return read(INDEXES_FILE); + } + + @WorkerThread + void deleteIndexesFile() { + delete(INDEXES_FILE); + } } } diff --git a/services/people/java/com/android/server/people/data/EventIndex.java b/services/people/java/com/android/server/people/data/EventIndex.java index 069ec0e80ee4..47b620773180 100644 --- a/services/people/java/com/android/server/people/data/EventIndex.java +++ b/services/people/java/com/android/server/people/data/EventIndex.java @@ -21,9 +21,14 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.text.format.DateFormat; import android.util.Range; +import android.util.Slog; +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.people.PeopleEventIndexProto; +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.time.Instant; @@ -34,6 +39,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.TimeZone; import java.util.function.Function; @@ -60,6 +66,7 @@ import java.util.function.Function; * </pre> */ public class EventIndex { + private static final String TAG = EventIndex.class.getSimpleName(); private static final int RETENTION_DAYS = 63; @@ -118,22 +125,23 @@ public class EventIndex { private final Injector mInjector; EventIndex() { - mInjector = new Injector(); - mEventBitmaps = new long[]{0L, 0L, 0L, 0L}; - mLastUpdatedTime = mInjector.currentTimeMillis(); + this(new Injector()); } - EventIndex(EventIndex from) { - mInjector = new Injector(); - mEventBitmaps = Arrays.copyOf(from.mEventBitmaps, TIME_SLOT_TYPES_COUNT); - mLastUpdatedTime = from.mLastUpdatedTime; + EventIndex(@NonNull EventIndex from) { + this(from.mInjector, Arrays.copyOf(from.mEventBitmaps, TIME_SLOT_TYPES_COUNT), + from.mLastUpdatedTime); } @VisibleForTesting - EventIndex(Injector injector) { + EventIndex(@NonNull Injector injector) { + this(injector, new long[]{0L, 0L, 0L, 0L}, injector.currentTimeMillis()); + } + + private EventIndex(@NonNull Injector injector, long[] eventBitmaps, long lastUpdatedTime) { mInjector = injector; - mEventBitmaps = new long[]{0L, 0L, 0L, 0L}; - mLastUpdatedTime = mInjector.currentTimeMillis(); + mEventBitmaps = eventBitmaps; + mLastUpdatedTime = lastUpdatedTime; } /** @@ -232,6 +240,31 @@ public class EventIndex { return sb.toString(); } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof EventIndex)) { + return false; + } + EventIndex other = (EventIndex) obj; + return mLastUpdatedTime == other.mLastUpdatedTime + && Arrays.equals(mEventBitmaps, other.mEventBitmaps); + } + + @Override + public int hashCode() { + return Objects.hash(mLastUpdatedTime, mEventBitmaps); + } + + synchronized void writeToProto(@NonNull ProtoOutputStream protoOutputStream) { + for (long bitmap : mEventBitmaps) { + protoOutputStream.write(PeopleEventIndexProto.EVENT_BITMAPS, bitmap); + } + protoOutputStream.write(PeopleEventIndexProto.LAST_UPDATED_TIME, mLastUpdatedTime); + } + /** Shifts the event bitmaps to make them up-to-date. */ private void updateEventBitmaps(long currentTimeMillis) { for (int slotType = 0; slotType < TIME_SLOT_TYPES_COUNT; slotType++) { @@ -249,6 +282,28 @@ public class EventIndex { mLastUpdatedTime = currentTimeMillis; } + static EventIndex readFromProto(@NonNull ProtoInputStream protoInputStream) throws IOException { + int bitmapIndex = 0; + long[] eventBitmaps = new long[TIME_SLOT_TYPES_COUNT]; + long lastUpdated = 0L; + while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (protoInputStream.getFieldNumber()) { + case (int) PeopleEventIndexProto.EVENT_BITMAPS: + eventBitmaps[bitmapIndex++] = protoInputStream.readLong( + PeopleEventIndexProto.EVENT_BITMAPS); + break; + case (int) PeopleEventIndexProto.LAST_UPDATED_TIME: + lastUpdated = protoInputStream.readLong( + PeopleEventIndexProto.LAST_UPDATED_TIME); + break; + default: + Slog.e(TAG, "Could not read undefined field: " + + protoInputStream.getFieldNumber()); + } + } + return new EventIndex(new Injector(), eventBitmaps, lastUpdated); + } + private static LocalDateTime toLocalDateTime(long epochMilli) { return LocalDateTime.ofInstant( Instant.ofEpochMilli(epochMilli), TimeZone.getDefault().toZoneId()); diff --git a/services/people/java/com/android/server/people/data/EventList.java b/services/people/java/com/android/server/people/data/EventList.java index d770f912ea40..3788d6c92acf 100644 --- a/services/people/java/com/android/server/people/data/EventList.java +++ b/services/people/java/com/android/server/people/data/EventList.java @@ -18,6 +18,8 @@ package com.android.server.people.data; import android.annotation.NonNull; +import com.android.internal.util.CollectionUtils; + import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -41,6 +43,16 @@ class EventList { mEvents.add(index, event); } + + /** + * Call #add on each event to keep the order. + */ + void addAll(@NonNull List<Event> events) { + for (Event event : events) { + add(event); + } + } + /** * Returns a {@link List} of {@link Event}s whose timestamps are between the specified {@code * fromTimestamp}, inclusive, and {@code toTimestamp} exclusive, and match the specified event @@ -73,6 +85,44 @@ class EventList { mEvents.clear(); } + /** + * Returns a copy of events. + */ + @NonNull + List<Event> getAllEvents() { + return CollectionUtils.copyOf(mEvents); + } + + /** + * Remove events that are older than the specified cut off threshold timestamp. + */ + void removeOldEvents(long cutOffThreshold) { + + // Everything before the cut off is considered old, and should be removed. + int cutOffIndex = firstIndexOnOrAfter(cutOffThreshold); + if (cutOffIndex == 0) { + return; + } + + // Clear entire list if the cut off is greater than the last element. + int eventsSize = mEvents.size(); + if (cutOffIndex == eventsSize) { + mEvents.clear(); + return; + } + + // Reorder the list starting from the cut off index. + int i = 0; + for (; cutOffIndex < eventsSize; i++, cutOffIndex++) { + mEvents.set(i, mEvents.get(cutOffIndex)); + } + + // Clear the list after reordering. + if (eventsSize > i) { + mEvents.subList(i, eventsSize).clear(); + } + } + /** Returns the first index whose timestamp is greater or equal to the provided timestamp. */ private int firstIndexOnOrAfter(long timestamp) { int result = mEvents.size(); diff --git a/services/people/java/com/android/server/people/data/EventStore.java b/services/people/java/com/android/server/people/data/EventStore.java index c8d44ac07106..00d4241fc5f7 100644 --- a/services/people/java/com/android/server/people/data/EventStore.java +++ b/services/people/java/com/android/server/people/data/EventStore.java @@ -17,16 +17,22 @@ package com.android.server.people.data; import android.annotation.IntDef; +import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.net.Uri; import android.util.ArrayMap; +import com.android.internal.annotations.GuardedBy; + +import java.io.File; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; import java.util.function.Predicate; /** The store that stores and accesses the events data for a package. */ @@ -57,14 +63,58 @@ class EventStore { @Retention(RetentionPolicy.SOURCE) @interface EventCategory {} + @GuardedBy("this") private final List<Map<String, EventHistoryImpl>> mEventHistoryMaps = new ArrayList<>(); + private final List<File> mEventsCategoryDirs = new ArrayList<>(); + private final ScheduledExecutorService mScheduledExecutorService; - EventStore() { + EventStore(@NonNull File packageDir, + @NonNull ScheduledExecutorService scheduledExecutorService) { mEventHistoryMaps.add(CATEGORY_SHORTCUT_BASED, new ArrayMap<>()); mEventHistoryMaps.add(CATEGORY_LOCUS_ID_BASED, new ArrayMap<>()); mEventHistoryMaps.add(CATEGORY_CALL, new ArrayMap<>()); mEventHistoryMaps.add(CATEGORY_SMS, new ArrayMap<>()); mEventHistoryMaps.add(CATEGORY_CLASS_BASED, new ArrayMap<>()); + + File eventDir = new File(packageDir, "event"); + mEventsCategoryDirs.add(CATEGORY_SHORTCUT_BASED, new File(eventDir, "shortcut")); + mEventsCategoryDirs.add(CATEGORY_LOCUS_ID_BASED, new File(eventDir, "locus")); + mEventsCategoryDirs.add(CATEGORY_CALL, new File(eventDir, "call")); + mEventsCategoryDirs.add(CATEGORY_SMS, new File(eventDir, "sms")); + mEventsCategoryDirs.add(CATEGORY_CLASS_BASED, new File(eventDir, "class")); + + mScheduledExecutorService = scheduledExecutorService; + } + + /** + * Loads existing {@link EventHistoryImpl}s from disk. This should be called when device powers + * on and user is unlocked. + */ + @MainThread + void loadFromDisk() { + mScheduledExecutorService.execute(() -> { + synchronized (this) { + for (@EventCategory int category = 0; category < mEventsCategoryDirs.size(); + category++) { + File categoryDir = mEventsCategoryDirs.get(category); + Map<String, EventHistoryImpl> existingEventHistoriesImpl = + EventHistoryImpl.eventHistoriesImplFromDisk(categoryDir, + mScheduledExecutorService); + mEventHistoryMaps.get(category).putAll(existingEventHistoriesImpl); + } + } + }); + } + + /** + * Flushes all {@link EventHistoryImpl}s to disk. Should be called when device is shutting down. + */ + synchronized void saveToDisk() { + for (Map<String, EventHistoryImpl> map : mEventHistoryMaps) { + for (EventHistoryImpl eventHistory : map.values()) { + eventHistory.saveToDisk(); + } + } } /** @@ -74,7 +124,7 @@ class EventStore { * name. */ @Nullable - EventHistory getEventHistory(@EventCategory int category, String key) { + synchronized EventHistory getEventHistory(@EventCategory int category, String key) { return mEventHistoryMaps.get(category).get(key); } @@ -87,8 +137,11 @@ class EventStore { * name. */ @NonNull - EventHistoryImpl getOrCreateEventHistory(@EventCategory int category, String key) { - return mEventHistoryMaps.get(category).computeIfAbsent(key, k -> new EventHistoryImpl()); + synchronized EventHistoryImpl getOrCreateEventHistory(@EventCategory int category, String key) { + return mEventHistoryMaps.get(category).computeIfAbsent(key, + k -> new EventHistoryImpl( + new File(mEventsCategoryDirs.get(category), Uri.encode(key)), + mScheduledExecutorService)); } /** @@ -97,7 +150,7 @@ class EventStore { * @param key Category-specific key, it can be shortcut ID, locus ID, phone number, or class * name. */ - void deleteEventHistory(@EventCategory int category, String key) { + synchronized void deleteEventHistory(@EventCategory int category, String key) { EventHistoryImpl eventHistory = mEventHistoryMaps.get(category).remove(key); if (eventHistory != null) { eventHistory.onDestroy(); @@ -105,16 +158,18 @@ class EventStore { } /** Deletes all the events and index data for the specified category from disk. */ - void deleteEventHistories(@EventCategory int category) { + synchronized void deleteEventHistories(@EventCategory int category) { + for (EventHistoryImpl eventHistory : mEventHistoryMaps.get(category).values()) { + eventHistory.onDestroy(); + } mEventHistoryMaps.get(category).clear(); - // TODO: Implement this method to delete the data from disk. } /** Deletes the events data that exceeds the retention period. */ - void pruneOldEvents(long currentTimeMillis) { + synchronized void pruneOldEvents() { for (Map<String, EventHistoryImpl> map : mEventHistoryMaps) { for (EventHistoryImpl eventHistory : map.values()) { - eventHistory.pruneOldEvents(currentTimeMillis); + eventHistory.pruneOldEvents(); } } } @@ -125,7 +180,8 @@ class EventStore { * * @param keyChecker Check whether there exists a conversation contains this key. */ - void pruneOrphanEventHistories(@EventCategory int category, Predicate<String> keyChecker) { + synchronized void pruneOrphanEventHistories(@EventCategory int category, + Predicate<String> keyChecker) { Set<String> keys = mEventHistoryMaps.get(category).keySet(); List<String> keysToDelete = new ArrayList<>(); for (String key : keys) { @@ -141,4 +197,12 @@ class EventStore { } } } + + synchronized void onDestroy() { + for (Map<String, EventHistoryImpl> map : mEventHistoryMaps) { + for (EventHistoryImpl eventHistory : map.values()) { + eventHistory.onDestroy(); + } + } + } } diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java index c55f97205bc5..d47e2cc1ba90 100644 --- a/services/people/java/com/android/server/people/data/PackageData.java +++ b/services/people/java/com/android/server/people/data/PackageData.java @@ -27,8 +27,10 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.LocusId; import android.text.TextUtils; +import android.util.ArrayMap; import java.io.File; +import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.function.Consumer; import java.util.function.Predicate; @@ -63,22 +65,50 @@ public class PackageData { mUserId = userId; mPackageDataDir = new File(perUserPeopleDataDir, mPackageName); + mPackageDataDir.mkdirs(); + mConversationStore = new ConversationStore(mPackageDataDir, scheduledExecutorService, helper); - mEventStore = new EventStore(); + mEventStore = new EventStore(mPackageDataDir, scheduledExecutorService); mIsDefaultDialerPredicate = isDefaultDialerPredicate; mIsDefaultSmsAppPredicate = isDefaultSmsAppPredicate; } - /** Called when user is unlocked. */ - void loadFromDisk() { - mPackageDataDir.mkdirs(); + /** + * Returns a map of package directory names as keys and their associated {@link PackageData}. + * This should be called when device is powered on and unlocked. + */ + @NonNull + static Map<String, PackageData> packagesDataFromDisk(@UserIdInt int userId, + @NonNull Predicate<String> isDefaultDialerPredicate, + @NonNull Predicate<String> isDefaultSmsAppPredicate, + @NonNull ScheduledExecutorService scheduledExecutorService, + @NonNull File perUserPeopleDataDir, + @NonNull ContactsQueryHelper helper) { + Map<String, PackageData> results = new ArrayMap<>(); + File[] packageDirs = perUserPeopleDataDir.listFiles(File::isDirectory); + if (packageDirs == null) { + return results; + } + for (File packageDir : packageDirs) { + PackageData packageData = new PackageData(packageDir.getName(), userId, + isDefaultDialerPredicate, isDefaultSmsAppPredicate, scheduledExecutorService, + perUserPeopleDataDir, helper); + packageData.loadFromDisk(); + results.put(packageDir.getName(), packageData); + } + return results; + } + + private void loadFromDisk() { mConversationStore.loadConversationsFromDisk(); + mEventStore.loadFromDisk(); } /** Called when device is shutting down. */ void saveToDisk() { mConversationStore.saveConversationsToDisk(); + mEventStore.saveToDisk(); } @NonNull @@ -222,6 +252,7 @@ public class PackageData { } void onDestroy() { - // TODO: STOPSHIP: Implements this method for the case of package being uninstalled. + mEventStore.onDestroy(); + mConversationStore.onDestroy(); } } diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java index 7ca4b6c76a36..d3cecceed884 100644 --- a/services/people/java/com/android/server/people/data/UserData.java +++ b/services/people/java/com/android/server/people/data/UserData.java @@ -73,9 +73,8 @@ class UserData { // Ensures per user root directory for people data is present, and attempt to load // data from disk. mPerUserPeopleDataDir.mkdirs(); - for (PackageData packageData : mPackageDataMap.values()) { - packageData.loadFromDisk(); - } + mPackageDataMap.putAll(PackageData.packagesDataFromDisk(mUserId, this::isDefaultDialer, + this::isDefaultSmsApp, mScheduledExecutorService, mPerUserPeopleDataDir, mHelper)); } void setUserStopped() { diff --git a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java index 19cf8af5d66b..c89dadc3fbd6 100644 --- a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java +++ b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java @@ -73,6 +73,7 @@ public class AppTargetPredictor { */ @MainThread public void onAppTargetEvent(AppTargetEvent event) { + mCallbackExecutor.execute(() -> reportAppTargetEvent(event)); } /** @@ -104,6 +105,11 @@ public class AppTargetPredictor { return mUpdatePredictionsMethod; } + /** To be overridden by the subclass to report app target event. */ + @WorkerThread + void reportAppTargetEvent(AppTargetEvent event) { + } + /** To be overridden by the subclass to predict the targets. */ @WorkerThread void predictTargets() { diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java index 90d821641149..8e5d75be12b7 100644 --- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java +++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java @@ -16,7 +16,6 @@ package com.android.server.people.prediction; -import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -28,15 +27,18 @@ import android.app.prediction.AppTargetId; import android.content.IntentFilter; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager.ShareShortcutInfo; +import android.util.Range; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ChooserActivity; import com.android.server.people.data.ConversationInfo; import com.android.server.people.data.DataManager; +import com.android.server.people.data.Event; import com.android.server.people.data.EventHistory; import com.android.server.people.data.PackageData; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -52,89 +54,139 @@ class ShareTargetPredictor extends AppTargetPredictor { ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY); } - @MainThread + /** Reports chosen history of direct/app share targets. */ + @WorkerThread @Override - public void onAppTargetEvent(AppTargetEvent event) { - getDataManager().reportAppTargetEvent(event, mIntentFilter); + void reportAppTargetEvent(AppTargetEvent event) { + getDataManager().reportShareTargetEvent(event, mIntentFilter); } + /** Provides prediction on direct share targets */ @WorkerThread @Override - protected void predictTargets() { - List<ShareTarget> shareTargets = getShareTargets(); - // TODO: Rank the share targets with the data in ShareTarget.mConversationData. - List<AppTarget> appTargets = new ArrayList<>(); - for (ShareTarget shareTarget : shareTargets) { - - ShortcutInfo shortcutInfo = shareTarget.getShareShortcutInfo().getShortcutInfo(); - AppTargetId appTargetId = new AppTargetId(shortcutInfo.getId()); - String shareTargetClassName = - shareTarget.getShareShortcutInfo().getTargetComponent().getClassName(); - AppTarget appTarget = new AppTarget.Builder(appTargetId, shortcutInfo) - .setClassName(shareTargetClassName) - .build(); - appTargets.add(appTarget); - if (appTargets.size() >= getPredictionContext().getPredictedTargetCount()) { - break; - } + void predictTargets() { + List<ShareTarget> shareTargets = getDirectShareTargets(); + rankTargets(shareTargets); + List<AppTarget> res = new ArrayList<>(); + for (int i = 0; i < Math.min(getPredictionContext().getPredictedTargetCount(), + shareTargets.size()); i++) { + res.add(shareTargets.get(i).getAppTarget()); } - updatePredictions(appTargets); + updatePredictions(res); } - @VisibleForTesting - List<ShareTarget> getShareTargets() { + /** Provides prediction on app share targets */ + @WorkerThread + @Override + void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) { + List<ShareTarget> shareTargets = getAppShareTargets(targets); + rankTargets(shareTargets); + List<AppTarget> appTargetList = new ArrayList<>(); + shareTargets.forEach(t -> appTargetList.add(t.getAppTarget())); + callback.accept(appTargetList); + } + + private void rankTargets(List<ShareTarget> shareTargets) { + // Rank targets based on recency of sharing history only for the moment. + // TODO: Take more factors into ranking, e.g. frequency, mime type, foreground app. + Collections.sort(shareTargets, (t1, t2) -> { + if (t1.getEventHistory() == null) { + return 1; + } + if (t2.getEventHistory() == null) { + return -1; + } + Range<Long> timeSlot1 = t1.getEventHistory().getEventIndex( + Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot(); + Range<Long> timeSlot2 = t2.getEventHistory().getEventIndex( + Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot(); + if (timeSlot1 == null) { + return 1; + } else if (timeSlot2 == null) { + return -1; + } else { + return -Long.compare(timeSlot1.getUpper(), timeSlot2.getUpper()); + } + }); + } + + private List<ShareTarget> getDirectShareTargets() { List<ShareTarget> shareTargets = new ArrayList<>(); List<ShareShortcutInfo> shareShortcuts = getDataManager().getShareShortcuts(mIntentFilter, mCallingUserId); for (ShareShortcutInfo shareShortcut : shareShortcuts) { ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo(); + AppTarget appTarget = new AppTarget.Builder( + new AppTargetId(shortcutInfo.getId()), + shortcutInfo) + .setClassName(shareShortcut.getTargetComponent().getClassName()) + .build(); String packageName = shortcutInfo.getPackage(); int userId = shortcutInfo.getUserId(); PackageData packageData = getDataManager().getPackage(packageName, userId); - ConversationData conversationData = null; + ConversationInfo conversationInfo = null; + EventHistory eventHistory = null; if (packageData != null) { String shortcutId = shortcutInfo.getId(); - ConversationInfo conversationInfo = - packageData.getConversationInfo(shortcutId); - + conversationInfo = packageData.getConversationInfo(shortcutId); if (conversationInfo != null) { - EventHistory eventHistory = packageData.getEventHistory(shortcutId); - conversationData = new ConversationData( - packageName, userId, conversationInfo, eventHistory); + eventHistory = packageData.getEventHistory(shortcutId); } } - shareTargets.add(new ShareTarget(shareShortcut, conversationData)); + shareTargets.add(new ShareTarget(appTarget, eventHistory, conversationInfo)); } return shareTargets; } + private List<ShareTarget> getAppShareTargets(List<AppTarget> targets) { + List<ShareTarget> shareTargets = new ArrayList<>(); + for (AppTarget target : targets) { + PackageData packageData = getDataManager().getPackage(target.getPackageName(), + target.getUser().getIdentifier()); + shareTargets.add(new ShareTarget(target, + packageData == null ? null + : packageData.getClassLevelEventHistory(target.getClassName()), null)); + } + return shareTargets; + } + @VisibleForTesting static class ShareTarget { @NonNull - private final ShareShortcutInfo mShareShortcutInfo; + private final AppTarget mAppTarget; @Nullable - private final ConversationData mConversationData; - - private ShareTarget(@NonNull ShareShortcutInfo shareShortcutInfo, - @Nullable ConversationData conversationData) { - mShareShortcutInfo = shareShortcutInfo; - mConversationData = conversationData; + private final EventHistory mEventHistory; + @Nullable + private final ConversationInfo mConversationInfo; + + private ShareTarget(@NonNull AppTarget appTarget, + @Nullable EventHistory eventHistory, + @Nullable ConversationInfo conversationInfo) { + mAppTarget = appTarget; + mEventHistory = eventHistory; + mConversationInfo = conversationInfo; } @NonNull @VisibleForTesting - ShareShortcutInfo getShareShortcutInfo() { - return mShareShortcutInfo; + AppTarget getAppTarget() { + return mAppTarget; + } + + @Nullable + @VisibleForTesting + EventHistory getEventHistory() { + return mEventHistory; } @Nullable @VisibleForTesting - ConversationData getConversationData() { - return mConversationData; + ConversationInfo getConversationInfo() { + return mConversationInfo; } } } 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 d7a3cfd8aeca..1bf9c2a0822e 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 @@ -73,7 +73,9 @@ public class JobStatusTest { private static final double DELTA = 0.00001; private static final String TEST_PACKAGE = "job.test.package"; private static final ComponentName TEST_JOB_COMPONENT = new ComponentName(TEST_PACKAGE, "test"); - private static final Uri TEST_MEDIA_URI = Uri.parse("content://media/path/to/media"); + + private static final Uri IMAGES_MEDIA_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + private static final Uri VIDEO_MEDIA_URI = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; @Mock private JobSchedulerInternal mJobSchedulerInternal; @@ -127,7 +129,7 @@ public class JobStatusTest { @Test public void testMediaBackupExemption_lateConstraint() { final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) - .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0)) + .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0)) .setOverrideDeadline(12) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .build(); @@ -138,7 +140,7 @@ public class JobStatusTest { @Test public void testMediaBackupExemption_noConnectivityConstraint() { final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) - .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0)) + .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0)) .build(); when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE); assertEffectiveBucketForMediaExemption(createJobStatus(triggerContentJob), false); @@ -156,7 +158,7 @@ public class JobStatusTest { @Test public void testMediaBackupExemption_wrongSourcePackage() { final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) - .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0)) + .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .build(); when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn("not.test.package"); @@ -164,11 +166,12 @@ public class JobStatusTest { } @Test - public void testMediaBackupExemption_nonMediaUri() { - final Uri nonMediaUri = Uri.parse("content://not-media/any/path"); + public void testMediaBackupExemption_nonEligibleUri() { + final Uri nonEligibleUri = MediaStore.AUTHORITY_URI.buildUpon() + .appendPath("any_path").build(); final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) - .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0)) - .addTriggerContentUri(new JobInfo.TriggerContentUri(nonMediaUri, 0)) + .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0)) + .addTriggerContentUri(new JobInfo.TriggerContentUri(nonEligibleUri, 0)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .build(); when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE); @@ -177,12 +180,25 @@ public class JobStatusTest { @Test public void testMediaBackupExemptionGranted() { - final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) - .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0)) + when(mJobSchedulerInternal.getMediaBackupPackage()).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) .build(); - when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE); - assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), true); + assertEffectiveBucketForMediaExemption(createJobStatus(imageUriJob), true); + + final JobInfo videoUriJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) + .addTriggerContentUri(new JobInfo.TriggerContentUri(VIDEO_MEDIA_URI, 0)) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + .build(); + assertEffectiveBucketForMediaExemption(createJobStatus(videoUriJob), true); + + final JobInfo bothUriJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT) + .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0)) + .addTriggerContentUri(new JobInfo.TriggerContentUri(VIDEO_MEDIA_URI, 0)) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) + .build(); + assertEffectiveBucketForMediaExemption(createJobStatus(bothUriJob), true); } @Test diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 25d077823a3f..feae1e173f52 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -29,6 +29,12 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs; +import com.android.server.display.DisplayModeDirector.RefreshRateRange; +import com.android.server.display.DisplayModeDirector.Vote; + +import com.google.common.truth.Truth; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,6 +43,9 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidJUnit4.class) public class DisplayModeDirectorTest { + // The tolerance within which we consider something approximately equals. + private static final float FLOAT_TOLERANCE = 0.01f; + private Context mContext; @Before @@ -56,30 +65,22 @@ public class DisplayModeDirectorTest { modes[i - minFps] = new Display.Mode( /*modeId=*/i, /*width=*/1000, /*height=*/1000, /*refreshRate=*/i); } - SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<Display.Mode[]>(); + SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>(); supportedModesByDisplay.put(displayId, modes); director.injectSupportedModesByDisplay(supportedModesByDisplay); - SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<Display.Mode>(); + SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>(); defaultModesByDisplay.put(displayId, modes[0]); director.injectDefaultModeByDisplay(defaultModesByDisplay); return director; } - private int[] intRange(int min, int max) { - int[] range = new int[max - min + 1]; - for (int i = min; i <= max; i++) { - range[i - min] = i; - } - return range; - } - @Test public void testDisplayModeVoting() { int displayId = 0; // With no votes present, DisplayModeDirector should allow any refresh rate. - assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*baseModeId=*/60, - new DisplayModeDirector.RefreshRateRange(0f, Float.POSITIVE_INFINITY)), + assertEquals(new DesiredDisplayModeSpecs(/*baseModeId=*/60, + new RefreshRateRange(0f, Float.POSITIVE_INFINITY)), createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayModeSpecs( displayId)); @@ -93,20 +94,16 @@ public class DisplayModeDirectorTest { int maxFps = 90; DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); assertTrue(2 * numPriorities < maxFps - minFps + 1); - SparseArray<DisplayModeDirector.Vote> votes = - new SparseArray<DisplayModeDirector.Vote>(); - SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay = - new SparseArray<SparseArray<DisplayModeDirector.Vote>>(); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); for (int i = 0; i < numPriorities; i++) { - int priority = DisplayModeDirector.Vote.MIN_PRIORITY + i; - votes.put( - priority, DisplayModeDirector.Vote.forRefreshRates(minFps + i, maxFps - i)); + int priority = Vote.MIN_PRIORITY + i; + votes.put(priority, Vote.forRefreshRates(minFps + i, maxFps - i)); director.injectVotesByDisplay(votesByDisplay); - assertEquals( - new DisplayModeDirector.DesiredDisplayModeSpecs( + assertEquals(new DesiredDisplayModeSpecs( /*baseModeId=*/minFps + i, - new DisplayModeDirector.RefreshRateRange(minFps + i, maxFps - i)), + new RefreshRateRange(minFps + i, maxFps - i)), director.getDesiredDisplayModeSpecs(displayId)); } } @@ -116,19 +113,35 @@ public class DisplayModeDirectorTest { { assertTrue(numPriorities >= 2); DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); - SparseArray<DisplayModeDirector.Vote> votes = - new SparseArray<DisplayModeDirector.Vote>(); - SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay = - new SparseArray<SparseArray<DisplayModeDirector.Vote>>(); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); - votes.put(DisplayModeDirector.Vote.MAX_PRIORITY, - DisplayModeDirector.Vote.forRefreshRates(65, 85)); - votes.put(DisplayModeDirector.Vote.MIN_PRIORITY, - DisplayModeDirector.Vote.forRefreshRates(70, 80)); + votes.put(Vote.MAX_PRIORITY, Vote.forRefreshRates(65, 85)); + votes.put(Vote.MIN_PRIORITY, Vote.forRefreshRates(70, 80)); director.injectVotesByDisplay(votesByDisplay); - assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*baseModeId=*/70, - new DisplayModeDirector.RefreshRateRange(70, 80)), + assertEquals(new DesiredDisplayModeSpecs(/*baseModeId=*/70, + new RefreshRateRange(70, 80)), director.getDesiredDisplayModeSpecs(displayId)); } } + + @Test + public void testVotingWithFloatingPointErrors() { + int displayId = 0; + DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(50, 90); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(displayId, votes); + float error = FLOAT_TOLERANCE / 4; + votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(0, 60)); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forRefreshRates(60 + error, 60 + error)); + votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, + Vote.forRefreshRates(60 - error, 60 - error)); + director.injectVotesByDisplay(votesByDisplay); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); + + Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60); + } } diff --git a/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java b/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java index 6fafe113d90d..9b076e8edb52 100644 --- a/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java @@ -118,16 +118,6 @@ public class MockableLocationProviderTest { } @Test - public void testRequestSetAllowed() { - mProvider.requestSetAllowed(true); - verify(mRealProvider, times(1)).onRequestSetAllowed(true); - - mProvider.setMockProvider(mMockProvider); - mProvider.requestSetAllowed(true); - verify(mMockProvider, times(1)).onRequestSetAllowed(true); - } - - @Test public void testSendExtraCommand() { mProvider.sendExtraCommand(0, 0, "command", null); verify(mRealProvider, times(1)).onExtraCommand(0, 0, "command", null); diff --git a/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java b/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java index 762080fe5745..5943f67494f8 100644 --- a/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java +++ b/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java @@ -38,8 +38,5 @@ public class FakeProvider extends AbstractLocationProvider { protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {} @Override - protected void onRequestSetAllowed(boolean allowed) {} - - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {} } diff --git a/services/tests/servicestests/src/com/android/server/people/data/AggregateEventHistoryImplTest.java b/services/tests/servicestests/src/com/android/server/people/data/AggregateEventHistoryImplTest.java index b614a4f91d28..443718d94be6 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/AggregateEventHistoryImplTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/AggregateEventHistoryImplTest.java @@ -21,11 +21,16 @@ import static com.android.server.people.data.TestUtils.timestamp; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.content.Context; + +import androidx.test.InstrumentationRegistry; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.io.File; import java.util.List; @RunWith(JUnit4.class) @@ -60,11 +65,16 @@ public final class AggregateEventHistoryImplTest { EventHistoryImpl.Injector injector = new EventHistoryImplInjector(); - mEventHistory1 = new EventHistoryImpl(injector); + Context ctx = InstrumentationRegistry.getContext(); + File testDir = new File(ctx.getCacheDir(), "testdir"); + MockScheduledExecutorService mockScheduledExecutorService = + new MockScheduledExecutorService(); + + mEventHistory1 = new EventHistoryImpl(injector, testDir, mockScheduledExecutorService); mEventHistory1.addEvent(E1); mEventHistory1.addEvent(E2); - mEventHistory2 = new EventHistoryImpl(injector); + mEventHistory2 = new EventHistoryImpl(injector, testDir, mockScheduledExecutorService); mEventHistory2.addEvent(E3); mEventHistory2.addEvent(E4); } diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index f73a4b5776b6..b54317b768c5 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -100,6 +100,7 @@ public final class DataManagerTest { private static final int USER_ID_PRIMARY_MANAGED = 10; private static final int USER_ID_SECONDARY = 11; private static final String TEST_PKG_NAME = "pkg"; + private static final String TEST_CLASS_NAME = "class"; private static final String TEST_SHORTCUT_ID = "sc"; private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123"; private static final String PHONE_NUMBER = "+1234567890"; @@ -206,13 +207,13 @@ public final class DataManagerTest { mDataManager.onUserUnlocked(USER_ID_PRIMARY_MANAGED); mDataManager.onUserUnlocked(USER_ID_SECONDARY); - mDataManager.onShortcutAddedOrUpdated( + mDataManager.addOrUpdateConversationInfo( buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson(true, false))); - mDataManager.onShortcutAddedOrUpdated( + mDataManager.addOrUpdateConversationInfo( buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2", buildPerson(false, true))); - mDataManager.onShortcutAddedOrUpdated( + mDataManager.addOrUpdateConversationInfo( buildShortcutInfo("pkg_3", USER_ID_SECONDARY, "sc_3", buildPerson())); List<ConversationInfo> conversations = getConversationsInPrimary(); @@ -236,9 +237,9 @@ public final class DataManagerTest { @Test public void testAccessConversationForUnlockedUsersOnly() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); - mDataManager.onShortcutAddedOrUpdated( + mDataManager.addOrUpdateConversationInfo( buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson())); - mDataManager.onShortcutAddedOrUpdated( + mDataManager.addOrUpdateConversationInfo( buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2", buildPerson())); List<ConversationInfo> conversations = getConversationsInPrimary(); @@ -261,11 +262,12 @@ public final class DataManagerTest { } @Test - public void testReportAppTargetEvent() throws IntentFilter.MalformedMimeTypeException { + public void testReportAppTargetEvent_directSharing() + throws IntentFilter.MalformedMimeTypeException { mDataManager.onUserUnlocked(USER_ID_PRIMARY); ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut) .build(); @@ -274,7 +276,55 @@ public final class DataManagerTest { .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE) .build(); IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg"); - mDataManager.reportAppTargetEvent(appTargetEvent, intentFilter); + mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter); + + List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut( + Event.SHARE_EVENT_TYPES); + assertEquals(1, activeShareTimeSlots.size()); + } + + @Test + public void testReportAppTargetEvent_directSharing_createConversation() + throws IntentFilter.MalformedMimeTypeException { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + null); + AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut) + .build(); + AppTargetEvent appTargetEvent = + new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH) + .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE) + .build(); + IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg"); + + mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter); + + List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut( + Event.SHARE_EVENT_TYPES); + assertEquals(1, activeShareTimeSlots.size()); + ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY) + .getConversationStore() + .getConversation(TEST_SHORTCUT_ID); + assertNotNull(conversationInfo); + assertEquals(conversationInfo.getShortcutId(), TEST_SHORTCUT_ID); + } + + @Test + public void testReportAppTargetEvent_appSharing() + throws IntentFilter.MalformedMimeTypeException { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + AppTarget appTarget = new AppTarget.Builder( + new AppTargetId(TEST_SHORTCUT_ID), + TEST_PKG_NAME, + UserHandle.of(USER_ID_PRIMARY)) + .setClassName(TEST_CLASS_NAME) + .build(); + AppTargetEvent appTargetEvent = + new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH) + .build(); + IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg"); + + mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter); List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut( Event.SHARE_EVENT_TYPES); @@ -288,7 +338,7 @@ public final class DataManagerTest { ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); final String newPhoneNumber = "+1000000000"; mInjector.mContactsQueryHelper.mIsStarred = true; @@ -312,7 +362,7 @@ public final class DataManagerTest { ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); NotificationListenerService listenerService = mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY); @@ -330,7 +380,7 @@ public final class DataManagerTest { ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); NotificationListenerService listenerService = mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY); @@ -350,7 +400,7 @@ public final class DataManagerTest { ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); NotificationListenerService listenerService = mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY); @@ -375,7 +425,7 @@ public final class DataManagerTest { ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); NotificationListenerService listenerService = mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY); @@ -401,7 +451,7 @@ public final class DataManagerTest { ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); NotificationListenerService listenerService = mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY); @@ -430,7 +480,7 @@ public final class DataManagerTest { ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); ContentObserver contentObserver = mDataManager.getCallLogContentObserverForTesting(); contentObserver.onChange(false); @@ -453,7 +503,7 @@ public final class DataManagerTest { ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME); ContentObserver contentObserver = mDataManager.getMmsSmsContentObserverForTesting(); @@ -476,7 +526,7 @@ public final class DataManagerTest { ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)); PackageMonitor packageMonitor = mDataManager.getPackageMonitorForTesting(USER_ID_PRIMARY); @@ -493,7 +543,7 @@ public final class DataManagerTest { ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)); doAnswer(ans -> null).when(mPackageManagerInternal) @@ -508,7 +558,7 @@ public final class DataManagerTest { ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); long currentTimestamp = System.currentTimeMillis(); mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER, @@ -529,7 +579,7 @@ public final class DataManagerTest { ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); + mDataManager.addOrUpdateConversationInfo(shortcut); mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME); long currentTimestamp = System.currentTimeMillis(); diff --git a/services/tests/servicestests/src/com/android/server/people/data/EventHistoryImplTest.java b/services/tests/servicestests/src/com/android/server/people/data/EventHistoryImplTest.java index 43e1001f2aee..825ca1065e73 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/EventHistoryImplTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/EventHistoryImplTest.java @@ -21,18 +21,27 @@ import static com.android.server.people.data.TestUtils.timestamp; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.content.Context; +import android.os.FileUtils; +import android.text.format.DateUtils; + +import androidx.test.InstrumentationRegistry; + +import com.google.android.collect.Lists; import com.google.android.collect.Sets; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.io.File; import java.util.List; +import java.util.Map; @RunWith(JUnit4.class) public final class EventHistoryImplTest { - private static final long CURRENT_TIMESTAMP = timestamp("01-30 18:50"); private static final Event E1 = new Event(timestamp("01-06 05:26"), @@ -41,26 +50,48 @@ public final class EventHistoryImplTest { Event.TYPE_NOTIFICATION_OPENED); private static final Event E3 = new Event(timestamp("01-30 03:06"), Event.TYPE_SHARE_IMAGE); - private static final Event E4 = new Event(timestamp("01-30 18:14"), + private static final Event E4 = new Event(timestamp("01-30 16:14"), + Event.TYPE_SMS_INCOMING); + private static final Event E5 = new Event(timestamp("01-30 18:30"), Event.TYPE_SMS_INCOMING); + private static final EventIndex.Injector EVENT_INDEX_INJECTOR = new EventIndex.Injector() { + @Override + long currentTimeMillis() { + return CURRENT_TIMESTAMP; + } + }; + private static final EventHistoryImpl.Injector EVENT_HISTORY_INJECTOR = + new EventHistoryImpl.Injector() { + @Override + EventIndex createEventIndex() { + return new EventIndex(EVENT_INDEX_INJECTOR); + } + + @Override + long currentTimeMillis() { + return CURRENT_TIMESTAMP; + } + }; + private EventHistoryImpl mEventHistory; + private File mCacheDir; + private File mFile; + private MockScheduledExecutorService mMockScheduledExecutorService; @Before public void setUp() { - EventIndex.Injector eventIndexInjector = new EventIndex.Injector() { - @Override - long currentTimeMillis() { - return CURRENT_TIMESTAMP; - } - }; - EventHistoryImpl.Injector eventHistoryInjector = new EventHistoryImpl.Injector() { - @Override - EventIndex createEventIndex() { - return new EventIndex(eventIndexInjector); - } - }; - mEventHistory = new EventHistoryImpl(eventHistoryInjector); + Context ctx = InstrumentationRegistry.getContext(); + mCacheDir = ctx.getCacheDir(); + mFile = new File(mCacheDir, "testdir"); + mMockScheduledExecutorService = new MockScheduledExecutorService(); + mEventHistory = new EventHistoryImpl(EVENT_HISTORY_INJECTOR, mFile, + mMockScheduledExecutorService); + } + + @After + public void tearDown() { + FileUtils.deleteContentsAndDir(mFile); } @Test @@ -115,4 +146,105 @@ public final class EventHistoryImplTest { Sets.newArraySet(Event.TYPE_SHARE_IMAGE), 0L, Long.MAX_VALUE); assertEquals(1, events.size()); } + + @Test + public void testPersistenceAndRestoration() { + mEventHistory.addEvent(E1); + mEventHistory.addEvent(E2); + mEventHistory.addEvent(E3); + mEventHistory.addEvent(E4); + mEventHistory.addEvent(E5); + + // futures of events and event index flush. + long futuresExecuted = mMockScheduledExecutorService.fastForwardTime( + 3L * DateUtils.MINUTE_IN_MILLIS); + assertEquals(2, futuresExecuted); + + EventIndex indexBeforePowerOff = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES); + + resetAndLoadEventHistory(); + + List<Event> events = mEventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE); + assertEquals(2, events.size()); + assertTrue(events.containsAll(Lists.newArrayList(E4, E5))); + + EventIndex indexAfterPowerOff = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES); + assertEquals(indexBeforePowerOff, indexAfterPowerOff); + } + + @Test + public void testMimicDevicePowerOff() { + mEventHistory.addEvent(E1); + mEventHistory.addEvent(E2); + mEventHistory.addEvent(E3); + mEventHistory.addEvent(E4); + mEventHistory.addEvent(E5); + mEventHistory.saveToDisk(); + + EventIndex indexBeforePowerOff = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES); + + // Ensure that futures were cancelled and the immediate flush occurred. + assertEquals(0, mMockScheduledExecutorService.getFutures().size()); + + // Expect to see 2 executes from #saveToDisk, one for events and another for index. + assertEquals(2, mMockScheduledExecutorService.getExecutes().size()); + + resetAndLoadEventHistory(); + + List<Event> events = mEventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE); + assertEquals(2, events.size()); + assertTrue(events.containsAll(Lists.newArrayList(E4, E5))); + + EventIndex indexAfterPowerOff = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES); + assertEquals(indexBeforePowerOff, indexAfterPowerOff); + } + + @Test + public void testOnDestroy() { + mEventHistory.addEvent(E1); + mEventHistory.addEvent(E2); + mEventHistory.addEvent(E3); + mEventHistory.addEvent(E4); + mEventHistory.addEvent(E5); + mEventHistory.saveToDisk(); + + mEventHistory.onDestroy(); + + List<Event> events = mEventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE); + assertTrue(events.isEmpty()); + + EventIndex index = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES); + assertTrue(index.isEmpty()); + } + + @Test + public void testEventHistoriesImplFromDisk() { + mEventHistory.addEvent(E1); + mEventHistory.addEvent(E2); + mEventHistory.addEvent(E3); + mEventHistory.addEvent(E4); + mEventHistory.addEvent(E5); + mEventHistory.saveToDisk(); + + EventIndex indexBefore = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES); + + Map<String, EventHistoryImpl> map = EventHistoryImpl.eventHistoriesImplFromDisk( + EVENT_HISTORY_INJECTOR, mCacheDir, mMockScheduledExecutorService); + assertEquals(1, map.size()); + assertTrue(map.containsKey("testdir")); + + List<Event> events = map.get("testdir").queryEvents(Event.ALL_EVENT_TYPES, 0L, + Long.MAX_VALUE); + assertEquals(2, events.size()); + assertTrue(events.containsAll(Lists.newArrayList(E4, E5))); + + EventIndex indexAfter = map.get("testdir").getEventIndex(Event.ALL_EVENT_TYPES); + assertEquals(indexBefore, indexAfter); + } + + private void resetAndLoadEventHistory() { + mEventHistory = new EventHistoryImpl(EVENT_HISTORY_INJECTOR, mFile, + mMockScheduledExecutorService); + mEventHistory.loadFromDisk(); + } } diff --git a/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java b/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java index 8b8ba1247a4b..aecbc8d031e1 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java +++ b/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java @@ -54,8 +54,8 @@ class MockScheduledExecutorService implements ScheduledExecutorService { long totalExecuted = 0; for (MockScheduledFuture<?> future : futuresCopy) { if (future.getDelay() < mTimeElapsedMillis) { - future.getCommand().run(); - mExecutes.add(future.getCommand()); + future.getRunnable().run(); + mExecutes.add(future.getRunnable()); totalExecuted += 1; } else { mFutures.add(future); @@ -96,7 +96,8 @@ class MockScheduledExecutorService implements ScheduledExecutorService { @Override public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { - throw new UnsupportedOperationException(); + Preconditions.checkState(unit == TimeUnit.MILLISECONDS); + return new MockScheduledFuture<>(command, period, unit); } @Override @@ -132,7 +133,13 @@ class MockScheduledExecutorService implements ScheduledExecutorService { @Override public <T> Future<T> submit(Callable<T> task) { - throw new UnsupportedOperationException(); + MockScheduledFuture<T> future = new MockScheduledFuture<>(task, 0, TimeUnit.MILLISECONDS); + try { + future.getCallable().call(); + } catch (Exception e) { + e.printStackTrace(); + } + return future; } @Override @@ -141,11 +148,11 @@ class MockScheduledExecutorService implements ScheduledExecutorService { } @Override - public Future<?> submit(Runnable command) { - mExecutes.add(command); - MockScheduledFuture<?> future = new MockScheduledFuture<>(command, 0, + public Future<?> submit(Runnable runnable) { + mExecutes.add(runnable); + MockScheduledFuture<?> future = new MockScheduledFuture<>(runnable, 0, TimeUnit.MILLISECONDS); - future.getCommand().run(); + future.getRunnable().run(); return future; } @@ -181,12 +188,22 @@ class MockScheduledExecutorService implements ScheduledExecutorService { class MockScheduledFuture<V> implements ScheduledFuture<V> { - private final Runnable mCommand; + private final Runnable mRunnable; + private final Callable<V> mCallable; private final long mDelay; private boolean mCancelled = false; - MockScheduledFuture(Runnable command, long delay, TimeUnit timeUnit) { - mCommand = command; + MockScheduledFuture(Runnable runnable, long delay, TimeUnit timeUnit) { + this(runnable, null, delay); + } + + MockScheduledFuture(Callable<V> callable, long delay, TimeUnit timeUnit) { + this(null, callable, delay); + } + + private MockScheduledFuture(Runnable runnable, Callable<V> callable, long delay) { + mCallable = callable; + mRunnable = runnable; mDelay = delay; } @@ -194,8 +211,12 @@ class MockScheduledExecutorService implements ScheduledExecutorService { return mDelay; } - public Runnable getCommand() { - return mCommand; + public Runnable getRunnable() { + return mRunnable; + } + + public Callable<V> getCallable() { + return mCallable; } @Override diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java index d444466cdef5..418067fd14f8 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java @@ -16,6 +16,8 @@ package com.android.server.people.data; +import static com.android.server.people.data.TestUtils.timestamp; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -62,7 +64,8 @@ public final class UsageStatsQueryHelperTest { private static final LocusId LOCUS_ID_1 = new LocusId("locus_1"); private static final LocusId LOCUS_ID_2 = new LocusId("locus_2"); - @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal; + @Mock + private UsageStatsManagerInternal mUsageStatsManagerInternal; private TestPackageData mPackageData; private UsageStatsQueryHelper mHelper; @@ -233,7 +236,7 @@ public final class UsageStatsQueryHelperTest { private static class TestPackageData extends PackageData { private final TestConversationStore mConversationStore; - private final TestEventStore mEventStore = new TestEventStore(); + private final TestEventStore mEventStore; TestPackageData(@NonNull String packageName, @UserIdInt int userId, @NonNull Predicate<String> isDefaultDialerPredicate, @@ -244,6 +247,7 @@ public final class UsageStatsQueryHelperTest { scheduledExecutorService, rootDir, helper); mConversationStore = new TestConversationStore(rootDir, scheduledExecutorService, helper); + mEventStore = new TestEventStore(rootDir, scheduledExecutorService); } @Override @@ -261,8 +265,31 @@ public final class UsageStatsQueryHelperTest { private static class TestEventStore extends EventStore { - private final EventHistoryImpl mShortcutEventHistory = new TestEventHistoryImpl(); - private final EventHistoryImpl mLocusEventHistory = new TestEventHistoryImpl(); + private static final long CURRENT_TIMESTAMP = timestamp("01-30 18:50"); + private static final EventIndex.Injector EVENT_INDEX_INJECTOR = new EventIndex.Injector() { + @Override + long currentTimeMillis() { + return CURRENT_TIMESTAMP; + } + }; + private static final EventHistoryImpl.Injector EVENT_HISTORY_INJECTOR = + new EventHistoryImpl.Injector() { + @Override + EventIndex createEventIndex() { + return new EventIndex(EVENT_INDEX_INJECTOR); + } + }; + + private final EventHistoryImpl mShortcutEventHistory; + private final EventHistoryImpl mLocusEventHistory; + + TestEventStore(File rootDir, ScheduledExecutorService scheduledExecutorService) { + super(rootDir, scheduledExecutorService); + mShortcutEventHistory = new TestEventHistoryImpl(EVENT_HISTORY_INJECTOR, rootDir, + scheduledExecutorService); + mLocusEventHistory = new TestEventHistoryImpl(EVENT_HISTORY_INJECTOR, rootDir, + scheduledExecutorService); + } @Override @NonNull @@ -280,6 +307,11 @@ public final class UsageStatsQueryHelperTest { private final List<Event> mEvents = new ArrayList<>(); + TestEventHistoryImpl(Injector injector, File rootDir, + ScheduledExecutorService scheduledExecutorService) { + super(injector, rootDir, scheduledExecutorService); + } + @Override @NonNull public List<Event> queryEvents(Set<Integer> eventTypes, long startTime, long endTime) { diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java index f498a9450c9e..c6cd34732acf 100644 --- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java +++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java @@ -16,16 +16,19 @@ package com.android.server.people.prediction; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.prediction.AppPredictionContext; +import android.app.prediction.AppTarget; +import android.app.prediction.AppTargetId; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -33,22 +36,26 @@ import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager.ShareShortcutInfo; import android.os.Bundle; import android.os.UserHandle; +import android.util.Range; import com.android.server.people.data.ConversationInfo; import com.android.server.people.data.DataManager; import com.android.server.people.data.EventHistory; +import com.android.server.people.data.EventIndex; import com.android.server.people.data.PackageData; -import com.android.server.people.prediction.ShareTargetPredictor.ShareTarget; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; @RunWith(JUnit4.class) public final class ShareTargetPredictorTest { @@ -57,17 +64,32 @@ public final class ShareTargetPredictorTest { private static final int NUM_PREDICTED_TARGETS = 5; private static final int USER_ID = 0; private static final String PACKAGE_1 = "pkg1"; - private static final String CLASS_1 = "cls1"; private static final String PACKAGE_2 = "pkg2"; + private static final String PACKAGE_3 = "pkg3"; + private static final String CLASS_1 = "cls1"; private static final String CLASS_2 = "cls2"; @Mock private Context mContext; @Mock private DataManager mDataManager; + @Mock private Consumer<List<AppTarget>> mUpdatePredictionsMethod; @Mock private PackageData mPackageData1; @Mock private PackageData mPackageData2; + @Mock private EventHistory mEventHistory1; + @Mock private EventHistory mEventHistory2; + @Mock private EventHistory mEventHistory3; + @Mock private EventHistory mEventHistory4; + @Mock private EventHistory mEventHistory5; + @Mock private EventHistory mEventHistory6; - private List<ShareShortcutInfo> mShareShortcuts = new ArrayList<>(); + @Mock private EventIndex mEventIndex1; + @Mock private EventIndex mEventIndex2; + @Mock private EventIndex mEventIndex3; + @Mock private EventIndex mEventIndex4; + @Mock private EventIndex mEventIndex5; + @Mock private EventIndex mEventIndex6; + @Captor private ArgumentCaptor<List<AppTarget>> mAppTargetCaptor; + private List<ShareShortcutInfo> mShareShortcuts = new ArrayList<>(); private ShareTargetPredictor mPredictor; @Before @@ -84,11 +106,11 @@ public final class ShareTargetPredictorTest { .setExtras(new Bundle()) .build(); mPredictor = new ShareTargetPredictor( - predictionContext, targets -> { }, mDataManager, USER_ID); + predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID); } @Test - public void testGetShareTargets() { + public void testPredictTargets() { mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1")); mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2")); mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3")); @@ -99,24 +121,148 @@ public final class ShareTargetPredictorTest { when(mPackageData2.getConversationInfo("sc3")).thenReturn(mock(ConversationInfo.class)); // "sc4" does not have a ConversationInfo. - when(mPackageData1.getEventHistory(anyString())).thenReturn(mock(EventHistory.class)); - when(mPackageData2.getEventHistory(anyString())).thenReturn(mock(EventHistory.class)); + when(mPackageData1.getEventHistory("sc1")).thenReturn(mEventHistory1); + when(mPackageData1.getEventHistory("sc2")).thenReturn(mEventHistory2); + when(mPackageData2.getEventHistory("sc3")).thenReturn(mEventHistory3); + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L)); + when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L)); + when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L)); - List<ShareTarget> shareTargets = mPredictor.getShareTargets(); + mPredictor.predictTargets(); - assertEquals(4, shareTargets.size()); + verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture()); + List<AppTarget> res = mAppTargetCaptor.getValue(); + assertEquals(4, res.size()); + + assertEquals("sc3", res.get(0).getId().getId()); + assertEquals(CLASS_2, res.get(0).getClassName()); + assertEquals(PACKAGE_2, res.get(0).getPackageName()); + + assertEquals("sc2", res.get(1).getId().getId()); + assertEquals(CLASS_1, res.get(1).getClassName()); + assertEquals(PACKAGE_1, res.get(1).getPackageName()); + + assertEquals("sc1", res.get(2).getId().getId()); + assertEquals(CLASS_1, res.get(2).getClassName()); + assertEquals(PACKAGE_1, res.get(2).getPackageName()); + + assertEquals("sc4", res.get(3).getId().getId()); + assertEquals(CLASS_2, res.get(3).getClassName()); + assertEquals(PACKAGE_2, res.get(3).getPackageName()); + } + + @Test + public void testPredictTargets_reachTargetsLimit() { + mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1")); + mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2")); + mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3")); + mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4")); + mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc5")); + mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc6")); + + when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class)); + when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class)); + when(mPackageData2.getConversationInfo("sc3")).thenReturn(mock(ConversationInfo.class)); + when(mPackageData2.getConversationInfo("sc4")).thenReturn(mock(ConversationInfo.class)); + when(mPackageData1.getConversationInfo("sc5")).thenReturn(mock(ConversationInfo.class)); + when(mPackageData2.getConversationInfo("sc6")).thenReturn(mock(ConversationInfo.class)); + + when(mPackageData1.getEventHistory("sc1")).thenReturn(mEventHistory1); + when(mPackageData1.getEventHistory("sc2")).thenReturn(mEventHistory2); + when(mPackageData2.getEventHistory("sc3")).thenReturn(mEventHistory3); + when(mPackageData2.getEventHistory("sc4")).thenReturn(mEventHistory4); + when(mPackageData1.getEventHistory("sc5")).thenReturn(mEventHistory5); + when(mPackageData2.getEventHistory("sc6")).thenReturn(mEventHistory6); + + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + when(mEventHistory6.getEventIndex(anySet())).thenReturn(mEventIndex6); + when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L)); + when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L)); + when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L)); + when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(4L, 5L)); + when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(5L, 6L)); + when(mEventIndex6.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(6L, 7L)); + + mPredictor.predictTargets(); + + verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture()); + List<AppTarget> res = mAppTargetCaptor.getValue(); + assertEquals(5, res.size()); + + assertEquals("sc6", res.get(0).getId().getId()); + assertEquals(CLASS_2, res.get(0).getClassName()); + assertEquals(PACKAGE_2, res.get(0).getPackageName()); + + assertEquals("sc5", res.get(1).getId().getId()); + assertEquals(CLASS_1, res.get(1).getClassName()); + assertEquals(PACKAGE_1, res.get(1).getPackageName()); + + assertEquals("sc4", res.get(2).getId().getId()); + assertEquals(CLASS_2, res.get(2).getClassName()); + assertEquals(PACKAGE_2, res.get(2).getPackageName()); + + assertEquals("sc3", res.get(3).getId().getId()); + assertEquals(CLASS_2, res.get(3).getClassName()); + assertEquals(PACKAGE_2, res.get(3).getPackageName()); + + assertEquals("sc2", res.get(4).getId().getId()); + assertEquals(CLASS_1, res.get(4).getClassName()); + assertEquals(PACKAGE_1, res.get(4).getPackageName()); + } + + @Test + public void testSortTargets() { + AppTarget appTarget1 = new AppTarget.Builder( + new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)) + .setClassName(CLASS_1) + .build(); + AppTarget appTarget2 = new AppTarget.Builder( + new AppTargetId("cls2#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)) + .setClassName(CLASS_2) + .build(); + AppTarget appTarget3 = new AppTarget.Builder( + new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) + .setClassName(CLASS_1) + .build(); + AppTarget appTarget4 = new AppTarget.Builder( + new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) + .setClassName(CLASS_2) + .build(); + AppTarget appTarget5 = new AppTarget.Builder( + new AppTargetId("cls1#pkg3"), PACKAGE_3, UserHandle.of(USER_ID)) + .setClassName(CLASS_1) + .build(); - assertEquals("sc1", shareTargets.get(0).getShareShortcutInfo().getShortcutInfo().getId()); - assertNotNull(shareTargets.get(0).getConversationData()); + when(mPackageData1.getClassLevelEventHistory(CLASS_1)).thenReturn(mEventHistory1); + when(mPackageData1.getClassLevelEventHistory(CLASS_2)).thenReturn(mEventHistory2); + when(mPackageData2.getClassLevelEventHistory(CLASS_1)).thenReturn(mEventHistory3); + when(mPackageData2.getClassLevelEventHistory(CLASS_2)).thenReturn(mEventHistory4); + // PackageData of PACKAGE_3 is empty. + when(mDataManager.getPackage(PACKAGE_3, USER_ID)).thenReturn(null); - assertEquals("sc2", shareTargets.get(1).getShareShortcutInfo().getShortcutInfo().getId()); - assertNotNull(shareTargets.get(1).getConversationData()); + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L)); + when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L)); + when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L)); + when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(4L, 5L)); - assertEquals("sc3", shareTargets.get(2).getShareShortcutInfo().getShortcutInfo().getId()); - assertNotNull(shareTargets.get(2).getConversationData()); + mPredictor.sortTargets( + List.of(appTarget1, appTarget2, appTarget3, appTarget4, appTarget5), + mUpdatePredictionsMethod); - assertEquals("sc4", shareTargets.get(3).getShareShortcutInfo().getShortcutInfo().getId()); - assertNull(shareTargets.get(3).getConversationData()); + verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture()); + assertThat(mAppTargetCaptor.getValue()).containsExactly( + appTarget4, appTarget3, appTarget2, appTarget1, appTarget5); } private ShareShortcutInfo buildShareShortcut( diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index 2936bdd8501b..56460fb6f0a0 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -379,6 +379,113 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } + public void testPushDynamicShortcut() { + + setCaller(CALLING_PACKAGE_1, USER_0); + + final ShortcutInfo s1 = makeShortcut("s1"); + final ShortcutInfo s2 = makeShortcut("s2"); + final ShortcutInfo s3 = makeShortcut("s3"); + final ShortcutInfo s4 = makeShortcut("s4"); + + final ShortcutInfo s10 = makeShortcut("s10"); + final ShortcutInfo s11 = makeShortcut("s11"); + final ShortcutInfo s12 = makeShortcut("s12"); + final ShortcutInfo s13 = makeShortcut("s13"); + final ShortcutInfo s14 = makeShortcut("s14"); + + // Test push as first shortcut + mManager.pushDynamicShortcut(s1); + assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), "s1"); + assertEquals(0, getCallerShortcut("s1").getRank()); + + // Test push when other shortcuts exist + assertTrue(mManager.setDynamicShortcuts(list(s1, s2))); + assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), "s1", "s2"); + mManager.pushDynamicShortcut(s3); + assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), + "s1", "s2", "s3"); + assertEquals(0, getCallerShortcut("s3").getRank()); + assertEquals(1, getCallerShortcut("s1").getRank()); + assertEquals(2, getCallerShortcut("s2").getRank()); + + mInjectedCurrentTimeMillis += INTERVAL; // reset + + // Push with set rank + s4.setRank(2); + mManager.pushDynamicShortcut(s4); + assertEquals(2, getCallerShortcut("s4").getRank()); + assertEquals(3, getCallerShortcut("s2").getRank()); + + // Push existing shortcut with set rank + final ShortcutInfo s4_2 = makeShortcut("s4"); + s4_2.setRank(4); + mManager.pushDynamicShortcut(s4_2); + assertEquals(2, getCallerShortcut("s2").getRank()); + assertEquals(3, getCallerShortcut("s4").getRank()); + + mInjectedCurrentTimeMillis += INTERVAL; // reset + + // Test push as last + assertTrue(mManager.addDynamicShortcuts(makeShortcuts("s5", "s6", "s7", "s8", "s9"))); + mManager.pushDynamicShortcut(s10); + assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), + "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10"); + assertEquals(0, getCallerShortcut("s10").getRank()); + assertEquals(1, getCallerShortcut("s5").getRank()); + assertEquals(6, getCallerShortcut("s3").getRank()); + assertEquals(7, getCallerShortcut("s1").getRank()); + assertEquals(8, getCallerShortcut("s2").getRank()); + assertEquals(9, getCallerShortcut("s4").getRank()); + + // Push when max has already reached + mManager.pushDynamicShortcut(s11); + assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), + "s1", "s2", "s3", "s5", "s6", "s7", "s8", "s9", "s10", "s11"); + assertEquals(0, getCallerShortcut("s11").getRank()); + assertEquals(1, getCallerShortcut("s10").getRank()); + assertEquals(9, getCallerShortcut("s2").getRank()); + + mInjectedCurrentTimeMillis += INTERVAL; // reset + + // Push with different activity + s12.setActivity(makeComponent(ShortcutActivity2.class)); + mManager.pushDynamicShortcut(s12); + assertEquals(makeComponent(ShortcutActivity2.class), + getCallerShortcut("s12").getActivity()); + assertEquals(0, getCallerShortcut("s12").getRank()); + + // Push to update shortcut with different activity + final ShortcutInfo s1_2 = makeShortcut("s1"); + s1_2.setActivity(makeComponent(ShortcutActivity2.class)); + s1_2.setRank(1); + mManager.pushDynamicShortcut(s1_2); + assertEquals(0, getCallerShortcut("s12").getRank()); + assertEquals(1, getCallerShortcut("s1").getRank()); + assertEquals(0, getCallerShortcut("s11").getRank()); + assertEquals(1, getCallerShortcut("s10").getRank()); + assertEquals(7, getCallerShortcut("s3").getRank()); + assertEquals(8, getCallerShortcut("s2").getRank()); + + mInjectedCurrentTimeMillis += INTERVAL; // reset + + // Test push when dropped shortcut is cached + s13.setLongLived(); + s13.setRank(100); + mManager.pushDynamicShortcut(s13); + assertEquals(9, getCallerShortcut("s13").getRank()); + runWithCaller(LAUNCHER_1, USER_0, () -> { + mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s13"), HANDLE_USER_0); + }); + + mManager.pushDynamicShortcut(s14); + assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), + "s1", "s2", "s3", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "s12", "s14"); + // Verify s13 stayed as cached + assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), + "s13"); + } + public void testUnlimitedCalls() { setCaller(CALLING_PACKAGE_1, USER_0); diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 9e577636c1b3..0fdffd554b36 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -66,6 +66,7 @@ import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; +import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.util.ArraySet; import android.view.Display; @@ -83,6 +84,7 @@ import org.junit.runner.RunWith; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -101,6 +103,8 @@ public class AppStandbyControllerTests { private static final int UID_EXEMPTED_1 = 10001; private static final int USER_ID = 0; private static final int USER_ID2 = 10; + private static final UserHandle USER_HANDLE_USER2 = new UserHandle(USER_ID2); + private static final int USER_ID3 = 11; private static final String PACKAGE_UNKNOWN = "com.example.unknown"; @@ -150,6 +154,8 @@ public class AppStandbyControllerTests { boolean mDisplayOn; DisplayManager.DisplayListener mDisplayListener; String mBoundWidgetPackage = PACKAGE_EXEMPTED_1; + int[] mRunningUsers = new int[] {USER_ID}; + List<UserHandle> mCrossProfileTargets = Collections.emptyList(); MyInjector(Context context, Looper looper) { super(context, looper); @@ -212,7 +218,7 @@ public class AppStandbyControllerTests { @Override int[] getRunningUserIds() { - return new int[] {USER_ID}; + return mRunningUsers; } @Override @@ -248,6 +254,11 @@ public class AppStandbyControllerTests { return false; } + @Override + public List<UserHandle> getValidCrossProfileTargets(String pkg, int userId) { + return mCrossProfileTargets; + } + // Internal methods void setDisplayOn(boolean on) { @@ -379,10 +390,15 @@ public class AppStandbyControllerTests { } private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) { + assertTimeout(controller, elapsedTime, bucket, USER_ID); + } + + private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket, + int userId) { mInjector.mElapsedRealtime = elapsedTime; - controller.checkIdleStates(USER_ID); + controller.checkIdleStates(userId); assertEquals(bucket, - controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime, + controller.getAppStandbyBucket(PACKAGE_1, userId, mInjector.mElapsedRealtime, false)); } @@ -397,7 +413,11 @@ public class AppStandbyControllerTests { } private int getStandbyBucket(AppStandbyController controller, String packageName) { - return controller.getAppStandbyBucket(packageName, USER_ID, mInjector.mElapsedRealtime, + return getStandbyBucket(USER_ID, controller, packageName); + } + + private int getStandbyBucket(int userId, AppStandbyController controller, String packageName) { + return controller.getAppStandbyBucket(packageName, userId, mInjector.mElapsedRealtime, true); } @@ -1012,6 +1032,29 @@ public class AppStandbyControllerTests { assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID); } + @Test + public void testUserInteraction_CrossProfile() throws Exception { + mInjector.mRunningUsers = new int[] {USER_ID, USER_ID2, USER_ID3}; + mInjector.mCrossProfileTargets = Arrays.asList(USER_HANDLE_USER2); + reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); + assertEquals("Cross profile connected package bucket should be elevated on usage", + STANDBY_BUCKET_ACTIVE, getStandbyBucket(USER_ID2, mController, PACKAGE_1)); + assertEquals("Not Cross profile connected package bucket should not be elevated on usage", + STANDBY_BUCKET_NEVER, getStandbyBucket(USER_ID3, mController, PACKAGE_1)); + + assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE, USER_ID); + assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE, USER_ID2); + + assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET, USER_ID); + assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET, USER_ID2); + + mInjector.mCrossProfileTargets = Collections.emptyList(); + reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); + assertEquals("No longer cross profile connected package bucket should not be " + + "elevated on usage", + STANDBY_BUCKET_WORKING_SET, getStandbyBucket(USER_ID2, mController, PACKAGE_1)); + } + private String getAdminAppsStr(int userId) { return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId)); } diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 3f0cda3b8e5a..b7199f7cdd5a 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -29,6 +29,7 @@ android_test { libs: [ "android.test.runner", "android.test.base", + "android.test.mock", ], dxflags: ["--multi-dex"], 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 7f9732bb350a..58299614efe8 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -36,9 +36,10 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -67,6 +68,7 @@ import android.provider.Settings; import android.provider.Settings.Global; import android.provider.Settings.Secure; import android.service.notification.ConversationChannelWrapper; +import android.test.mock.MockIContentProvider; import android.test.suitebuilder.annotation.SmallTest; import android.testing.TestableContentResolver; import android.util.ArrayMap; @@ -87,6 +89,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; @@ -123,7 +126,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Mock NotificationUsageStats mUsageStats; @Mock RankingHandler mHandler; @Mock PackageManager mPm; - @Mock IContentProvider mTestIContentProvider; + @Spy IContentProvider mTestIContentProvider = new MockIContentProvider(); @Mock Context mContext; @Mock ZenModeHelper mMockZenModeHelper; @@ -170,12 +173,12 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider); contentResolver.addProvider(TEST_AUTHORITY, testContentProvider); - when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI))) - .thenReturn(CANONICAL_SOUND_URI); - when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) - .thenReturn(CANONICAL_SOUND_URI); - when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) - .thenReturn(SOUND_URI); + doReturn(CANONICAL_SOUND_URI) + .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI)); + doReturn(CANONICAL_SOUND_URI) + .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); + doReturn(SOUND_URI) + .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0); @@ -506,12 +509,13 @@ public class PreferencesHelperTest extends UiServiceTestCase { .appendQueryParameter("title", "Test") .appendQueryParameter("canonical", "1") .build(); - when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) - .thenReturn(canonicalBasedOnLocal); - when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) - .thenReturn(localUri); - when(mTestIContentProvider.uncanonicalize(any(), any(), eq(canonicalBasedOnLocal))) - .thenReturn(localUri); + doReturn(canonicalBasedOnLocal) + .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); + doReturn(localUri) + .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); + doReturn(localUri) + .when(mTestIContentProvider).uncanonicalize(any(), any(), + eq(canonicalBasedOnLocal)); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); @@ -530,10 +534,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception { Thread.sleep(3000); - when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) - .thenReturn(null); - when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI))) - .thenReturn(null); + doReturn(null) + .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); + doReturn(null) + .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); @@ -557,7 +561,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception { // Not a local uncanonicalized uri, simulating that it fails to exist locally - when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI))).thenReturn(null); + doReturn(null) + .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI)); String id = "id"; String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n" diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index a0ea7290ec01..c9c3649783b8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1025,9 +1025,9 @@ public class ActivityRecordTests extends ActivityTestsBase { public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() { // Empty the home stack. final ActivityStack homeStack = mActivity.getDisplay().getRootHomeTask(); - homeStack.forAllTasks((t) -> { + homeStack.forAllLeafTasks((t) -> { homeStack.removeChild(t, "test"); - }, true /* traverseTopToBottom */, homeStack); + }, true /* traverseTopToBottom */); mActivity.finishing = true; doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities(); spyOn(mStack); @@ -1051,9 +1051,9 @@ public class ActivityRecordTests extends ActivityTestsBase { public void testCompleteFinishing_lastActivityAboveEmptyHomeStack() { // Empty the home stack. final ActivityStack homeStack = mActivity.getDisplay().getRootHomeTask(); - homeStack.forAllTasks((t) -> { + homeStack.forAllLeafTasks((t) -> { homeStack.removeChild(t, "test"); - }, true /* traverseTopToBottom */, homeStack); + }, true /* traverseTopToBottom */); mActivity.finishing = true; spyOn(mStack); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 1a8f2a6b65f5..b3c6b22bf265 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -27,6 +27,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; import static android.view.DisplayCutout.fromBoundingRect; +import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_90; import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; @@ -998,12 +999,10 @@ public class DisplayContentTests extends WindowTestsBase { public void testApplyTopFixedRotationTransform() { mWm.mIsFixedRotationTransformEnabled = true; final Configuration config90 = new Configuration(); - mDisplayContent.getDisplayRotation().setRotation(ROTATION_90); - mDisplayContent.computeScreenConfiguration(config90); - mDisplayContent.onRequestedOverrideConfigurationChanged(config90); + mDisplayContent.computeScreenConfiguration(config90, ROTATION_90); final Configuration config = new Configuration(); - mDisplayContent.getDisplayRotation().setRotation(Surface.ROTATION_0); + mDisplayContent.getDisplayRotation().setRotation(ROTATION_0); mDisplayContent.computeScreenConfiguration(config); mDisplayContent.onRequestedOverrideConfigurationChanged(config); @@ -1014,11 +1013,18 @@ public class DisplayContentTests extends WindowTestsBase { app.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); assertTrue(app.isFixedRotationTransforming()); + assertTrue(mDisplayContent.getDisplayRotation().shouldRotateSeamlessly( + ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */, + false /* forceUpdate */)); + // The display should keep current orientation and the rotated configuration should apply + // to the activity. assertEquals(config.orientation, mDisplayContent.getConfiguration().orientation); assertEquals(config90.orientation, app.getConfiguration().orientation); + assertEquals(config90.windowConfiguration.getBounds(), app.getBounds()); mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token); + // The display should be rotated after the launch is finished. assertFalse(app.hasFixedRotationTransform()); assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index ba577454f9d5..2b0ad890aae1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -605,6 +605,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + mWindow.mAttrs.setFitInsetsTypes(0 /* types */); addWindow(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); @@ -625,6 +626,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; addWindow(mWindow); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index dd466731389e..ec2026255f8e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -313,8 +313,8 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ @Test public void testResizeDockedStackForSplitScreenPrimary() { - final Rect taskSize = new Rect(0, 0, 1000, 1000); - final Rect stackSize = new Rect(0, 0, 300, 300); + final Rect configSize = new Rect(0, 0, 1000, 1000); + final Rect displayedSize = new Rect(0, 0, 300, 300); // Create primary split-screen stack with a task. final ActivityStack primaryStack = new StackBuilder(mRootWindowContainer) @@ -325,11 +325,13 @@ public class RootActivityContainerTests extends ActivityTestsBase { final Task task = primaryStack.getTopMostTask(); // Resize dock stack. - mService.resizeDockedStack(stackSize, taskSize, null, null, null); + mService.resizeDockedStack(displayedSize, configSize, null, null, null); // Verify dock stack & its task bounds if is equal as resized result. - assertEquals(stackSize, primaryStack.getBounds()); - assertEquals(taskSize, task.getBounds()); + assertEquals(displayedSize, primaryStack.getDisplayedBounds()); + assertEquals(displayedSize, primaryStack.getDisplayedBounds()); + assertEquals(configSize, primaryStack.getBounds()); + assertEquals(configSize, task.getBounds()); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java index cd53eced948f..45b51cf9d2db 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -28,6 +28,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; @@ -300,6 +301,7 @@ public class TaskOrganizerTests extends WindowTestsBase { Rect newSize = new Rect(10, 10, 300, 300); Configuration c = new Configuration(tile1.getRequestedOverrideConfiguration()); c.windowConfiguration.setBounds(newSize); + doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any()); tile1.onRequestedOverrideConfigurationChanged(c); assertEquals(newSize, stack.getBounds()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java index 6e4be88a31fe..6387a3b7c474 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java @@ -16,9 +16,11 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; @@ -33,6 +35,7 @@ import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.clearInvocations; +import android.app.WindowConfiguration; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -184,6 +187,16 @@ public class TaskStackTests extends WindowTestsBase { doReturn(stackOutset).when(stack).getStackOutset(); doReturn(true).when(stack).inMultiWindowMode(); + // Mock the resolved override windowing mode to non-fullscreen + final WindowConfiguration windowConfiguration = + stack.getResolvedOverrideConfiguration().windowConfiguration; + spyOn(windowConfiguration); + doReturn(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) + .when(windowConfiguration).getWindowingMode(); + + // Prevent adjust task dimensions + doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any()); + final Rect stackBounds = new Rect(200, 200, 800, 1000); // Update surface position and size by the given bounds. stack.setBounds(stackBounds); diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java index 18b640ff6bf5..c3d3d83f02fa 100644 --- a/services/usage/java/com/android/server/usage/StorageStatsService.java +++ b/services/usage/java/com/android/server/usage/StorageStatsService.java @@ -86,6 +86,7 @@ import java.util.function.Consumer; public class StorageStatsService extends IStorageStatsManager.Stub { private static final String TAG = "StorageStatsService"; + private static final String PROP_STORAGE_CRATES = "fw.storage_crates"; private static final String PROP_DISABLE_QUOTA = "fw.disable_quota"; private static final String PROP_VERIFY_STORAGE = "fw.verify_storage"; @@ -595,6 +596,13 @@ public class StorageStatsService extends IStorageStatsManager.Stub { Uri.parse("content://com.android.externalstorage.documents/"), null, false); } + private static void checkCratesEnable() { + final boolean enable = SystemProperties.getBoolean(PROP_STORAGE_CRATES, false); + if (!enable) { + throw new IllegalStateException("Storage Crate feature is disabled."); + } + } + /** * To enforce the calling or self to have the {@link android.Manifest.permission#MANAGE_CRATES} * permission. @@ -650,6 +658,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { @Override public ParceledListSlice<CrateInfo> queryCratesForPackage(String volumeUuid, @NonNull String packageName, @UserIdInt int userId, @NonNull String callingPackage) { + checkCratesEnable(); if (userId != UserHandle.getCallingUserId()) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS, TAG); @@ -677,6 +686,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { @Override public ParceledListSlice<CrateInfo> queryCratesForUid(String volumeUuid, int uid, @NonNull String callingPackage) { + checkCratesEnable(); final int userId = UserHandle.getUserId(uid); if (userId != UserHandle.getCallingUserId()) { mContext.enforceCallingOrSelfPermission( @@ -718,6 +728,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub { @Override public ParceledListSlice<CrateInfo> queryCratesForUser(String volumeUuid, int userId, @NonNull String callingPackage) { + checkCratesEnable(); if (userId != UserHandle.getCallingUserId()) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS, TAG); diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 3564add65113..4604cd2e2e75 100755 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -3576,7 +3576,7 @@ public abstract class Connection extends Conferenceable { * ATIS-1000082. * @return the verification status. */ - public @VerificationStatus int getCallerNumberVerificationStatus() { + public final @VerificationStatus int getCallerNumberVerificationStatus() { return mCallerNumberVerificationStatus; } @@ -3588,7 +3588,7 @@ public abstract class Connection extends Conferenceable { * by * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}. */ - public void setCallerNumberVerificationStatus( + public final void setCallerNumberVerificationStatus( @VerificationStatus int callerNumberVerificationStatus) { mCallerNumberVerificationStatus = callerNumberVerificationStatus; } diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java index a27c4802c306..9ae86c8c586b 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -432,7 +432,7 @@ public class Annotation { DataFailCause.LIMITED_TO_IPV6, DataFailCause.VSNCP_TIMEOUT, DataFailCause.VSNCP_GEN_ERROR, - DataFailCause.VSNCP_APN_UNATHORIZED, + DataFailCause.VSNCP_APN_UNAUTHORIZED, DataFailCause.VSNCP_PDN_LIMIT_EXCEEDED, DataFailCause.VSNCP_NO_PDN_GATEWAY_ADDRESS, DataFailCause.VSNCP_PDN_GATEWAY_UNREACHABLE, diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 5c6ca4574397..a7e52ea21758 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -36,6 +36,8 @@ import android.telephony.ims.ImsReasonInfo; import com.android.internal.telephony.ICarrierConfigLoader; import com.android.telephony.Rlog; +import java.util.concurrent.TimeUnit; + /** * Provides access to telephony configuration values that are carrier-specific. */ @@ -2479,6 +2481,21 @@ public class CarrierConfigManager { "parameters_use_for_5g_nr_signal_bar_int"; /** + * String array of default bandwidth values per network type. + * The entries should be of form "network_name:downstream,upstream", with values in Kbps. + * @hide + */ + public static final String KEY_BANDWIDTH_STRING_ARRAY = "bandwidth_string_array"; + + /** + * For NR (non-standalone), whether to use the LTE value instead of NR value as the default for + * upstream bandwidth. Downstream bandwidth will still use the NR value as the default. + * @hide + */ + public static final String KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPSTREAM_BOOL = + "bandwidth_nr_nsa_use_lte_value_for_upstream_bool"; + + /** * Key identifying if voice call barring notification is required to be shown to the user. * @hide */ @@ -2980,6 +2997,33 @@ public class CarrierConfigManager { "5g_icon_display_grace_period_sec_int"; /** + * Controls time in milliseconds until DcTracker reevaluates 5G connection state. + */ + public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_long"; + + /** + * Whether NR (non-standalone) should be unmetered for all frequencies. + * If either {@link #KEY_UNMETERED_NR_NSA_MMWAVE_BOOL} or + * {@link #KEY_UNMETERED_NR_NSA_SUB6_BOOL} are true, then this value will be ignored. + * @hide + */ + public static final String KEY_UNMETERED_NR_NSA_BOOL = "unmetered_nr_nsa_bool"; + + /** + * Whether NR (non-standalone) frequencies above 6GHz (millimeter wave) should be unmetered. + * If this is true, then the value for {@link #KEY_UNMETERED_NR_NSA_BOOL} will be ignored. + * @hide + */ + public static final String KEY_UNMETERED_NR_NSA_MMWAVE_BOOL = "unmetered_nr_nsa_mmwave_bool"; + + /** + * Whether NR (non-standalone) frequencies below 6GHz (sub6) should be unmetered. + * If this is true, then the value for {@link #KEY_UNMETERED_NR_NSA_BOOL} will be ignored. + * @hide + */ + public static final String KEY_UNMETERED_NR_NSA_SUB6_BOOL = "unmetered_nr_nsa_sub6_bool"; + + /** * Support ASCII 7-BIT encoding for long SMS. This carrier config is used to enable * this feature. * @hide @@ -3051,11 +3095,6 @@ public class CarrierConfigManager { "ping_test_before_data_switch_bool"; /** - * Controls time in milliseconds until DcTracker reevaluates 5G connection state. - */ - public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = - "5g_watchdog_time_long"; - /** * Controls whether to switch data to primary from opportunistic subscription * if primary is out of service. This control only affects system or 1st party app * initiated data switch, but will not override data switch initiated by privileged carrier apps @@ -3931,6 +3970,13 @@ public class CarrierConfigManager { }); sDefaults.putInt(KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, CellSignalStrengthNr.USE_SSRSRP); + sDefaults.putStringArray(KEY_BANDWIDTH_STRING_ARRAY, new String[]{ + "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA-IS95A:14,14", "CDMA-IS95B:14,14", + "1xRTT:30,30", "EvDo-rev.0:750,48", "EvDo-rev.A:950,550", "HSDPA:4300,620", + "HSUPA:4300,1800", "HSPA:4300,1800", "EvDo-rev.B:1500,550:", "eHRPD:750,48", + "HSPAP:13000,3400", "TD-SCDMA:115,115", "LTE:30000,15000", "NR_NSA:47000,15000", + "NR_NSA_MMWAVE:145000,15000", "NR_SA:145000,15000"}); + sDefaults.putBoolean(KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPSTREAM_BOOL, false); sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "rssi"); sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false); sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false); @@ -3946,6 +3992,11 @@ public class CarrierConfigManager { sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING, "connected_mmwave:5G,connected:5G"); sDefaults.putInt(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT, 0); + /* Default value is 1 hour. */ + sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000); + sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false); + sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false); + sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_SUB6_BOOL, false); sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false); /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108); @@ -3964,8 +4015,6 @@ public class CarrierConfigManager { /* Default value is 3 seconds. */ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 3000); sDefaults.putBoolean(KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL, true); - /* Default value is 1 hour. */ - sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000); sDefaults.putBoolean(KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL, true); /* Default value is 60 seconds. */ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG, 60000); @@ -4007,7 +4056,7 @@ public class CarrierConfigManager { sDefaults.putAll(Wifi.getDefaults()); sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false); sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false); - sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, 0); + sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1)); } /** diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java index e1c4bef0dd65..8b7a243c30e6 100644 --- a/telephony/java/android/telephony/DataFailCause.java +++ b/telephony/java/android/telephony/DataFailCause.java @@ -30,10 +30,8 @@ import java.util.Map; import java.util.Set; /** - * Returned as the reason for a data connection failure as defined by modem and some local errors. - * @hide + * DataFailCause collects data connection failure causes code from different sources. */ -@SystemApi public final class DataFailCause { /** There is no failure */ public static final int NONE = 0; @@ -841,8 +839,19 @@ public final class DataFailCause { /** * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP * configuration request because the requested APN is unauthorized. + * + * @deprecated Use {@link #VSNCP_APN_UNAUTHORIZED} instead. + * + * @hide + */ + @SystemApi + @Deprecated + public static final int VSNCP_APN_UNATHORIZED = 0x8BE; // NOTYPO + /** + * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP + * configuration request because the requested APN is unauthorized. */ - public static final int VSNCP_APN_UNATHORIZED = 0x8BE; + public static final int VSNCP_APN_UNAUTHORIZED = 0x8BE; /** * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP * configuration request because the PDN limit has been exceeded. @@ -1318,6 +1327,7 @@ public final class DataFailCause { sFailCauseMap.put(VSNCP_TIMEOUT, "VSNCP_TIMEOUT"); sFailCauseMap.put(VSNCP_GEN_ERROR, "VSNCP_GEN_ERROR"); sFailCauseMap.put(VSNCP_APN_UNATHORIZED, "VSNCP_APN_UNATHORIZED"); + sFailCauseMap.put(VSNCP_APN_UNAUTHORIZED, "VSNCP_APN_UNAUTHORIZED"); sFailCauseMap.put(VSNCP_PDN_LIMIT_EXCEEDED, "VSNCP_PDN_LIMIT_EXCEEDED"); sFailCauseMap.put(VSNCP_NO_PDN_GATEWAY_ADDRESS, "VSNCP_NO_PDN_GATEWAY_ADDRESS"); sFailCauseMap.put(VSNCP_PDN_GATEWAY_UNREACHABLE, "VSNCP_PDN_GATEWAY_UNREACHABLE"); @@ -1423,8 +1433,8 @@ public final class DataFailCause { if (configManager != null) { PersistableBundle b = configManager.getConfigForSubId(subId); if (b != null) { - String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager. - KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS); + String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager + .KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS); if (permanentFailureStrings != null) { permanentFailureSet = new HashSet<>(); for (Map.Entry<Integer, String> e : sFailCauseMap.entrySet()) { diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java index 708adebb6db3..e37a9b9e7a8c 100644 --- a/telephony/java/android/telephony/PreciseDataConnectionState.java +++ b/telephony/java/android/telephony/PreciseDataConnectionState.java @@ -257,8 +257,7 @@ public final class PreciseDataConnectionState implements Parcelable { * Return the cause code for the most recent change in {@link #getState}. In the event of an * error, this cause code will be non-zero. */ - // FIXME(b144774287): some of these cause codes should have a prescribed meaning. - public int getLastCauseCode() { + public @DataFailureCause int getLastCauseCode() { return mFailCause; } diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index 1449a62fbf35..f61d4e14f1d3 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -114,6 +114,7 @@ public class DctConstants { public static final int EVENT_SERVICE_STATE_CHANGED = BASE + 52; public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53; public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54; + public static final int EVENT_UPDATE_CARRIER_CONFIGS = BASE + 55; /***** Constants *****/ @@ -123,4 +124,6 @@ public class DctConstants { public static final String APN_TYPE_KEY = "apnType"; public static final String PROVISIONING_URL_KEY = "provisioningUrl"; + public static final String BANDWIDTH_SOURCE_MODEM_KEY = "modem"; + public static final String BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY = "carrier_config"; } diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java index f7ec11c79476..d1d64d39b688 100644 --- a/test-mock/src/android/test/mock/MockContentProvider.java +++ b/test-mock/src/android/test/mock/MockContentProvider.java @@ -156,6 +156,12 @@ public class MockContentProvider extends ContentProvider { } @Override + public void canonicalizeAsync(String callingPkg, String featureId, Uri uri, + RemoteCallback callback) { + MockContentProvider.this.canonicalizeAsync(uri, callback); + } + + @Override public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) throws RemoteException { return MockContentProvider.this.uncanonicalize(uri); @@ -292,6 +298,18 @@ public class MockContentProvider extends ContentProvider { /** * @hide */ + @SuppressWarnings("deprecation") + public void canonicalizeAsync(Uri uri, RemoteCallback callback) { + AsyncTask.SERIAL_EXECUTOR.execute(() -> { + final Bundle bundle = new Bundle(); + bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, canonicalize(uri)); + callback.sendResult(bundle); + }); + } + + /** + * @hide + */ public boolean refresh(Uri url, Bundle args) { throw new UnsupportedOperationException("unimplemented mock method call"); } diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java index 359c44849634..2c6604759813 100644 --- a/test-mock/src/android/test/mock/MockContext.java +++ b/test-mock/src/android/test/mock/MockContext.java @@ -834,6 +834,12 @@ public class MockContext extends Context { /** @hide */ @Override + public Display getDisplayNoVerify() { + throw new UnsupportedOperationException(); + } + + /** @hide */ + @Override public int getDisplayId() { throw new UnsupportedOperationException(); } diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java index 1831bcdf9df7..223bcc59039d 100644 --- a/test-mock/src/android/test/mock/MockIContentProvider.java +++ b/test-mock/src/android/test/mock/MockIContentProvider.java @@ -145,12 +145,23 @@ public class MockIContentProvider implements IContentProvider { } @Override - public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) - throws RemoteException { + public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) { throw new UnsupportedOperationException("unimplemented mock method"); } @Override + @SuppressWarnings("deprecation") + public void canonicalizeAsync(String callingPkg, String featureId, Uri uri, + RemoteCallback remoteCallback) { + AsyncTask.SERIAL_EXECUTOR.execute(() -> { + final Bundle bundle = new Bundle(); + bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, + canonicalize(callingPkg, featureId, uri)); + remoteCallback.sendResult(bundle); + }); + } + + @Override public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) throws RemoteException { throw new UnsupportedOperationException("unimplemented mock method"); diff --git a/tests/BootImageProfileTest/TEST_MAPPING b/tests/BootImageProfileTest/DISABLED_TEST_MAPPING index 1b569f9455bf..1b569f9455bf 100644 --- a/tests/BootImageProfileTest/TEST_MAPPING +++ b/tests/BootImageProfileTest/DISABLED_TEST_MAPPING diff --git a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java index 52f6eba4072b..e616ac46830f 100644 --- a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java +++ b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java @@ -27,6 +27,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.TimeUnit; + /** * Runs rollback tests for multiple users. */ @@ -41,7 +43,6 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test { @After public void tearDown() throws Exception { - getDevice().switchUser(mOriginalUserId); getDevice().executeShellCommand("pm uninstall com.android.cts.install.lib.testapp.A"); removeSecondaryUserIfNecessary(); } @@ -49,9 +50,9 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test { @Before public void setup() throws Exception { mOriginalUserId = getDevice().getCurrentUser(); - installPackageAsUser("RollbackTest.apk", true, mOriginalUserId); - createAndSwitchToSecondaryUserIfNecessary(); - installPackageAsUser("RollbackTest.apk", true, mSecondaryUserId); + createAndStartSecondaryUser(); + // TODO(b/149733368): Remove the '-g' workaround when the bug is fixed. + installPackage("RollbackTest.apk", "-g --user all"); } @Test @@ -64,7 +65,6 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test { runPhaseForUsers("testMultipleUsersInstallV1", mOriginalUserId, mSecondaryUserId); runPhaseForUsers("testMultipleUsersUpgradeToV2", mOriginalUserId); runPhaseForUsers("testMultipleUsersUpdateUserData", mOriginalUserId, mSecondaryUserId); - switchToUser(mOriginalUserId); getDevice().executeShellCommand("pm rollback-app com.android.cts.install.lib.testapp.A"); runPhaseForUsers("testMultipleUsersVerifyUserdataRollback", mOriginalUserId, mSecondaryUserId); @@ -74,11 +74,11 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test { * Run the phase for the given user ids, in the order they are given. */ private void runPhaseForUsers(String phase, int... userIds) throws Exception { + final long timeout = TimeUnit.MINUTES.toMillis(10); for (int userId: userIds) { - switchToUser(userId); - assertTrue(runDeviceTests("com.android.tests.rollback", + assertTrue(runDeviceTests(getDevice(), "com.android.tests.rollback", "com.android.tests.rollback.MultiUserRollbackTest", - phase)); + phase, userId, timeout)); } } @@ -89,21 +89,7 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test { } } - private void createAndSwitchToSecondaryUserIfNecessary() throws Exception { - if (mSecondaryUserId == -1) { - mOriginalUserId = getDevice().getCurrentUser(); - mSecondaryUserId = getDevice().createUser("MultiUserRollbackTest_User" - + System.currentTimeMillis()); - switchToUser(mSecondaryUserId); - } - } - - private void switchToUser(int userId) throws Exception { - if (getDevice().getCurrentUser() == userId) { - return; - } - - assertTrue(getDevice().switchUser(userId)); + private void awaitUserUnlocked(int userId) throws Exception { for (int i = 0; i < SWITCH_USER_COMPLETED_NUMBER_OF_POLLS; ++i) { String userState = getDevice().executeShellCommand("am get-started-user-state " + userId); @@ -112,6 +98,14 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test { } Thread.sleep(SWITCH_USER_COMPLETED_POLL_INTERVAL_IN_MILLIS); } - fail("User switch to user " + userId + " timed out"); + fail("Timed out in unlocking user: " + userId); + } + + private void createAndStartSecondaryUser() throws Exception { + String name = "MultiUserRollbackTest_User" + System.currentTimeMillis(); + mSecondaryUserId = getDevice().createUser(name); + getDevice().startUser(mSecondaryUserId); + // Note we can't install apps on a locked user + awaitUserUnlocked(mSecondaryUserId); } } diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java index 0ffe041b0377..400bb04f0fab 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java @@ -17,13 +17,11 @@ package com.android.tests.rollback; import static com.android.cts.rollback.lib.RollbackInfoSubject.assertThat; -import static com.android.cts.rollback.lib.RollbackUtils.getUniqueRollbackInfoForPackage; import static com.google.common.truth.Truth.assertThat; import android.Manifest; import android.content.rollback.RollbackInfo; -import android.content.rollback.RollbackManager; import com.android.cts.install.lib.Install; import com.android.cts.install.lib.InstallUtils; @@ -77,13 +75,10 @@ public class MultiUserRollbackTest { */ @Test public void testMultipleUsersUpgradeToV2() throws Exception { - RollbackManager rm = RollbackUtils.getRollbackManager(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); Install.single(TestApp.A2).setEnableRollback().commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); - RollbackInfo rollback = getUniqueRollbackInfoForPackage( - rm.getAvailableRollbacks(), TestApp.A); - assertThat(rollback).isNotNull(); + RollbackInfo rollback = RollbackUtils.waitForAvailableRollback(TestApp.A); assertThat(rollback).packagesContainsExactly( Rollback.from(TestApp.A2).to(TestApp.A1)); } diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml index 638b6d1d7b5a..480b12be91c8 100644 --- a/tests/net/AndroidManifest.xml +++ b/tests/net/AndroidManifest.xml @@ -50,6 +50,7 @@ <application> <uses-library android:name="android.test.runner" /> + <uses-library android:name="android.net.ipsec.ike" /> </application> <instrumentation diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt index 490c46794e3d..23caf4952991 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt +++ b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt @@ -26,6 +26,7 @@ import android.net.util.SharedLog import android.os.IBinder import com.android.networkstack.metrics.DataStallStatsUtils import com.android.networkstack.netlink.TcpSocketTracker +import com.android.server.NetworkStackService import com.android.server.NetworkStackService.NetworkMonitorConnector import com.android.server.NetworkStackService.NetworkStackConnector import com.android.server.connectivity.NetworkMonitor @@ -88,6 +89,7 @@ class TestNetworkStackService : Service() { val nm = NetworkMonitor(this@TestNetworkStackService, cb, this.network, mock(IpConnectivityLog::class.java), mock(SharedLog::class.java), + mock(NetworkStackService.NetworkStackServiceManager::class.java), NetworkMonitorDeps(privateDnsBypassNetwork), mock(DataStallStatsUtils::class.java), mock(TcpSocketTracker::class.java)) diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 220cdce0d178..6d4a1b265171 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -1180,6 +1180,10 @@ public class ConnectivityServiceTest { Arrays.asList(new UserInfo[] { new UserInfo(VPN_USER, "", 0), })); + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; + when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) + .thenReturn(applicationInfo); // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not. // http://b/25897652 . @@ -3042,7 +3046,7 @@ public class ConnectivityServiceTest { networkCapabilities.addTransportType(TRANSPORT_WIFI) .setNetworkSpecifier(new MatchAllNetworkSpecifier()); mService.requestNetwork(networkCapabilities, null, 0, null, - ConnectivityManager.TYPE_WIFI, TEST_PACKAGE_NAME); + ConnectivityManager.TYPE_WIFI, mContext.getPackageName()); }); class NonParcelableSpecifier extends NetworkSpecifier { @@ -6439,17 +6443,89 @@ public class ConnectivityServiceTest { assertEquals(wifiLp, mService.getActiveLinkProperties()); } + private void setupLocationPermissions( + int targetSdk, boolean locationToggle, String op, String perm) throws Exception { + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.targetSdkVersion = targetSdk; + when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) + .thenReturn(applicationInfo); + + when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle); + + if (op != null) { + when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName()))) + .thenReturn(AppOpsManager.MODE_ALLOWED); + } + + if (perm != null) { + mServiceContext.setPermission(perm, PERMISSION_GRANTED); + } + } + + private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) { + final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid); + + return mService + .maybeSanitizeLocationInfoForCaller(netCap, callerUid, mContext.getPackageName()) + .getOwnerUid(); + } + + @Test + public void testMaybeSanitizeLocationInfoForCallerWithFineLocationAfterQ() throws Exception { + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + + final int myUid = Process.myUid(); + assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + } + + @Test + public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationPreQ() throws Exception { + setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION); + + final int myUid = Process.myUid(); + assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + } + + @Test + public void testMaybeSanitizeLocationInfoForCallerLocationOff() throws Exception { + // Test that even with fine location permission, and UIDs matching, the UID is sanitized. + setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + + final int myUid = Process.myUid(); + assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + } + + @Test + public void testMaybeSanitizeLocationInfoForCallerWrongUid() throws Exception { + // Test that even with fine location permission, not being the owner leads to sanitization. + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + + final int myUid = Process.myUid(); + assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid)); + } + @Test - public void testNetworkCapabilitiesRestrictedForCallerPermissions() { - int callerUid = Process.myUid(); - final NetworkCapabilities originalNc = new NetworkCapabilities(); - originalNc.setOwnerUid(callerUid); + public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationAfterQ() throws Exception { + // Test that not having fine location permission leads to sanitization. + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION); + + // Test that without the location permission, the owner field is sanitized. + final int myUid = Process.myUid(); + assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + } - final NetworkCapabilities newNc = - mService.networkCapabilitiesRestrictedForCallerPermissions( - originalNc, Process.myPid(), callerUid); + @Test + public void testMaybeSanitizeLocationInfoForCallerWithoutLocationPermission() throws Exception { + setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */); - assertEquals(Process.INVALID_UID, newNc.getOwnerUid()); + // Test that without the location permission, the owner field is sanitized. + final int myUid = Process.myUid(); + assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); } private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) @@ -6735,21 +6811,6 @@ public class ConnectivityServiceTest { mContext.getOpPackageName())); } - private void setupLocationPermissions( - int targetSdk, boolean locationToggle, String op, String perm) throws Exception { - final ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.targetSdkVersion = targetSdk; - when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) - .thenReturn(applicationInfo); - - when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle); - - when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName()))) - .thenReturn(AppOpsManager.MODE_ALLOWED); - - mServiceContext.setPermission(perm, PERMISSION_GRANTED); - } - private void setUpConnectivityDiagnosticsCallback() throws Exception { final NetworkRequest request = new NetworkRequest.Builder().build(); when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder); diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java index 957216e17925..26916bcffcfb 100644 --- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java +++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java @@ -38,6 +38,7 @@ public final class FrameworksTestsFilter extends SelectTest { "android.app.activity.ActivityThreadClientTest", // Test specifications for FrameworksCoreTests. "android.app.servertransaction.", // all tests under the package. + "android.view.CutoutSpecificationTest", "android.view.DisplayCutoutTest", "android.view.InsetsAnimationControlImplTest", "android.view.InsetsControllerTest", diff --git a/wifi/java/android/net/wifi/WifiOemMigrationHook.java b/wifi/java/android/net/wifi/WifiOemMigrationHook.java index 44dbb98a8ab8..5301dd013363 100755 --- a/wifi/java/android/net/wifi/WifiOemMigrationHook.java +++ b/wifi/java/android/net/wifi/WifiOemMigrationHook.java @@ -437,7 +437,7 @@ public final class WifiOemMigrationHook { Settings.Global.WIFI_SCAN_THROTTLE_ENABLED, 1) == 1) .setVerboseLoggingEnabled( Settings.Global.getInt(context.getContentResolver(), - Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 1) == 1) + Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 0) == 1) .build(); } } |