diff options
605 files changed, 16483 insertions, 6865 deletions
diff --git a/Android.bp b/Android.bp index 202e548d56c8..9655daf649c7 100644 --- a/Android.bp +++ b/Android.bp @@ -536,7 +536,7 @@ java_library { "android.hardware.vibrator-V1.3-java", "android.security.apc-java", "android.security.authorization-java", - "android.system.keystore2-java", + "android.system.keystore2-V1-java", "android.system.suspend.control.internal-java", "cameraprotosnano", "devicepolicyprotosnano", @@ -760,6 +760,7 @@ gensrcs { srcs: [ ":ipconnectivity-proto-src", ":libstats_atom_enum_protos", + ":libtombstone_proto-src", "core/proto/**/*.proto", "libs/incident/**/*.proto", ":service-permission-protos", diff --git a/StubLibraries.bp b/StubLibraries.bp index 49433f16f572..86364af20812 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -82,12 +82,13 @@ stubs_defaults { "android.hardware.vibrator-V1.3-java", "framework-protos", "stable.core.platform.api.stubs", - // There are a few classes from modules used as type arguments that - // need to be resolved by metalava. For now, we can use a previously - // finalized stub library to resolve them. If a new class gets added, - // this may be need to be revisited to use a manually maintained stub - // library with empty classes in order to resolve those references. - "sdk_system_30_android", + // There are a few classes from modules used by the core that + // need to be resolved by metalava. We use a prebuilt stub of the + // full sdk to ensure we can resolve them. If a new class gets added, + // the prebuilts/sdk/current needs to be updated. + "sdk_system_current_android", + // NOTE: The below can be removed once the prebuilt stub contains IKE. + "sdk_system_current_android.net.ipsec.ike", ], high_mem: true, // Lots of sources => high memory use, see b/170701554 installable: false, @@ -404,7 +405,11 @@ java_library_static { "android_defaults_stubs_current", "android_stubs_dists_default", ], - libs: ["sdk_system_29_android"], + libs: [ + "sdk_system_current_android", + // NOTE: The below can be removed once the prebuilt stub contains IKE. + "sdk_system_current_android.net.ipsec.ike", + ], static_libs: ["art.module.public.api.stubs"], dist: { dir: "apistubs/android/module-lib", diff --git a/apct-tests/perftests/core/src/android/app/OWNERS b/apct-tests/perftests/core/src/android/app/OWNERS new file mode 100644 index 000000000000..4f168ceb3c55 --- /dev/null +++ b/apct-tests/perftests/core/src/android/app/OWNERS @@ -0,0 +1,2 @@ +per-file Overlay* = file:/core/java/android/app/RESOURCES_OWNERS +per-file Resources* = file:/core/java/android/app/RESOURCES_OWNERS diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java index 18856f782b7d..82e967ae1a0b 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -16,33 +16,43 @@ package com.android.server.job; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + import android.annotation.IntDef; import android.annotation.NonNull; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; +import android.app.UserSwitchObserver; import android.app.job.JobInfo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.UserInfo; import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; +import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Pair; import android.util.Slog; import android.util.SparseIntArray; +import android.util.SparseLongArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.util.StatLogger; import com.android.server.JobSchedulerBackgroundThread; +import com.android.server.LocalServices; import com.android.server.job.controllers.JobStatus; import com.android.server.job.controllers.StateController; +import com.android.server.pm.UserManagerInternal; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -65,13 +75,18 @@ class JobConcurrencyManager { // Try to give higher priority types lower values. static final int WORK_TYPE_NONE = 0; static final int WORK_TYPE_TOP = 1 << 0; - static final int WORK_TYPE_BG = 1 << 1; - private static final int NUM_WORK_TYPES = 2; + static final int WORK_TYPE_EJ = 1 << 1; + static final int WORK_TYPE_BG = 1 << 2; + static final int WORK_TYPE_BGUSER = 1 << 3; + @VisibleForTesting + static final int NUM_WORK_TYPES = 4; @IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = { WORK_TYPE_NONE, WORK_TYPE_TOP, - WORK_TYPE_BG + WORK_TYPE_EJ, + WORK_TYPE_BG, + WORK_TYPE_BGUSER }) @Retention(RetentionPolicy.SOURCE) public @interface WorkType { @@ -94,49 +109,63 @@ class JobConcurrencyManager { private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_ON = new WorkConfigLimitsPerMemoryTrimLevel( - new WorkTypeConfig("screen_on_normal", 8, + new WorkTypeConfig("screen_on_normal", 11, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 2)), + List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_EJ, 3), + Pair.create(WORK_TYPE_BG, 2)), // defaultMax - List.of(Pair.create(WORK_TYPE_BG, 6))), - new WorkTypeConfig("screen_on_moderate", 8, + List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4)) + ), + new WorkTypeConfig("screen_on_moderate", 9, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2), + Pair.create(WORK_TYPE_BG, 2)), // defaultMax - List.of(Pair.create(WORK_TYPE_BG, 4))), - new WorkTypeConfig("screen_on_low", 5, + List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)) + ), + new WorkTypeConfig("screen_on_low", 6, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1), + Pair.create(WORK_TYPE_BG, 1)), // defaultMax - List.of(Pair.create(WORK_TYPE_BG, 1))), + List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) + ), new WorkTypeConfig("screen_on_critical", 5, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)), // defaultMax - List.of(Pair.create(WORK_TYPE_BG, 1))) + List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) + ) ); private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF = new WorkConfigLimitsPerMemoryTrimLevel( - new WorkTypeConfig("screen_off_normal", 10, + new WorkTypeConfig("screen_off_normal", 13, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3), + Pair.create(WORK_TYPE_BG, 2)), // defaultMax - List.of(Pair.create(WORK_TYPE_BG, 6))), - new WorkTypeConfig("screen_off_moderate", 10, + List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4)) + ), + new WorkTypeConfig("screen_off_moderate", 13, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)), + List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 3), + Pair.create(WORK_TYPE_BG, 2)), // defaultMax - List.of(Pair.create(WORK_TYPE_BG, 4))), - new WorkTypeConfig("screen_off_low", 5, + List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)) + ), + new WorkTypeConfig("screen_off_low", 7, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2), + Pair.create(WORK_TYPE_BG, 1)), // defaultMax - List.of(Pair.create(WORK_TYPE_BG, 1))), + List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) + ), new WorkTypeConfig("screen_off_critical", 5, // defaultMin - List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)), // defaultMax - List.of(Pair.create(WORK_TYPE_BG, 1))) + List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)) + ) ); /** @@ -171,6 +200,10 @@ class JobConcurrencyManager { "assignJobsToContexts", "refreshSystemState", }); + @VisibleForTesting + GracePeriodObserver mGracePeriodObserver; + @VisibleForTesting + boolean mShouldRestrictBgUser; interface Stats { int ASSIGN_JOBS_TO_CONTEXTS = 0; @@ -182,9 +215,13 @@ class JobConcurrencyManager { JobConcurrencyManager(JobSchedulerService service) { mService = service; mLock = mService.mLock; - mContext = service.getContext(); + mContext = service.getTestableContext(); mHandler = JobSchedulerBackgroundThread.getHandler(); + + mGracePeriodObserver = new GracePeriodObserver(mContext); + mShouldRestrictBgUser = mContext.getResources().getBoolean( + R.bool.config_jobSchedulerRestrictBackgroundUser); } public void onSystemReady() { @@ -193,10 +230,18 @@ class JobConcurrencyManager { final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); mContext.registerReceiver(mReceiver, filter); + try { + ActivityManager.getService().registerUserSwitchObserver(mGracePeriodObserver, TAG); + } catch (RemoteException e) { + } onInteractiveStateChanged(mPowerManager.isInteractive()); } + void onUserRemoved(int userId) { + mGracePeriodObserver.onUserRemoved(userId); + } + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -224,7 +269,7 @@ class JobConcurrencyManager { Slog.d(TAG, "Interactive: " + interactive); } - final long nowRealtime = JobSchedulerService.sElapsedRealtimeClock.millis(); + final long nowRealtime = sElapsedRealtimeClock.millis(); if (interactive) { mLastScreenOnRealtime = nowRealtime; mEffectiveInteractiveState = true; @@ -261,7 +306,7 @@ class JobConcurrencyManager { if (mLastScreenOnRealtime > mLastScreenOffRealtime) { return; } - final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + final long now = sElapsedRealtimeClock.millis(); if ((mLastScreenOffRealtime + mScreenOffAdjustmentDelayMs) > now) { return; } @@ -723,6 +768,10 @@ class JobConcurrencyManager { pw.print(mLastMemoryTrimLevel); pw.println(); + pw.print("User Grace Period: "); + pw.print(mGracePeriodObserver.mGracePeriodExpiration); + pw.println(); + mStatLogger.dump(pw); } finally { pw.decreaseIndent(); @@ -748,15 +797,52 @@ class JobConcurrencyManager { proto.end(token); } + /** + * Decides whether a job is from the current foreground user or the equivalent. + */ + @VisibleForTesting + boolean shouldRunAsFgUserJob(JobStatus job) { + if (!mShouldRestrictBgUser) return true; + int userId = job.getSourceUserId(); + UserManagerInternal um = LocalServices.getService(UserManagerInternal.class); + UserInfo userInfo = um.getUserInfo(userId); + + // If the user has a parent user (e.g. a work profile of another user), the user should be + // treated equivalent as its parent user. + if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID + && userInfo.profileGroupId != userId) { + userId = userInfo.profileGroupId; + userInfo = um.getUserInfo(userId); + } + + int currentUser = LocalServices.getService(ActivityManagerInternal.class) + .getCurrentUserId(); + // A user is treated as foreground user if any of the followings is true: + // 1. The user is current user + // 2. The user is primary user + // 3. The user's grace period has not expired + return currentUser == userId || userInfo.isPrimary() + || mGracePeriodObserver.isWithinGracePeriodForUser(userId); + } + int getJobWorkTypes(@NonNull JobStatus js) { int classification = 0; - // TODO(171305774): create dedicated work type for EJ and FGS - if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP - || js.shouldTreatAsExpeditedJob()) { - classification |= WORK_TYPE_TOP; + // TODO: create dedicated work type for FGS + if (shouldRunAsFgUserJob(js)) { + if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { + classification |= WORK_TYPE_TOP; + } else { + classification |= WORK_TYPE_BG; + } + + if (js.shouldTreatAsExpeditedJob()) { + classification |= WORK_TYPE_EJ; + } } else { - classification |= WORK_TYPE_BG; + // TODO(171305774): create dedicated slots for EJs of bg user + classification |= WORK_TYPE_BGUSER; } + return classification; } @@ -765,9 +851,15 @@ class JobConcurrencyManager { private static final String KEY_PREFIX_MAX_TOTAL = CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_"; private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_"; + private static final String KEY_PREFIX_MAX_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "max_ej_"; private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_"; + private static final String KEY_PREFIX_MAX_BGUSER = + CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_"; private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_"; + private static final String KEY_PREFIX_MIN_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "min_ej_"; private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_"; + private static final String KEY_PREFIX_MIN_BGUSER = + CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_"; private final String mConfigIdentifier; private int mMaxTotal; @@ -811,10 +903,18 @@ class JobConcurrencyManager { properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal)))); mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop); + final int maxEj = Math.max(1, Math.min(mMaxTotal, + properties.getInt(KEY_PREFIX_MAX_EJ + mConfigIdentifier, + mDefaultMaxAllowedSlots.get(WORK_TYPE_EJ, mMaxTotal)))); + mMaxAllowedSlots.put(WORK_TYPE_EJ, maxEj); final int maxBg = Math.max(1, Math.min(mMaxTotal, properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier, mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal)))); mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg); + final int maxBgUser = Math.max(1, Math.min(mMaxTotal, + properties.getInt(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier, + mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER, mMaxTotal)))); + mMaxAllowedSlots.put(WORK_TYPE_BGUSER, maxBgUser); int remaining = mMaxTotal; mMinReservedSlots.clear(); @@ -824,11 +924,23 @@ class JobConcurrencyManager { mDefaultMinReservedSlots.get(WORK_TYPE_TOP)))); mMinReservedSlots.put(WORK_TYPE_TOP, minTop); remaining -= minTop; + // Ensure ej is in the range [0, min(maxEj, remaining)] + final int minEj = Math.max(0, Math.min(Math.min(maxEj, remaining), + properties.getInt(KEY_PREFIX_MIN_EJ + mConfigIdentifier, + mDefaultMinReservedSlots.get(WORK_TYPE_EJ)))); + mMinReservedSlots.put(WORK_TYPE_EJ, minEj); + remaining -= minEj; // Ensure bg is in the range [0, min(maxBg, remaining)] final int minBg = Math.max(0, Math.min(Math.min(maxBg, remaining), properties.getInt(KEY_PREFIX_MIN_BG + mConfigIdentifier, mDefaultMinReservedSlots.get(WORK_TYPE_BG)))); mMinReservedSlots.put(WORK_TYPE_BG, minBg); + remaining -= minBg; + // Ensure bg user is in the range [0, min(maxBgUser, remaining)] + final int minBgUser = Math.max(0, Math.min(Math.min(maxBgUser, remaining), + properties.getInt(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier, + mDefaultMinReservedSlots.get(WORK_TYPE_BGUSER, 0)))); + mMinReservedSlots.put(WORK_TYPE_BGUSER, minBgUser); } int getMaxTotal() { @@ -849,10 +961,18 @@ class JobConcurrencyManager { .println(); pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP)) .println(); + pw.print(KEY_PREFIX_MIN_EJ + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_EJ)) + .println(); + pw.print(KEY_PREFIX_MAX_EJ + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_EJ)) + .println(); pw.print(KEY_PREFIX_MIN_BG + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_BG)) .println(); pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG)) .println(); + pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier, + mMinReservedSlots.get(WORK_TYPE_BGUSER)).println(); + pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier, + mMaxAllowedSlots.get(WORK_TYPE_BGUSER)).println(); } } @@ -873,6 +993,58 @@ class JobConcurrencyManager { } /** + * This class keeps the track of when a user's grace period expires. + */ + @VisibleForTesting + static class GracePeriodObserver extends UserSwitchObserver { + // Key is UserId and Value is the time when grace period expires + @VisibleForTesting + final SparseLongArray mGracePeriodExpiration = new SparseLongArray(); + private int mCurrentUserId; + @VisibleForTesting + int mGracePeriod; + private final UserManagerInternal mUserManagerInternal; + final Object mLock = new Object(); + + + GracePeriodObserver(Context context) { + mCurrentUserId = LocalServices.getService(ActivityManagerInternal.class) + .getCurrentUserId(); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); + mGracePeriod = Math.max(0, context.getResources().getInteger( + R.integer.config_jobSchedulerUserGracePeriod)); + } + + @Override + public void onUserSwitchComplete(int newUserId) { + final long expiration = sElapsedRealtimeClock.millis() + mGracePeriod; + synchronized (mLock) { + if (mCurrentUserId != UserHandle.USER_NULL + && mUserManagerInternal.exists(mCurrentUserId)) { + mGracePeriodExpiration.append(mCurrentUserId, expiration); + } + mGracePeriodExpiration.delete(newUserId); + mCurrentUserId = newUserId; + } + } + + void onUserRemoved(int userId) { + synchronized (mLock) { + mGracePeriodExpiration.delete(userId); + } + } + + @VisibleForTesting + public boolean isWithinGracePeriodForUser(int userId) { + synchronized (mLock) { + return userId == mCurrentUserId + || sElapsedRealtimeClock.millis() + < mGracePeriodExpiration.get(userId, Long.MAX_VALUE); + } + } + } + + /** * This class decides, taking into account the current {@link WorkTypeConfig} and how many jobs * are running/pending, how many more job can start. * @@ -892,20 +1064,21 @@ class JobConcurrencyManager { private final SparseIntArray mNumPendingJobs = new SparseIntArray(NUM_WORK_TYPES); private final SparseIntArray mNumRunningJobs = new SparseIntArray(NUM_WORK_TYPES); private final SparseIntArray mNumStartingJobs = new SparseIntArray(NUM_WORK_TYPES); - private int mNumUnspecialized = 0; private int mNumUnspecializedRemaining = 0; void setConfig(@NonNull WorkTypeConfig workTypeConfig) { mConfigMaxTotal = workTypeConfig.getMaxTotal(); mConfigNumReservedSlots.put(WORK_TYPE_TOP, workTypeConfig.getMinReserved(WORK_TYPE_TOP)); + mConfigNumReservedSlots.put(WORK_TYPE_EJ, workTypeConfig.getMinReserved(WORK_TYPE_EJ)); mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG)); + mConfigNumReservedSlots.put(WORK_TYPE_BGUSER, + workTypeConfig.getMinReserved(WORK_TYPE_BGUSER)); mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP)); + mConfigAbsoluteMaxSlots.put(WORK_TYPE_EJ, workTypeConfig.getMax(WORK_TYPE_EJ)); mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG)); + mConfigAbsoluteMaxSlots.put(WORK_TYPE_BGUSER, workTypeConfig.getMax(WORK_TYPE_BGUSER)); - mNumUnspecialized = mConfigMaxTotal; - mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP); - mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG); mNumUnspecializedRemaining = mConfigMaxTotal; for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) { mNumUnspecializedRemaining -= Math.max(mNumRunningJobs.valueAt(i), @@ -934,9 +1107,15 @@ class JobConcurrencyManager { if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) { mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + 1); } + if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) { + mNumPendingJobs.put(WORK_TYPE_EJ, mNumPendingJobs.get(WORK_TYPE_EJ) + 1); + } if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) { mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + 1); } + if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) { + mNumPendingJobs.put(WORK_TYPE_BGUSER, mNumPendingJobs.get(WORK_TYPE_BGUSER) + 1); + } } void stageJob(@WorkType int workType) { @@ -1018,48 +1197,76 @@ class JobConcurrencyManager { void onCountDone() { // Calculate how many slots to reserve for each work type. "Unspecialized" slots will - // be reserved for higher importance types first (ie. top before bg). - mNumUnspecialized = mConfigMaxTotal; - final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP) - + mNumPendingJobs.get(WORK_TYPE_TOP); - int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop); - mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop); - mNumUnspecialized -= resTop; - final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG); - int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg); - mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg); - mNumUnspecialized -= resBg; - - mNumUnspecializedRemaining = mNumUnspecialized; - // Account for already running jobs after we've assigned the minimum number of slots. - int unspecializedAssigned; - int extraRunning = (mNumRunningJobs.get(WORK_TYPE_TOP) - resTop); - if (extraRunning > 0) { - unspecializedAssigned = Math.max(0, - Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP) - resTop, - extraRunning)); - resTop += unspecializedAssigned; - mNumUnspecializedRemaining -= extraRunning; - } - extraRunning = (mNumRunningJobs.get(WORK_TYPE_BG) - resBg); - if (extraRunning > 0) { - unspecializedAssigned = Math.max(0, - Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG) - resBg, extraRunning)); - resBg += unspecializedAssigned; - mNumUnspecializedRemaining -= extraRunning; - } + // be reserved for higher importance types first (ie. top before ej before bg). + // Steps: + // 1. Account for slots for already running jobs + // 2. Use remaining unaccounted slots to try and ensure minimum reserved slots + // 3. Allocate remaining up to max, based on importance - // Assign remaining unspecialized based on ranking. - unspecializedAssigned = Math.max(0, + mNumUnspecializedRemaining = mConfigMaxTotal; + + // Step 1 + int runTop = mNumRunningJobs.get(WORK_TYPE_TOP); + int resTop = runTop; + mNumUnspecializedRemaining -= resTop; + int runEj = mNumRunningJobs.get(WORK_TYPE_EJ); + int resEj = runEj; + mNumUnspecializedRemaining -= resEj; + int runBg = mNumRunningJobs.get(WORK_TYPE_BG); + int resBg = runBg; + mNumUnspecializedRemaining -= resBg; + int runBgUser = mNumRunningJobs.get(WORK_TYPE_BGUSER); + int resBgUser = runBgUser; + mNumUnspecializedRemaining -= resBgUser; + + // Step 2 + final int numTop = runTop + mNumPendingJobs.get(WORK_TYPE_TOP); + int fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining, + Math.min(numTop, mConfigNumReservedSlots.get(WORK_TYPE_TOP) - resTop))); + resTop += fillUp; + mNumUnspecializedRemaining -= fillUp; + final int numEj = runEj + mNumPendingJobs.get(WORK_TYPE_EJ); + fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining, + Math.min(numEj, mConfigNumReservedSlots.get(WORK_TYPE_EJ) - resEj))); + resEj += fillUp; + mNumUnspecializedRemaining -= fillUp; + final int numBg = runBg + mNumPendingJobs.get(WORK_TYPE_BG); + fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining, + Math.min(numBg, mConfigNumReservedSlots.get(WORK_TYPE_BG) - resBg))); + resBg += fillUp; + mNumUnspecializedRemaining -= fillUp; + final int numBgUser = runBgUser + mNumPendingJobs.get(WORK_TYPE_BGUSER); + fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining, + Math.min(numBgUser, + mConfigNumReservedSlots.get(WORK_TYPE_BGUSER) - resBgUser))); + resBgUser += fillUp; + mNumUnspecializedRemaining -= fillUp; + + // Step 3 + int unspecializedAssigned = Math.max(0, Math.min(mNumUnspecializedRemaining, Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), numTop) - resTop)); mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned); mNumUnspecializedRemaining -= unspecializedAssigned; + + unspecializedAssigned = Math.max(0, + Math.min(mNumUnspecializedRemaining, + Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ), numEj) - resEj)); + mNumActuallyReservedSlots.put(WORK_TYPE_EJ, resEj + unspecializedAssigned); + mNumUnspecializedRemaining -= unspecializedAssigned; + unspecializedAssigned = Math.max(0, Math.min(mNumUnspecializedRemaining, Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), numBg) - resBg)); mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned); mNumUnspecializedRemaining -= unspecializedAssigned; + + unspecializedAssigned = Math.max(0, + Math.min(mNumUnspecializedRemaining, + Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER), numBgUser) + - resBgUser)); + mNumActuallyReservedSlots.put(WORK_TYPE_BGUSER, resBgUser + unspecializedAssigned); + mNumUnspecializedRemaining -= unspecializedAssigned; } int canJobStart(int workTypes) { @@ -1072,6 +1279,15 @@ class JobConcurrencyManager { return WORK_TYPE_TOP; } } + if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) { + final int maxAllowed = Math.min( + mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ), + mNumActuallyReservedSlots.get(WORK_TYPE_EJ) + mNumUnspecializedRemaining); + if (mNumRunningJobs.get(WORK_TYPE_EJ) + mNumStartingJobs.get(WORK_TYPE_EJ) + < maxAllowed) { + return WORK_TYPE_EJ; + } + } if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) { final int maxAllowed = Math.min( mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), @@ -1081,6 +1297,16 @@ class JobConcurrencyManager { return WORK_TYPE_BG; } } + if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) { + final int maxAllowed = Math.min( + mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER), + mNumActuallyReservedSlots.get(WORK_TYPE_BGUSER) + + mNumUnspecializedRemaining); + if (mNumRunningJobs.get(WORK_TYPE_BGUSER) + mNumStartingJobs.get(WORK_TYPE_BGUSER) + < maxAllowed) { + return WORK_TYPE_BGUSER; + } + } return WORK_TYPE_NONE; } 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 7ce867c6c850..bfc153f5f2f7 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -743,6 +743,7 @@ public class JobSchedulerService extends com.android.server.SystemService mControllers.get(c).onUserRemovedLocked(userId); } } + mConcurrencyManager.onUserRemoved(userId); } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) { // Has this package scheduled any jobs, such that we will take action // if it were to be force-stopped? diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index e8a281717754..d249f2ae813c 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -16,7 +16,6 @@ package com.android.server.job.controllers; -import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; @@ -332,7 +331,7 @@ public final class ConnectivityController extends RestrictingController implemen if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { final long bandwidth = capabilities.getLinkDownstreamBandwidthKbps(); // If we don't know the bandwidth, all we can do is hope the job finishes in time. - if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) { + if (bandwidth > 0) { // Divide by 8 to convert bits to bytes. final long estimatedMillis = ((downloadBytes * DateUtils.SECOND_IN_MILLIS) / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8)); @@ -350,7 +349,7 @@ public final class ConnectivityController extends RestrictingController implemen if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { final long bandwidth = capabilities.getLinkUpstreamBandwidthKbps(); // If we don't know the bandwidth, all we can do is hope the job finishes in time. - if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) { + if (bandwidth > 0) { // Divide by 8 to convert bits to bytes. final long estimatedMillis = ((uploadBytes * DateUtils.SECOND_IN_MILLIS) / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8)); @@ -380,18 +379,16 @@ public final class ConnectivityController extends RestrictingController implemen private static boolean isStrictSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { - final NetworkCapabilities required; // A restricted job that's out of quota MUST use an unmetered network. if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX && !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) { - required = new NetworkCapabilities( + final NetworkCapabilities required = new NetworkCapabilities.Builder( jobStatus.getJob().getRequiredNetwork().networkCapabilities) - .addCapability(NET_CAPABILITY_NOT_METERED); + .addCapability(NET_CAPABILITY_NOT_METERED).build(); + return required.satisfiedByNetworkCapabilities(capabilities); } else { - required = jobStatus.getJob().getRequiredNetwork().networkCapabilities; + return jobStatus.getJob().getRequiredNetwork().canBeSatisfiedBy(capabilities); } - - return required.satisfiedByNetworkCapabilities(capabilities); } private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network, @@ -402,9 +399,9 @@ public final class ConnectivityController extends RestrictingController implemen } // See if we match after relaxing any unmetered request - final NetworkCapabilities relaxed = new NetworkCapabilities( + final NetworkCapabilities relaxed = new NetworkCapabilities.Builder( jobStatus.getJob().getRequiredNetwork().networkCapabilities) - .removeCapability(NET_CAPABILITY_NOT_METERED); + .removeCapability(NET_CAPABILITY_NOT_METERED).build(); if (relaxed.satisfiedByNetworkCapabilities(capabilities)) { // TODO: treat this as "maybe" response; need to check quotas return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC; diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java index e1a859648ee6..aefeab621778 100644 --- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java +++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java @@ -36,30 +36,46 @@ import java.util.Map; import java.util.Set; /** - * ApplicationMediaCapabilities is an immutable class that encapsulates an application's - * capabilities for handling newer video codec format and media features. - * - * The ApplicationMediaCapabilities class is used by the platform to represent an application's - * media capabilities as defined in their manifest(TODO: Add link) in order to determine - * whether modern media files need to be transcoded for that application (TODO: Add link). - * - * ApplicationMediaCapabilities objects can also be built by applications at runtime for use with - * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more - * control over the transcoding that is built into the platform. ApplicationMediaCapabilities - * provided by applications at runtime like this override the default manifest capabilities for that - * media access. - * - * <h3> Video Codec Support</h3> - * Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support - * for newer format with this class as they are assumed to support older format like h.264. - * - * <h4>Capability of handling HDR(high dynamic range) video</h4> - * There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform, - * application will only need to specify individual types they supported. + ApplicationMediaCapabilities is an immutable class that encapsulates an application's capabilities + for handling newer video codec format and media features. + + <p> + Android 12 introduces seamless media transcoding feature. By default, Android assumes apps can + support playback of all media formats. Apps that would like to request that media be transcoded + into a more compatible format should declare their media capabilities in a media_capabilities + .xml resource file and add it as a property tag in the AndroidManifest.xml file. Here is a example: + <pre> + {@code + <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android"> + <format android:name="HEVC" supported="true"/> + <format android:name="HDR10" supported="false"/> + <format android:name="HDR10Plus" supported="false"/> + </media-capabilities> + } + </pre> + The ApplicationMediaCapabilities class is generated from this xml and used by the platform to + represent an application's media capabilities in order to determine whether modern media files need + to be transcoded for that application. + </p> + + <p> + ApplicationMediaCapabilities objects can also be built by applications at runtime for use with + {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more + control over the transcoding that is built into the platform. ApplicationMediaCapabilities + provided by applications at runtime like this override the default manifest capabilities for that + media access.The object could be build either through {@link #createFromXml(XmlPullParser)} or + through the builder class {@link ApplicationMediaCapabilities.Builder} + + <h3> Video Codec Support</h3> + <p> + Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support + for newer format with this class as they are assumed to support older format like h.264. + + <h3>Capability of handling HDR(high dynamic range) video</h3> + <p> + There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform, + application will only need to specify individual types they supported. */ -// TODO(huang): Correct openTypedAssetFileDescriptor with the new API after it is added. -// TODO(hkuang): Add a link to seamless transcoding detail when it is published -// TODO(hkuang): Add code sample on how to build a capability object with MediaCodecList public final class ApplicationMediaCapabilities implements Parcelable { private static final String TAG = "ApplicationMediaCapabilities"; @@ -105,9 +121,9 @@ public final class ApplicationMediaCapabilities implements Parcelable { */ public boolean isVideoMimeTypeSupported( @NonNull String videoMime) throws FormatNotFoundException { - if (mUnsupportedVideoMimeTypes.contains(videoMime)) { + if (mUnsupportedVideoMimeTypes.contains(videoMime.toLowerCase())) { return false; - } else if (mSupportedVideoMimeTypes.contains(videoMime)) { + } else if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) { return true; } else { throw new FormatNotFoundException(videoMime); @@ -262,11 +278,27 @@ public final class ApplicationMediaCapabilities implements Parcelable { /** * Creates {@link ApplicationMediaCapabilities} from an xml. + * + * The xml's syntax is the same as the media_capabilities.xml used by the AndroidManifest.xml. + * <p> Here is an example: + * + * <pre> + * {@code + * <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android"> + * <format android:name="HEVC" supported="true"/> + * <format android:name="HDR10" supported="false"/> + * <format android:name="HDR10Plus" supported="false"/> + * </media-capabilities> + * } + * </pre> + * <p> + * * @param xmlParser The underlying {@link XmlPullParser} that will read the xml. * @return An ApplicationMediaCapabilities object. * @throws UnsupportedOperationException if the capabilities in xml config are invalid or * incompatible. */ + // TODO: Add developer.android.com link for the format of the xml. @NonNull public static ApplicationMediaCapabilities createFromXml(@NonNull XmlPullParser xmlParser) { ApplicationMediaCapabilities.Builder builder = new ApplicationMediaCapabilities.Builder(); @@ -430,7 +462,7 @@ public final class ApplicationMediaCapabilities implements Parcelable { mIsSlowMotionSupported = isSupported; break; default: - throw new UnsupportedOperationException("Invalid format name " + name); + Log.w(TAG, "Invalid format name " + name); } // Save the name and isSupported into the map for validate later. mFormatSupportedMap.put(name, isSupported); diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index a7396faf7677..be82b2230183 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -43,7 +43,7 @@ #include <android-base/properties.h> -#include <ui/DisplayConfig.h> +#include <ui/DisplayMode.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -65,6 +65,8 @@ namespace android { +using ui::DisplayMode; + static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip"; static const char PRODUCT_BOOTANIMATION_DARK_FILE[] = "/product/media/bootanimation-dark.zip"; static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip"; @@ -345,14 +347,14 @@ public: continue; } - DisplayConfig displayConfig; - const status_t error = SurfaceComposerClient::getActiveDisplayConfig( - mBootAnimation->mDisplayToken, &displayConfig); + DisplayMode displayMode; + const status_t error = SurfaceComposerClient::getActiveDisplayMode( + mBootAnimation->mDisplayToken, &displayMode); if (error != NO_ERROR) { - SLOGE("Can't get active display configuration."); + SLOGE("Can't get active display mode."); } - mBootAnimation->resizeSurface(displayConfig.resolution.getWidth(), - displayConfig.resolution.getHeight()); + mBootAnimation->resizeSurface(displayMode.resolution.getWidth(), + displayMode.resolution.getHeight()); } } } while (numEvents > 0); @@ -401,15 +403,15 @@ status_t BootAnimation::readyToRun() { if (mDisplayToken == nullptr) return NAME_NOT_FOUND; - DisplayConfig displayConfig; + DisplayMode displayMode; const status_t error = - SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &displayConfig); + SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &displayMode); if (error != NO_ERROR) return error; mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0); mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0); - ui::Size resolution = displayConfig.resolution; + ui::Size resolution = displayMode.resolution; resolution = limitSurfaceSize(resolution.width, resolution.height); // create the native surface sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"), diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist index 43a8a878a624..da4b25519e80 100644 --- a/config/preloaded-classes-denylist +++ b/config/preloaded-classes-denylist @@ -4,7 +4,6 @@ android.os.FileObserver android.os.NullVibrator android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask android.widget.Magnifier -com.android.server.BootReceiver$2 gov.nist.core.net.DefaultNetworkLayer android.net.rtp.AudioGroup android.net.rtp.AudioStream diff --git a/core/api/current.txt b/core/api/current.txt index 0f39e4ff796b..77829fb5f23a 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -6930,6 +6930,7 @@ package android.app.admin { method public void addPersistentPreferredActivity(@NonNull android.content.ComponentName, android.content.IntentFilter, @NonNull android.content.ComponentName); method public void addUserRestriction(@NonNull android.content.ComponentName, String); method public boolean bindDeviceAdminServiceAsUser(@NonNull android.content.ComponentName, android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle); + method public boolean canAdminGrantSensorsPermissions(); method public void clearApplicationUserData(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener); method public void clearCrossProfileIntentFilters(@NonNull android.content.ComponentName); method @Deprecated public void clearDeviceOwnerApp(String); @@ -7227,6 +7228,7 @@ package android.app.admin { field public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI"; field public static final String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR"; field public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE"; + field public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT = "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT"; field public static final String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER"; field public static final String EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS = "android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS"; field public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION"; @@ -12298,7 +12300,7 @@ package android.content.pm { method @Deprecated public abstract void removePackageFromPreferred(@NonNull String); method public abstract void removePermission(@NonNull String); method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int); - method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException; + method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException; method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int); method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int); method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int); @@ -12318,7 +12320,6 @@ package android.content.pm { field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3 field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1 field public static final int DONT_KILL_APP = 1; // 0x1 - field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS"; field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID"; field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT"; field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays"; @@ -12504,6 +12505,10 @@ package android.content.pm { ctor public PackageManager.NameNotFoundException(String); } + @java.lang.FunctionalInterface public static interface PackageManager.OnChecksumsReadyListener { + method public void onChecksumsReady(@NonNull java.util.List<android.content.pm.ApkChecksum>); + } + public static final class PackageManager.Property implements android.os.Parcelable { method public int describeContents(); method public boolean getBoolean(); @@ -15025,6 +15030,7 @@ package android.graphics { field public static final int RGB_565 = 4; // 0x4 field public static final int UNKNOWN = 0; // 0x0 field public static final int Y8 = 538982489; // 0x20203859 + field public static final int YCBCR_P010 = 54; // 0x36 field public static final int YUV_420_888 = 35; // 0x23 field public static final int YUV_422_888 = 39; // 0x27 field public static final int YUV_444_888 = 40; // 0x28 @@ -15789,6 +15795,7 @@ package android.graphics { method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter); method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float); method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float, @NonNull android.graphics.RenderEffect); + method @NonNull public static android.graphics.RenderEffect createShaderEffect(@NonNull android.graphics.Shader); } public final class RenderNode { @@ -20471,6 +20478,7 @@ package android.media { field public static final int QUALITY_480P = 4; // 0x4 field public static final int QUALITY_4KDCI = 10; // 0xa field public static final int QUALITY_720P = 5; // 0x5 + field public static final int QUALITY_8KUHD = 13; // 0xd field public static final int QUALITY_CIF = 3; // 0x3 field public static final int QUALITY_HIGH = 1; // 0x1 field public static final int QUALITY_HIGH_SPEED_1080P = 2004; // 0x7d4 @@ -20492,6 +20500,7 @@ package android.media { field public static final int QUALITY_TIME_LAPSE_480P = 1004; // 0x3ec field public static final int QUALITY_TIME_LAPSE_4KDCI = 1010; // 0x3f2 field public static final int QUALITY_TIME_LAPSE_720P = 1005; // 0x3ed + field public static final int QUALITY_TIME_LAPSE_8KUHD = 1013; // 0x3f5 field public static final int QUALITY_TIME_LAPSE_CIF = 1003; // 0x3eb field public static final int QUALITY_TIME_LAPSE_HIGH = 1001; // 0x3e9 field public static final int QUALITY_TIME_LAPSE_LOW = 1000; // 0x3e8 @@ -31444,6 +31453,7 @@ package android.os { method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle); method public boolean hasUserRestriction(String); method public boolean isDemoUser(); + method public static boolean isHeadlessSystemUserMode(); method public boolean isManagedProfile(); method public boolean isQuietModeEnabled(android.os.UserHandle); method public boolean isSystemUser(); @@ -42717,7 +42727,11 @@ package android.telephony.ims { } public class ImsRcsManager { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter(); + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN"; } @@ -51068,7 +51082,7 @@ package android.view.inputmethod { method public boolean sendKeyEvent(android.view.KeyEvent); method public boolean setComposingRegion(int, int); method public boolean setComposingText(CharSequence, int); - method public default boolean setImeTemporarilyConsumesInput(boolean); + method public default boolean setImeConsumesInput(boolean); method public boolean setSelection(int, int); field public static final int CURSOR_UPDATE_IMMEDIATE = 1; // 0x1 field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2 @@ -53344,7 +53358,7 @@ package android.widget { method public void onSelectedDayChange(@NonNull android.widget.CalendarView, int, int, int); } - public class CheckBox extends android.widget.CompoundButton { + @android.widget.RemoteViews.RemoteView public class CheckBox extends android.widget.CompoundButton { ctor public CheckBox(android.content.Context); ctor public CheckBox(android.content.Context, android.util.AttributeSet); ctor public CheckBox(android.content.Context, android.util.AttributeSet, int); @@ -53410,6 +53424,7 @@ package android.widget { method public boolean isChecked(); method public void setButtonDrawable(@DrawableRes int); method public void setButtonDrawable(@Nullable android.graphics.drawable.Drawable); + method public void setButtonIcon(@Nullable android.graphics.drawable.Icon); method public void setButtonTintBlendMode(@Nullable android.graphics.BlendMode); method public void setButtonTintList(@Nullable android.content.res.ColorStateList); method public void setButtonTintMode(@Nullable android.graphics.PorterDuff.Mode); @@ -54450,14 +54465,14 @@ package android.widget { field protected String[] mExcludeMimes; } - public class RadioButton extends android.widget.CompoundButton { + @android.widget.RemoteViews.RemoteView public class RadioButton extends android.widget.CompoundButton { ctor public RadioButton(android.content.Context); ctor public RadioButton(android.content.Context, android.util.AttributeSet); ctor public RadioButton(android.content.Context, android.util.AttributeSet, int); ctor public RadioButton(android.content.Context, android.util.AttributeSet, int, int); } - public class RadioGroup extends android.widget.LinearLayout { + @android.widget.RemoteViews.RemoteView public class RadioGroup extends android.widget.LinearLayout { ctor public RadioGroup(android.content.Context); ctor public RadioGroup(android.content.Context, android.util.AttributeSet); method public void check(@IdRes int); @@ -54579,6 +54594,7 @@ package android.widget { method public void setChronometerCountDown(@IdRes int, boolean); method public void setColor(@IdRes int, @NonNull String, @ColorRes int); method public void setColorStateList(@IdRes int, @NonNull String, @ColorRes int); + method public void setCompoundButtonChecked(@IdRes int, boolean); method public void setContentDescription(@IdRes int, CharSequence); method public void setDisplayedChild(@IdRes int, int); method public void setDouble(@IdRes int, String, double); @@ -54603,6 +54619,7 @@ package android.widget { method public void setOnClickResponse(@IdRes int, @NonNull android.widget.RemoteViews.RemoteResponse); method public void setPendingIntentTemplate(@IdRes int, android.app.PendingIntent); method public void setProgressBar(@IdRes int, int, int, boolean); + method public void setRadioGroupChecked(@IdRes int, @IdRes int); method public void setRelativeScrollPosition(@IdRes int, int); method @Deprecated public void setRemoteAdapter(int, @IdRes int, android.content.Intent); method public void setRemoteAdapter(@IdRes int, android.content.Intent); @@ -54969,7 +54986,7 @@ package android.widget { ctor public StackView(android.content.Context, android.util.AttributeSet, int, int); } - public class Switch extends android.widget.CompoundButton { + @android.widget.RemoteViews.RemoteView public class Switch extends android.widget.CompoundButton { ctor public Switch(android.content.Context); ctor public Switch(android.content.Context, android.util.AttributeSet); ctor public Switch(android.content.Context, android.util.AttributeSet, int); @@ -55000,12 +55017,14 @@ package android.widget { method public void setTextOff(CharSequence); method public void setTextOn(CharSequence); method public void setThumbDrawable(android.graphics.drawable.Drawable); + method public void setThumbIcon(@Nullable android.graphics.drawable.Icon); method public void setThumbResource(@DrawableRes int); method public void setThumbTextPadding(int); method public void setThumbTintBlendMode(@Nullable android.graphics.BlendMode); method public void setThumbTintList(@Nullable android.content.res.ColorStateList); method public void setThumbTintMode(@Nullable android.graphics.PorterDuff.Mode); method public void setTrackDrawable(android.graphics.drawable.Drawable); + method public void setTrackIcon(@Nullable android.graphics.drawable.Icon); method public void setTrackResource(@DrawableRes int); method public void setTrackTintBlendMode(@Nullable android.graphics.BlendMode); method public void setTrackTintList(@Nullable android.content.res.ColorStateList); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 1977babe0bfb..5bb3e0517032 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -290,6 +290,7 @@ package android { public static final class R.attr { field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600 + field public static final int hotwordDetectionService = 16844326; // 0x1010626 field public static final int isVrOnly = 16844152; // 0x1010578 field public static final int minExtensionVersion = 16844305; // 0x1010611 field public static final int requiredSystemPropertyName = 16844133; // 0x1010565 @@ -869,6 +870,7 @@ package android.app.admin { } public class DevicePolicyManager { + method public boolean canAdminGrantSensorsPermissionsForUser(int); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser(); @@ -1988,6 +1990,7 @@ package android.bluetooth { field public static final int UUID_BYTES_128_BIT = 16; // 0x10 field public static final int UUID_BYTES_16_BIT = 2; // 0x2 field public static final int UUID_BYTES_32_BIT = 4; // 0x4 + field @NonNull public static final android.os.ParcelUuid VOLUME_CONTROL; } public final class BufferConstraint implements android.os.Parcelable { @@ -4664,6 +4667,7 @@ package android.location { method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String); method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback); + method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerProviderRequestListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.Listener); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent); @@ -4672,6 +4676,7 @@ package android.location { method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle); method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setProviderEnabledForUser(@NonNull String, boolean, @NonNull android.os.UserHandle); method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean unregisterGnssBatchedLocationCallback(@NonNull android.location.BatchedLocationCallback); + method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void unregisterProviderRequestListener(@NonNull android.location.provider.ProviderRequest.Listener); } public final class LocationRequest implements android.os.Parcelable { @@ -4821,6 +4826,10 @@ package android.location.provider { method @NonNull public android.location.provider.ProviderRequest.Builder setWorkSource(@NonNull android.os.WorkSource); } + public static interface ProviderRequest.Listener { + method public void onProviderRequestChanged(@NonNull String, @NonNull android.location.provider.ProviderRequest); + } + } package android.media { @@ -4970,6 +4979,7 @@ package android.media { method public android.media.PlayerProxy getPlayerProxy(); method public int getPlayerState(); method public int getPlayerType(); + method @IntRange(from=0) public int getSessionId(); method public boolean isActive(); field public static final int PLAYER_STATE_IDLE = 1; // 0x1 field public static final int PLAYER_STATE_PAUSED = 3; // 0x3 @@ -8642,6 +8652,7 @@ package android.os { method @NonNull public static String formatUid(int); method public static int getAppId(int); method public int getIdentifier(); + method public static int getUid(@NonNull android.os.UserHandle, int); method @Deprecated public boolean isOwner(); method public boolean isSystem(); method public static int myUserId(); @@ -9509,6 +9520,11 @@ package android.security.keystore { method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int); } + public abstract class KeyProperties { + field public static final int NAMESPACE_APPLICATION = -1; // 0xffffffff + field public static final int NAMESPACE_WIFI = 102; // 0x66 + } + } package android.security.keystore.recovery { @@ -10331,7 +10347,7 @@ package android.service.storage { ctor public ExternalStorageService(); method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onEndSession(@NonNull String) throws java.io.IOException; - method public void onFreeCacheRequested(@NonNull java.util.UUID, long); + method public void onFreeCache(@NonNull java.util.UUID, long) throws java.io.IOException; method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException; method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException; field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2 @@ -11704,6 +11720,7 @@ package android.telephony { method public boolean canManageSubscription(@NonNull android.telephony.SubscriptionInfo, @NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubscriptionIdList(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String); + method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public byte[] getAllSimSpecificSettingsForBackup(); method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int); @@ -11711,6 +11728,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int); method public void requestEmbeddedSubscriptionInfoListRefresh(); method public void requestEmbeddedSubscriptionInfoListRefresh(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[]); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int); @@ -12941,6 +12959,17 @@ package android.telephony.ims { method @Deprecated public void onRegistering(int); } + public class ImsRcsManager { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnAvailabilityChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.OnAvailabilityChangedListener) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int, int) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnAvailabilityChangedListener(@NonNull android.telephony.ims.ImsRcsManager.OnAvailabilityChangedListener); + } + + public static interface ImsRcsManager.OnAvailabilityChangedListener { + method public void onAvailabilityChanged(int); + } + public final class ImsReasonInfo implements android.os.Parcelable { field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service"; } @@ -13581,11 +13610,24 @@ package android.telephony.ims.feature { ctor public RcsFeature(@NonNull java.util.concurrent.Executor); method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener); + method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities); method public void onFeatureReady(); method public void onFeatureRemoved(); + method public boolean queryCapabilityConfiguration(int, int); + method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus(); method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase); } + public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities { + ctor public RcsFeature.RcsImsCapabilities(int); + method public void addCapabilities(int); + method public boolean isCapable(int); + method public void removeCapabilities(int); + field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0 + field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1 + field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2 + } + } package android.telephony.ims.stub { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index e88b6b92581e..e39b2b856661 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -12,6 +12,7 @@ package android { field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA"; field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS"; + field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE"; field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; field public static final String MANAGE_ACTIVITY_TASKS = "android.permission.MANAGE_ACTIVITY_TASKS"; @@ -28,6 +29,7 @@ package android { field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE"; field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS"; + field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS"; field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS"; field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC"; field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS"; @@ -119,6 +121,7 @@ package android.app { public class ActivityOptions { method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener); + method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener); method public static void setExitTransitionTimeout(long); method public void setLaunchActivityType(int); method public void setLaunchTaskId(int); @@ -390,11 +393,10 @@ package android.app.admin { method public boolean isFactoryResetProtectionPolicySupported(); method @NonNull public static String operationToString(int); method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException; + method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int); method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int); method @NonNull public static String unsafeOperationReasonToString(int); field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED"; - field public static final String ACTION_MANAGED_PROFILE_CREATED = "android.app.action.MANAGED_PROFILE_CREATED"; - field public static final String ACTION_PROVISIONED_MANAGED_DEVICE = "android.app.action.PROVISIONED_MANAGED_DEVICE"; field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6 field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd @@ -462,6 +464,7 @@ package android.app.admin { } public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable { + method public boolean canDeviceOwnerGrantSensorsPermissions(); method public int describeContents(); method @NonNull public android.content.ComponentName getDeviceAdminComponentName(); method public long getLocalTime(); @@ -476,6 +479,7 @@ package android.app.admin { public static final class FullyManagedDeviceProvisioningParams.Builder { ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String); method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build(); + method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setDeviceOwnerCanGrantSensorsPermissions(boolean); method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean); method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long); method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale); @@ -899,6 +903,40 @@ package android.hardware.camera2 { } +package android.hardware.devicestate { + + public final class DeviceStateManager { + method public void addDeviceStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener); + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest); + method @NonNull public int[] getSupportedStates(); + method public void removeDeviceStateListener(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener); + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback); + } + + public static interface DeviceStateManager.DeviceStateListener { + method public void onDeviceStateChanged(int); + } + + public final class DeviceStateRequest { + method public int getFlags(); + method public int getState(); + method @NonNull public static android.hardware.devicestate.DeviceStateRequest.Builder newBuilder(int); + field public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1; // 0x1 + } + + public static final class DeviceStateRequest.Builder { + method @NonNull public android.hardware.devicestate.DeviceStateRequest build(); + method @NonNull public android.hardware.devicestate.DeviceStateRequest.Builder setFlags(int); + } + + public static interface DeviceStateRequest.Callback { + method public default void onRequestActivated(@NonNull android.hardware.devicestate.DeviceStateRequest); + method public default void onRequestCanceled(@NonNull android.hardware.devicestate.DeviceStateRequest); + method public default void onRequestSuspended(@NonNull android.hardware.devicestate.DeviceStateRequest); + } + +} + package android.hardware.display { public class AmbientDisplayConfiguration { diff --git a/core/java/Android.bp b/core/java/Android.bp index fb27f74211fb..af5df769ffad 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -7,3 +7,8 @@ filegroup { name: "IDropBoxManagerService.aidl", srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"], } + +filegroup { + name: "ITracingServiceProxy.aidl", + srcs: ["android/tracing/ITracingServiceProxy.aidl"], +} diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 9b6f4b43581b..c31c22cca329 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -538,4 +538,10 @@ public abstract class ActivityManagerInternal { * @return mBootTimeTempAllowlistDuration of ActivityManagerConstants. */ public abstract long getBootTimeTempAllowListDuration(); + + /** Register an {@link AnrController} to control the ANR dialog behavior */ + public abstract void registerAnrController(AnrController controller); + + /** Unregister an {@link AnrController} */ + public abstract void unregisterAnrController(AnrController controller); } diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 2b5e18d3feec..28da1c3a3eb7 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -17,6 +17,7 @@ package android.app; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; +import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.INVALID_DISPLAY; @@ -310,6 +311,9 @@ public class ActivityOptions { private static final String KEY_REMOTE_TRANSITION = "android:activity.remoteTransition"; + private static final String KEY_OVERRIDE_TASK_TRANSITION = + "android:activity.overrideTaskTransition"; + /** * @see #setLaunchCookie * @hide @@ -393,6 +397,7 @@ public class ActivityOptions { private RemoteAnimationAdapter mRemoteAnimationAdapter; private IBinder mLaunchCookie; private IRemoteTransition mRemoteTransition; + private boolean mOverrideTaskTransition; /** * Create an ActivityOptions specifying a custom animation to run when @@ -476,6 +481,40 @@ public class ActivityOptions { } /** + * Create an ActivityOptions specifying a custom animation to run when the activity in the + * different task is displayed. + * + * @param context Who is defining this. This is the application that the + * animation resources will be loaded from. + * @param enterResId A resource ID of the animation resource to use for + * the incoming activity. Use 0 for no animation. + * @param exitResId A resource ID of the animation resource to use for + * the outgoing activity. Use 0 for no animation. + * @param handler If <var>listener</var> is non-null this must be a valid + * Handler on which to dispatch the callback; otherwise it should be null. + * @param startedListener Optional OnAnimationStartedListener to find out when the + * requested animation has started running. If for some reason the animation + * is not executed, the callback will happen immediately. + * @param finishedListener Optional OnAnimationFinishedListener when the animation + * has finished running. + * + * @return Returns a new ActivityOptions object that you can use to + * supply these options as the options Bundle when starting an activity. + * @hide + */ + @RequiresPermission(START_TASKS_FROM_RECENTS) + @TestApi + public static @NonNull ActivityOptions makeCustomTaskAnimation(@NonNull Context context, + int enterResId, int exitResId, @Nullable Handler handler, + @Nullable OnAnimationStartedListener startedListener, + @Nullable OnAnimationFinishedListener finishedListener) { + ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, handler, + startedListener, finishedListener); + opts.mOverrideTaskTransition = true; + return opts; + } + + /** * Creates an ActivityOptions specifying a custom animation to run in place on an existing * activity. * @@ -1107,6 +1146,7 @@ public class ActivityOptions { mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE); mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder( KEY_REMOTE_TRANSITION)); + mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION); } /** @@ -1561,6 +1601,12 @@ public class ActivityOptions { return mLaunchCookie; } + + /** @hide */ + public boolean getOverrideTaskTransition() { + return mOverrideTaskTransition; + } + /** * Update the current values in this ActivityOptions from those supplied * in <var>otherOptions</var>. Any values @@ -1789,6 +1835,9 @@ public class ActivityOptions { if (mRemoteTransition != null) { b.putBinder(KEY_REMOTE_TRANSITION, mRemoteTransition.asBinder()); } + if (mOverrideTaskTransition) { + b.putBoolean(KEY_OVERRIDE_TASK_TRANSITION, mOverrideTaskTransition); + } return b; } diff --git a/core/java/android/app/AnrController.java b/core/java/android/app/AnrController.java new file mode 100644 index 000000000000..cfc9d2715720 --- /dev/null +++ b/core/java/android/app/AnrController.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +/** + * Interface to control the ANR dialog within the activity manager + * {@hide} + */ +public interface AnrController { + /** + * Returns the delay in milliseconds for an ANR dialog that is about to be shown for + * {@code packageName}. + */ + long getAnrDelayMillis(String packageName, int uid); +} diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index b51d4ac8c988..8ac91396a6b0 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -39,11 +39,13 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.ActivityInfo; +import android.content.pm.ApkChecksum; import android.content.pm.ApplicationInfo; import android.content.pm.ChangedPackages; import android.content.pm.Checksum; import android.content.pm.ComponentInfo; import android.content.pm.FeatureInfo; +import android.content.pm.IOnChecksumsReadyListener; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageManager; @@ -880,10 +882,10 @@ public class ApplicationPackageManager extends PackageManager { @Override public void requestChecksums(@NonNull String packageName, boolean includeSplits, @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers, - @NonNull IntentSender statusReceiver) + @NonNull OnChecksumsReadyListener onChecksumsReadyListener) throws CertificateEncodingException, NameNotFoundException { Objects.requireNonNull(packageName); - Objects.requireNonNull(statusReceiver); + Objects.requireNonNull(onChecksumsReadyListener); Objects.requireNonNull(trustedInstallers); try { if (trustedInstallers == TRUST_ALL) { @@ -895,8 +897,17 @@ public class ApplicationPackageManager extends PackageManager { "trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty " + "list of certificates."); } + IOnChecksumsReadyListener onChecksumsReadyListenerDelegate = + new IOnChecksumsReadyListener.Stub() { + @Override + public void onChecksumsReady(List<ApkChecksum> checksums) + throws RemoteException { + onChecksumsReadyListener.onChecksumsReady(checksums); + } + }; mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required, - encodeCertificates(trustedInstallers), statusReceiver, getUserId()); + encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate, + getUserId()); } catch (ParcelableException e) { e.maybeRethrow(PackageManager.NameNotFoundException.class); throw new RuntimeException(e); diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index e6aa7a77357c..1ff64dbe6d2e 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -59,7 +59,7 @@ per-file IInstantAppResolver.aidl = file:/services/core/java/com/android/server/ per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS # ResourcesManager -per-file ResourcesManager.java = rtmitchell@google.com, toddke@google.com +per-file ResourcesManager.java = file:RESOURCES_OWNERS # VoiceInteraction per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS diff --git a/core/java/android/app/RESOURCES_OWNERS b/core/java/android/app/RESOURCES_OWNERS new file mode 100644 index 000000000000..21c39a8828ad --- /dev/null +++ b/core/java/android/app/RESOURCES_OWNERS @@ -0,0 +1,2 @@ +rtmitchell@google.com +toddke@google.com diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java index 9092ef36deb6..29792ac47a36 100644 --- a/core/java/android/app/WindowTokenClient.java +++ b/core/java/android/app/WindowTokenClient.java @@ -44,8 +44,8 @@ public class WindowTokenClient extends IWindowToken.Stub { * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient} * can only attach one {@link Context}. * <p>This method must be called before invoking - * {@link android.view.IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle, - * String)}.<p/> + * {@link android.view.IWindowManager#registerWindowContextListener(IBinder, int, int, + * Bundle, boolean)}.<p/> * * @param context context to be attached * @throws IllegalStateException if attached context has already existed. diff --git a/core/java/android/app/admin/DevicePolicyCache.java b/core/java/android/app/admin/DevicePolicyCache.java index 8b0c7061925f..9c07f85a6390 100644 --- a/core/java/android/app/admin/DevicePolicyCache.java +++ b/core/java/android/app/admin/DevicePolicyCache.java @@ -57,6 +57,13 @@ public abstract class DevicePolicyCache { public abstract int getPermissionPolicy(@UserIdInt int userHandle); /** + * Caches {@link DevicePolicyManager#canAdminGrantSensorsPermissionsForUser(int)} for the + * given user. + */ + public abstract boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle); + + + /** * Empty implementation. */ private static class EmptyDevicePolicyCache extends DevicePolicyCache { @@ -77,5 +84,10 @@ public abstract class DevicePolicyCache { public int getPermissionPolicy(int userHandle) { return DevicePolicyManager.PERMISSION_POLICY_PROMPT; } + + @Override + public boolean canAdminGrantSensorsPermissionsForUser(int userHandle) { + return false; + } } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 642bce4eea08..82255c87c971 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -980,6 +980,19 @@ public class DevicePolicyManager { = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM"; /** + * A boolean extra indicating the admin of a fully-managed device opts out of controlling + * permission grants for sensor-related permissions, + * see {@link #setPermissionGrantState(ComponentName, String, String, int)}. + * + * The default for this extra is {@code false} - by default, the admin of a fully-managed + * device has the ability to grant sensors-related permissions. + * + * <p>Use with {@link #ACTION_PROVISION_MANAGED_DEVICE} only. + */ + public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT = + "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT"; + + /** * A String extra holding the URL-safe base64 encoded SHA-256 checksum of any signature of the * android package archive at the download location specified in {@link * #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}. @@ -1730,8 +1743,12 @@ public class DevicePolicyManager { * Broadcast action to notify ManagedProvisioning that * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction has changed. * @hide + * @deprecated No longer needed as ManagedProvisioning no longer handles + * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction changing. */ + // TODO(b/177221010): Remove when Managed Provisioning no longer depend on it. @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @Deprecated public static final String ACTION_DATA_SHARING_RESTRICTION_CHANGED = "android.app.action.DATA_SHARING_RESTRICTION_CHANGED"; @@ -5469,26 +5486,6 @@ public class DevicePolicyManager { "android.app.action.ACTION_SHOW_NEW_USER_DISCLAIMER"; /** - * Broadcast action: notify managed provisioning that the device has been provisioned. - * - * @hide - */ - @TestApi - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_PROVISIONED_MANAGED_DEVICE = - "android.app.action.PROVISIONED_MANAGED_DEVICE"; - - /** - * Broadcast action: notify managed provisioning that a new managed profile is created. - * - * @hide - */ - @TestApi - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_MANAGED_PROFILE_CREATED = - "android.app.action.MANAGED_PROFILE_CREATED"; - - /** * Widgets are enabled in keyguard */ public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; @@ -10536,6 +10533,13 @@ public class DevicePolicyManager { * As this policy only acts on runtime permission requests, it only applies to applications * built with a {@code targetSdkVersion} of {@link android.os.Build.VERSION_CODES#M} or later. * + * <p> + * NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, an auto-grant + * policy will not apply to certain sensors-related permissions on some configurations. + * See {@link #setPermissionGrantState(ComponentName, String, String, int)} for the list of + * permissions affected, and the behavior change for managed profiles and fully-managed + * devices. + * * @param admin Which profile or device owner this request is associated with. * @param policy One of the policy constants {@link #PERMISSION_POLICY_PROMPT}, * {@link #PERMISSION_POLICY_AUTO_GRANT} and {@link #PERMISSION_POLICY_AUTO_DENY}. @@ -10594,6 +10598,31 @@ public class DevicePolicyManager { * application built with a {@code targetSdkVersion} < * {@link android.os.Build.VERSION_CODES#M} the app-op matching the permission is set to * {@link android.app.AppOpsManager#MODE_IGNORED}, but the permission stays granted. + * <p> + * NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, control over + * the following, sensors-related, permissions is restricted: + * <ul> + * <li>Manifest.permission.ACCESS_FINE_LOCATION</li> + * <li>Manifest.permission.ACCESS_BACKGROUND_LOCATION</li> + * <li>Manifest.permission.ACCESS_COARSE_LOCATION</li> + * <li>Manifest.permission.CAMERA</li> + * <li>Manifest.permission.RECORD_AUDIO</li> + * <li>Manifest.permission.RECORD_BACKGROUND_AUDIO</li> + * <li>Manifest.permission.ACTIVITY_RECOGNITION</li> + * <li>Manifest.permission.BODY_SENSORS</li> + * </ul> + * <p> + * A profile owner may not grant these permissions (i.e. call this method with any of the + * permissions listed above and {@code grantState} of {@code #PERMISSION_GRANT_STATE_GRANTED}), + * but may deny them. + * <p> + * A device owner, by default, may continue granting these permissions. However, for increased + * user control, the admin may opt out of controlling grants for these permissions by including + * {@link #EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT} in the provisioning parameters. In that + * case the device owner's control will be limited do denying these permissions. + * <p> + * Attempts by the admin to grant these permissions, when the admin is restricted from doing + * so, will be silently ignored (no exception will be thrown). * * @param admin Which profile or device owner this request is associated with. * @param packageName The application to grant or revoke a permission to. @@ -13268,4 +13297,57 @@ public class DevicePolicyManager { } } } + + /** + * Resets the default cross profile intent filters that were set during + * {@link #createAndProvisionManagedProfile} between {@code userId} and all it's managed + * profiles if any. + * + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) { + if (mService != null) { + try { + mService.resetDefaultCrossProfileIntentFilters(userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + } + /** + * Returns true if the caller is running on a device where the admin can grant + * permissions related to device sensors. + * This is a signal that the device is a fully-managed device where personal usage is + * discouraged. + * The list of permissions is listed in + * {@link #setPermissionGrantState(ComponentName, String, String, int)}. + * + * May be called by any app. + * @return true if the app can grant device sensors-related permissions, false otherwise. + */ + public boolean canAdminGrantSensorsPermissions() { + return canAdminGrantSensorsPermissionsForUser(myUserId()); + } + + /** + * Returns true if the admin can control grants of sensors-related permissions, for + * a given user. + * + * @hide + * @param userId The ID of the user to check. + * @return if the admin may grant these permissions, false otherwise. + */ + @SystemApi + public boolean canAdminGrantSensorsPermissionsForUser(int userId) { + if (mService == null) { + return false; + } + try { + return mService.canAdminGrantSensorsPermissionsForUser(userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java index 83af0195ddba..5e1cbadb458e 100644 --- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java +++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java @@ -42,6 +42,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { private final long mLocalTime; @SuppressLint("UseIcu") @Nullable private final Locale mLocale; + private final boolean mDeviceOwnerCanGrantSensorsPermissions; private FullyManagedDeviceProvisioningParams( @NonNull ComponentName deviceAdminComponentName, @@ -49,13 +50,16 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { boolean leaveAllSystemAppsEnabled, @Nullable String timeZone, long localTime, - @Nullable @SuppressLint("UseIcu") Locale locale) { + @Nullable @SuppressLint("UseIcu") Locale locale, + boolean deviceOwnerCanGrantSensorsPermissions) { this.mDeviceAdminComponentName = requireNonNull(deviceAdminComponentName); this.mOwnerName = requireNonNull(ownerName); this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled; this.mTimeZone = timeZone; this.mLocalTime = localTime; this.mLocale = locale; + this.mDeviceOwnerCanGrantSensorsPermissions = + deviceOwnerCanGrantSensorsPermissions; } private FullyManagedDeviceProvisioningParams( @@ -64,13 +68,15 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { boolean leaveAllSystemAppsEnabled, @Nullable String timeZone, long localTime, - @Nullable String localeStr) { + @Nullable String localeStr, + boolean deviceOwnerCanGrantSensorsPermissions) { this(deviceAdminComponentName, ownerName, leaveAllSystemAppsEnabled, timeZone, localTime, - getLocale(localeStr)); + getLocale(localeStr), + deviceOwnerCanGrantSensorsPermissions); } @Nullable @@ -107,6 +113,14 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { } /** + * @return true if the device owner can control sensor-related permission grants, false + * if the device owner has opted out of it. + */ + public boolean canDeviceOwnerGrantSensorsPermissions() { + return mDeviceOwnerCanGrantSensorsPermissions; + } + + /** * Builder class for {@link FullyManagedDeviceProvisioningParams} objects. */ public static final class Builder { @@ -117,6 +131,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { private long mLocalTime; @SuppressLint("UseIcu") @Nullable private Locale mLocale; + // Default to allowing control over sensor permission grants. + boolean mDeviceOwnerCanGrantSensorsPermissions = true; /** * Initialize a new {@link Builder} to construct a @@ -181,6 +197,17 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { } /** + * Marks that the Device Owner may grant permissions related to device sensors. + * See {@link DevicePolicyManager#EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT}. + */ + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setDeviceOwnerCanGrantSensorsPermissions(boolean mayGrant) { + mDeviceOwnerCanGrantSensorsPermissions = mayGrant; + return this; + } + + /** * Combines all of the attributes that have been set on this {@code Builder} * * @return a new {@link FullyManagedDeviceProvisioningParams} object. @@ -193,7 +220,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { mLeaveAllSystemAppsEnabled, mTimeZone, mLocalTime, - mLocale); + mLocale, + mDeviceOwnerCanGrantSensorsPermissions); } } @@ -211,6 +239,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { + ", mTimeZone=" + (mTimeZone == null ? "null" : mTimeZone) + ", mLocalTime=" + mLocalTime + ", mLocale=" + (mLocale == null ? "null" : mLocale) + + ", mDeviceOwnerCanGrantSensorsPermissions=" + + mDeviceOwnerCanGrantSensorsPermissions + '}'; } @@ -222,6 +252,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { dest.writeString(mTimeZone); dest.writeLong(mLocalTime); dest.writeString(mLocale == null ? null : mLocale.toLanguageTag()); + dest.writeBoolean(mDeviceOwnerCanGrantSensorsPermissions); } @NonNull @@ -235,6 +266,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { String timeZone = in.readString(); long localtime = in.readLong(); String locale = in.readString(); + boolean deviceOwnerCanGrantSensorsPermissions = in.readBoolean(); return new FullyManagedDeviceProvisioningParams( componentName, @@ -242,7 +274,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { leaveAllSystemAppsEnabled, timeZone, localtime, - locale); + locale, + deviceOwnerCanGrantSensorsPermissions); } @Override diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index f9ee15393e93..89f30cc821ab 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -498,4 +498,7 @@ interface IDevicePolicyManager { UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage); void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage); + + void resetDefaultCrossProfileIntentFilters(int userId); + boolean canAdminGrantSensorsPermissionsForUser(int userId); } diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index 587e883edaf2..742d05c1ffa4 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -41,6 +41,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Map; +import java.util.Optional; import java.util.Set; /** @@ -460,24 +461,40 @@ public class FullBackup { Set<PathWithRequiredFlags> excludes, Map<String, Set<PathWithRequiredFlags>> includes) throws IOException, XmlPullParserException { + verifyTopLevelTag(parser, "full-backup-content"); + + parseRules(parser, excludes, includes, Optional.empty()); + + logParsingResults(excludes, includes); + } + + private void verifyTopLevelTag(XmlPullParser parser, String tag) + throws XmlPullParserException, IOException { int event = parser.getEventType(); // START_DOCUMENT while (event != XmlPullParser.START_TAG) { event = parser.next(); } - if (!"full-backup-content".equals(parser.getName())) { + if (!tag.equals(parser.getName())) { throw new XmlPullParserException("Xml file didn't start with correct tag" + - " (<full-backup-content>). Found \"" + parser.getName() + "\""); + " (" + tag + " ). Found \"" + parser.getName() + "\""); } if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { Log.v(TAG_XML_PARSER, "\n"); Log.v(TAG_XML_PARSER, "===================================================="); - Log.v(TAG_XML_PARSER, "Found valid fullBackupContent; parsing xml resource."); + Log.v(TAG_XML_PARSER, "Found valid " + tag + "; parsing xml resource."); Log.v(TAG_XML_PARSER, "===================================================="); Log.v(TAG_XML_PARSER, ""); } + } + private void parseRules(XmlPullParser parser, + Set<PathWithRequiredFlags> excludes, + Map<String, Set<PathWithRequiredFlags>> includes, + Optional<Integer> maybeRequiredFlags) + throws IOException, XmlPullParserException { + int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { switch (event) { case XmlPullParser.START_TAG: @@ -498,13 +515,7 @@ public class FullBackup { break; } - int requiredFlags = 0; // no transport flags are required by default - if (TAG_INCLUDE.equals(parser.getName())) { - // requiredFlags are only supported for <include /> tag, for <exclude /> - // we should always leave them as the default = 0 - requiredFlags = getRequiredFlagsFromString( - parser.getAttributeValue(null, "requireFlags")); - } + int requiredFlags = getRequiredFlagsForRule(parser, maybeRequiredFlags); // retrieve the include/exclude set we'll be adding this rule to Set<PathWithRequiredFlags> activeSet = parseCurrentTagForDomain( @@ -542,7 +553,7 @@ public class FullBackup { // Special case for sharedpref files (not dirs) also add ".xml" suffix file. if ("sharedpref".equals(domainFromXml) && !canonicalFile.isDirectory() && - !canonicalFile.getCanonicalPath().endsWith(".xml")) { + !canonicalFile.getCanonicalPath().endsWith(".xml")) { final String canonicalXmlPath = canonicalFile.getCanonicalPath() + ".xml"; activeSet.add(new PathWithRequiredFlags(canonicalXmlPath, @@ -554,6 +565,10 @@ public class FullBackup { } } } + } + + private void logParsingResults(Set<PathWithRequiredFlags> excludes, + Map<String, Set<PathWithRequiredFlags>> includes) { if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { Log.v(TAG_XML_PARSER, "\n"); Log.v(TAG_XML_PARSER, "Xml resource parsing complete."); @@ -613,6 +628,24 @@ public class FullBackup { return flags; } + private int getRequiredFlagsForRule(XmlPullParser parser, + Optional<Integer> maybeRequiredFlags) { + if (maybeRequiredFlags.isPresent()) { + // This is the new config format where required flags are specified for the whole + // section, not per rule. + return maybeRequiredFlags.get(); + } + + if (TAG_INCLUDE.equals(parser.getName())) { + // In the legacy config, requiredFlags are only supported for <include /> tag, + // for <exclude /> we should always leave them as the default = 0. + return getRequiredFlagsFromString( + parser.getAttributeValue(null, "requireFlags")); + } + + return 0; + } + private Set<PathWithRequiredFlags> parseCurrentTagForDomain(XmlPullParser parser, Set<PathWithRequiredFlags> excludes, Map<String, Set<PathWithRequiredFlags>> includes, String domain) diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 7eda50e5c9cb..ea7e5ea7c802 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -3198,6 +3198,61 @@ public final class BluetoothAdapter { } /** + * Register a callback to receive events whenever the bluetooth stack goes down and back up, + * e.g. in the event the bluetooth is turned off/on via settings. + * + * If the bluetooth stack is currently up, there will not be an initial callback call. + * You can use the return value as an indication of this being the case. + * + * Callbacks will be delivered on a binder thread. + * + * @return whether bluetooth is already up currently + * + * @hide + */ + public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) { + return getBluetoothService(callback.mRemote) != null; + } + + /** + * Unregister a callback registered via {@link #registerServiceLifecycleCallback} + * + * @hide + */ + public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) { + removeServiceStateCallback(callback.mRemote); + } + + /** + * A callback for {@link #registerServiceLifecycleCallback} + * + * @hide + */ + public abstract static class ServiceLifecycleCallback { + + /** Called when the bluetooth stack is up */ + public abstract void onBluetoothServiceUp(); + + /** Called when the bluetooth stack is down */ + public abstract void onBluetoothServiceDown(); + + IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() { + @Override + public void onBluetoothServiceUp(IBluetooth bluetoothService) { + ServiceLifecycleCallback.this.onBluetoothServiceUp(); + } + + @Override + public void onBluetoothServiceDown() { + ServiceLifecycleCallback.this.onBluetoothServiceDown(); + } + + @Override + public void onBrEdrDown() {} + }; + } + + /** * Starts a scan for Bluetooth LE devices. * * <p>Results of the scan are reported using the diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index c0736a6b7bba..d82cf19e8822 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -167,6 +167,11 @@ public final class BluetoothUuid { /** @hide */ @NonNull @SystemApi + public static final ParcelUuid VOLUME_CONTROL = + ParcelUuid.fromString("00001844-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java index 17bf11b3d4ef..960a08755cb8 100644 --- a/core/java/android/companion/Association.java +++ b/core/java/android/companion/Association.java @@ -38,7 +38,7 @@ public final class Association implements Parcelable { private final @NonNull String mDeviceMacAddress; private final @NonNull String mPackageName; private final @Nullable String mDeviceProfile; - private final boolean mKeepProfilePrivilegesWhenDeviceAway; + private final boolean mNotifyOnDeviceNearby; /** @hide */ public int getUserId() { @@ -47,7 +47,7 @@ public final class Association implements Parcelable { - // Code below generated by codegen v1.0.21. + // Code below generated by codegen v1.0.22. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -71,7 +71,7 @@ public final class Association implements Parcelable { @NonNull String deviceMacAddress, @NonNull String packageName, @Nullable String deviceProfile, - boolean keepProfilePrivilegesWhenDeviceAway) { + boolean notifyOnDeviceNearby) { this.mUserId = userId; com.android.internal.util.AnnotationValidations.validate( UserIdInt.class, null, mUserId); @@ -82,7 +82,7 @@ public final class Association implements Parcelable { com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mPackageName); this.mDeviceProfile = deviceProfile; - this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway; + this.mNotifyOnDeviceNearby = notifyOnDeviceNearby; // onConstructed(); // You can define this method to get a callback } @@ -103,8 +103,8 @@ public final class Association implements Parcelable { } @DataClass.Generated.Member - public boolean isKeepProfilePrivilegesWhenDeviceAway() { - return mKeepProfilePrivilegesWhenDeviceAway; + public boolean isNotifyOnDeviceNearby() { + return mNotifyOnDeviceNearby; } @Override @@ -118,7 +118,7 @@ public final class Association implements Parcelable { "deviceMacAddress = " + mDeviceMacAddress + ", " + "packageName = " + mPackageName + ", " + "deviceProfile = " + mDeviceProfile + ", " + - "keepProfilePrivilegesWhenDeviceAway = " + mKeepProfilePrivilegesWhenDeviceAway + + "notifyOnDeviceNearby = " + mNotifyOnDeviceNearby + " }"; } @@ -139,7 +139,7 @@ public final class Association implements Parcelable { && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress) && Objects.equals(mPackageName, that.mPackageName) && Objects.equals(mDeviceProfile, that.mDeviceProfile) - && mKeepProfilePrivilegesWhenDeviceAway == that.mKeepProfilePrivilegesWhenDeviceAway; + && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby; } @Override @@ -153,7 +153,7 @@ public final class Association implements Parcelable { _hash = 31 * _hash + Objects.hashCode(mDeviceMacAddress); _hash = 31 * _hash + Objects.hashCode(mPackageName); _hash = 31 * _hash + Objects.hashCode(mDeviceProfile); - _hash = 31 * _hash + Boolean.hashCode(mKeepProfilePrivilegesWhenDeviceAway); + _hash = 31 * _hash + Boolean.hashCode(mNotifyOnDeviceNearby); return _hash; } @@ -164,7 +164,7 @@ public final class Association implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; - if (mKeepProfilePrivilegesWhenDeviceAway) flg |= 0x10; + if (mNotifyOnDeviceNearby) flg |= 0x10; if (mDeviceProfile != null) flg |= 0x8; dest.writeByte(flg); dest.writeInt(mUserId); @@ -185,7 +185,7 @@ public final class Association implements Parcelable { // static FieldType unparcelFieldName(Parcel in) { ... } byte flg = in.readByte(); - boolean keepProfilePrivilegesWhenDeviceAway = (flg & 0x10) != 0; + boolean notifyOnDeviceNearby = (flg & 0x10) != 0; int userId = in.readInt(); String deviceMacAddress = in.readString(); String packageName = in.readString(); @@ -201,7 +201,7 @@ public final class Association implements Parcelable { com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mPackageName); this.mDeviceProfile = deviceProfile; - this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway; + this.mNotifyOnDeviceNearby = notifyOnDeviceNearby; // onConstructed(); // You can define this method to get a callback } @@ -221,10 +221,10 @@ public final class Association implements Parcelable { }; @DataClass.Generated( - time = 1606940835778L, - codegenVersion = "1.0.21", + time = 1610482674799L, + codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/companion/Association.java", - inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mKeepProfilePrivilegesWhenDeviceAway\npublic int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)") + inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mNotifyOnDeviceNearby\npublic int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 5d28216756ae..2a402b204cb7 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3552,6 +3552,7 @@ public abstract class Context { //@hide: NETWORK_SCORE_SERVICE, USAGE_STATS_SERVICE, MEDIA_SESSION_SERVICE, + MEDIA_COMMUNICATION_SERVICE, BATTERY_SERVICE, JOB_SCHEDULER_SERVICE, //@hide: PERSISTENT_DATA_BLOCK_SERVICE, diff --git a/core/java/android/content/pm/IOnChecksumsReadyListener.aidl b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl new file mode 100644 index 000000000000..7963ce19956a --- /dev/null +++ b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.content.pm.ApkChecksum; + +/** + * Listener that gets notified when checksums are available. + * {@hide} + */ +oneway interface IOnChecksumsReadyListener { + void onChecksumsReady(in List<ApkChecksum> checksums); +} diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index b8829bbf1ca5..a46876ec53c4 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -27,6 +27,7 @@ import android.content.pm.InstantAppInfo; import android.content.pm.FeatureInfo; import android.content.pm.IDexModuleRegisterCallback; import android.content.pm.InstallSourceInfo; +import android.content.pm.IOnChecksumsReadyListener; import android.content.pm.IPackageInstaller; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageDeleteObserver2; @@ -754,7 +755,7 @@ interface IPackageManager { void notifyPackagesReplacedReceived(in String[] packages); - void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId); + void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId); //------------------------------------------------------------------------ // diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 62925757cde1..0c0e4020d04a 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -450,6 +450,7 @@ public class LauncherApps { FLAG_MATCH_PINNED, FLAG_MATCH_MANIFEST, FLAG_MATCH_CACHED, + FLAG_MATCH_PINNED_BY_ANY_LAUNCHER, FLAG_GET_KEY_FIELDS_ONLY, FLAG_GET_PERSONS_DATA, }) diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index d09d83f0cd1d..b95b991b095c 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3858,13 +3858,6 @@ public abstract class PackageManager { public static final String EXTRA_FAILURE_EXISTING_PERMISSION = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION"; - /** - * Extra field name for the ID of a package pending verification. Passed to - * a package verifier and is used to call back to - * @see #requestChecksums - */ - public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS"; - /** * Permission flag: The permission is set in its current state * by the user and apps can still request it at runtime. @@ -8709,9 +8702,20 @@ public abstract class PackageManager { */ public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null); + /** Listener that gets notified when checksums are available. */ + @FunctionalInterface + public interface OnChecksumsReadyListener { + /** + * Called when the checksums are available. + * + * @param checksums array of checksums. + */ + void onChecksumsReady(@NonNull List<ApkChecksum> checksums); + } + /** * Requesting the checksums for APKs within a package. - * The checksums will be returned asynchronously via statusReceiver. + * The checksums will be returned asynchronously via onChecksumsReadyListener. * * By default returns all readily available checksums: * - enforced by platform, @@ -8730,15 +8734,14 @@ public abstract class PackageManager { * {@link #TRUST_ALL} will return checksums from any installer, * {@link #TRUST_NONE} disables optimized installer-enforced checksums, * otherwise the list has to be non-empty list of certificates. - * @param statusReceiver called once when the results are available as - * {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[]. + * @param onChecksumsReadyListener called once when the results are available. * @throws CertificateEncodingException if an encoding error occurs for trustedInstallers. * @throws IllegalArgumentException if the list of trusted installer certificates is empty. * @throws NameNotFoundException if a package with the given name cannot be found on the system. */ public void requestChecksums(@NonNull String packageName, boolean includeSplits, @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers, - @NonNull IntentSender statusReceiver) + @NonNull OnChecksumsReadyListener onChecksumsReadyListener) throws CertificateEncodingException, NameNotFoundException { throw new UnsupportedOperationException("requestChecksums not implemented in subclass"); } diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java index cbd2c550a797..9012b5ce2b1e 100644 --- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java @@ -108,17 +108,14 @@ public class ParsedPermissionUtils { permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel); - if (permission.getProtectionFlags() != 0) { - if ((permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0 - && (permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) - == 0 - && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) - != PermissionInfo.PROTECTION_SIGNATURE - && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) - != PermissionInfo.PROTECTION_INTERNAL) { - return input.error("<permission> protectionLevel specifies a non-instant flag " - + "but is not based on signature or internal type"); - } + final int otherProtectionFlags = permission.getProtectionFlags() + & ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT + | PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY); + if (otherProtectionFlags != 0 + && permission.getProtection() != PermissionInfo.PROTECTION_SIGNATURE + && permission.getProtection() != PermissionInfo.PROTECTION_INTERNAL) { + return input.error("<permission> protectionLevel specifies a non-instant, non-appop," + + " non-runtimeOnly flag but is not based on signature or internal type"); } return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input); diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index 4145a7273ed2..08b1e245dc83 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -29,7 +29,6 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; import android.os.RemoteException; -import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.util.Slog; @@ -47,6 +46,13 @@ public class BiometricManager { private static final String TAG = "BiometricManager"; /** + * An ID that should match any biometric sensor on the device. + * + * @hide + */ + public static final int SENSOR_ID_ANY = -1; + + /** * No error detected. */ public static final int BIOMETRIC_SUCCESS = @@ -139,7 +145,7 @@ public class BiometricManager { * * <p>This corresponds to {@link KeyProperties#AUTH_BIOMETRIC_STRONG} during key generation. * - * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) + * @see android.security.keystore.KeyGenParameterSpec.Builder */ int BIOMETRIC_STRONG = 0x000F; @@ -182,7 +188,7 @@ public class BiometricManager { * <p>This corresponds to {@link KeyProperties#AUTH_DEVICE_CREDENTIAL} during key * generation. * - * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) + * @see android.security.keystore.KeyGenParameterSpec.Builder */ int DEVICE_CREDENTIAL = 1 << 15; } diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 76cf9b9d28b9..4f6a7c75cca6 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -36,7 +36,6 @@ import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.security.identity.IdentityCredential; -import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.text.TextUtils; import android.util.Log; @@ -325,7 +324,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * request authentication with the proper set of authenticators (e.g. match the * authenticators specified during key generation). * - * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) + * @see android.security.keystore.KeyGenParameterSpec.Builder * @see KeyProperties#AUTH_BIOMETRIC_STRONG * @see KeyProperties#AUTH_DEVICE_CREDENTIAL * @@ -365,6 +364,21 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } /** + * If set, authenticate using the biometric sensor with the given ID. + * + * @param sensorId The ID of a biometric sensor, or -1 to allow any sensor (default). + * @return This builder. + * + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + @NonNull + public Builder setSensorId(int sensorId) { + mPromptInfo.setSensorId(sensorId); + return this; + } + + /** * Creates a {@link BiometricPrompt}. * * @return An instance of {@link BiometricPrompt}. @@ -589,7 +603,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and * time-based. This is specified during key creation via the timeout parameter of the - * {@link KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)} API. + * {@code setUserAuthenticationParameters(int, int)} method of {@link + * android.security.keystore.KeyGenParameterSpec.Builder}. * * <p>CryptoObjects are used to unlock auth-per-use keys via * {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor, @@ -778,6 +793,27 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId) { + authenticateUserForOperation(cancel, executor, callback, userId, 0 /* operationId */); + } + + /** + * Authenticates for the given user and keystore operation. + * + * @param cancel An object that can be used to cancel authentication + * @param executor An executor to handle callback events + * @param callback An object to receive authentication events + * @param userId The user to authenticate + * @param operationId The keystore operation associated with authentication + * + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void authenticateUserForOperation( + @NonNull CancellationSignal cancel, + @NonNull @CallbackExecutor Executor executor, + @NonNull AuthenticationCallback callback, + int userId, + long operationId) { if (cancel == null) { throw new IllegalArgumentException("Must supply a cancellation signal"); } @@ -787,7 +823,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan if (callback == null) { throw new IllegalArgumentException("Must supply a callback"); } - authenticateInternal(null /* crypto */, cancel, executor, callback, userId); + authenticateInternal(operationId, cancel, executor, callback, userId); } /** @@ -912,11 +948,31 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } } - private void authenticateInternal(@Nullable CryptoObject crypto, + private void authenticateInternal( + @Nullable CryptoObject crypto, @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId) { + + mCryptoObject = crypto; + final long operationId = crypto != null ? crypto.getOpId() : 0L; + authenticateInternal(operationId, cancel, executor, callback, userId); + } + + private void authenticateInternal( + long operationId, + @NonNull CancellationSignal cancel, + @NonNull @CallbackExecutor Executor executor, + @NonNull AuthenticationCallback callback, + int userId) { + + // Ensure we don't return the wrong crypto object as an auth result. + if (mCryptoObject != null && mCryptoObject.getOpId() != operationId) { + Log.w(TAG, "CryptoObject operation ID does not match argument; setting field to null"); + mCryptoObject = null; + } + try { if (cancel.isCanceled()) { Log.w(TAG, "Authentication already canceled"); @@ -925,13 +981,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan cancel.setOnCancelListener(new OnAuthenticationCancelListener()); } - mCryptoObject = crypto; mExecutor = executor; mAuthenticationCallback = callback; - final long operationId = crypto != null ? crypto.getOpId() : 0; final PromptInfo promptInfo; - if (crypto != null) { + if (operationId != 0L) { // Allowed authenticators should default to BIOMETRIC_STRONG for crypto auth. // Note that we use a new PromptInfo here so as to not overwrite the application's // preference, since it is possible that the same prompt configuration be used @@ -952,10 +1006,9 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } catch (RemoteException e) { Log.e(TAG, "Remote exception while authenticating", e); - mExecutor.execute(() -> { - callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE, - mContext.getString(R.string.biometric_error_hw_unavailable)); - }); + mExecutor.execute(() -> callback.onAuthenticationError( + BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE, + mContext.getString(R.string.biometric_error_hw_unavailable))); } } } diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java index c2eff7de832b..0e99f31d3b52 100644 --- a/core/java/android/hardware/biometrics/PromptInfo.java +++ b/core/java/android/hardware/biometrics/PromptInfo.java @@ -40,6 +40,7 @@ public class PromptInfo implements Parcelable { private @BiometricManager.Authenticators.Types int mAuthenticators; private boolean mDisallowBiometricsIfPolicyExists; private boolean mReceiveSystemEvents; + private int mSensorId = -1; public PromptInfo() { @@ -59,6 +60,7 @@ public class PromptInfo implements Parcelable { mAuthenticators = in.readInt(); mDisallowBiometricsIfPolicyExists = in.readBoolean(); mReceiveSystemEvents = in.readBoolean(); + mSensorId = in.readInt(); } public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() { @@ -93,6 +95,7 @@ public class PromptInfo implements Parcelable { dest.writeInt(mAuthenticators); dest.writeBoolean(mDisallowBiometricsIfPolicyExists); dest.writeBoolean(mReceiveSystemEvents); + dest.writeInt(mSensorId); } public boolean containsPrivateApiConfigurations() { @@ -166,6 +169,10 @@ public class PromptInfo implements Parcelable { mReceiveSystemEvents = receiveSystemEvents; } + public void setSensorId(int sensorId) { + mSensorId = sensorId; + } + // Getters public CharSequence getTitle() { @@ -226,4 +233,8 @@ public class PromptInfo implements Parcelable { public boolean isReceiveSystemEvents() { return mReceiveSystemEvents; } + + public int getSensorId() { + return mSensorId; + } } diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java index 29a6ee278d9c..f175e7b00b7e 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManager.java +++ b/core/java/android/hardware/devicestate/DeviceStateManager.java @@ -16,8 +16,12 @@ package android.hardware.devicestate; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemService; +import android.annotation.TestApi; import android.content.Context; import java.util.concurrent.Executor; @@ -28,13 +32,19 @@ import java.util.concurrent.Executor; * * @hide */ +@TestApi @SystemService(Context.DEVICE_STATE_SERVICE) public final class DeviceStateManager { - /** Invalid device state. */ + /** + * Invalid device state. + * + * @hide + */ public static final int INVALID_DEVICE_STATE = -1; - private DeviceStateManagerGlobal mGlobal; + private final DeviceStateManagerGlobal mGlobal; + /** @hide */ public DeviceStateManager() { DeviceStateManagerGlobal global = DeviceStateManagerGlobal.getInstance(); if (global == null) { @@ -45,23 +55,73 @@ public final class DeviceStateManager { } /** + * Returns the list of device states that are supported and can be requested with + * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. + */ + @NonNull + public int[] getSupportedStates() { + return mGlobal.getSupportedStates(); + } + + /** + * Submits a {@link DeviceStateRequest request} to modify the device state. + * <p> + * By default, the request is kept active until a call to + * {@link #cancelRequest(DeviceStateRequest)} or until one of the following occurs: + * <ul> + * <li>Another processes submits a request succeeding this request in which case the request + * will be suspended until the interrupting request is canceled. + * <li>The requested state has become unsupported. + * <li>The process submitting the request dies. + * </ul> + * However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}. + * + * @throws IllegalArgumentException if the requested state is unsupported. + * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} + * permission is not held. + * + * @see DeviceStateRequest + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) + public void requestState(@NonNull DeviceStateRequest request, + @Nullable @CallbackExecutor Executor executor, + @Nullable DeviceStateRequest.Callback callback) { + mGlobal.requestState(request, callback, executor); + } + + /** + * Cancels a {@link DeviceStateRequest request} previously submitted with a call to + * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. + * <p> + * This method is noop if the {@code request} has not been submitted with a call to + * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. + * + * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} + * permission is not held. + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) + public void cancelRequest(@NonNull DeviceStateRequest request) { + mGlobal.cancelRequest(request); + } + + /** * Registers a listener to receive notifications about changes in device state. * - * @param listener the listener to register. * @param executor the executor to process notifications. + * @param listener the listener to register. * * @see DeviceStateListener */ - public void registerDeviceStateListener(@NonNull DeviceStateListener listener, - @NonNull Executor executor) { + public void addDeviceStateListener(@NonNull @CallbackExecutor Executor executor, + @NonNull DeviceStateListener listener) { mGlobal.registerDeviceStateListener(listener, executor); } /** * Unregisters a listener previously registered with - * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}. + * {@link #addDeviceStateListener(Executor, DeviceStateListener)}. */ - public void unregisterDeviceStateListener(@NonNull DeviceStateListener listener) { + public void removeDeviceStateListener(@NonNull DeviceStateListener listener) { mGlobal.unregisterDeviceStateListener(listener); } diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java index c8905038d056..b9ae88ea840f 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java @@ -20,9 +20,11 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.devicestate.DeviceStateManager.DeviceStateListener; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -67,6 +69,9 @@ public final class DeviceStateManagerGlobal { @GuardedBy("mLock") private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>(); + @GuardedBy("mLock") + private final ArrayMap<IBinder, DeviceStateRequestWrapper> mRequests = new ArrayMap<>(); + @Nullable @GuardedBy("mLock") private Integer mLastReceivedState; @@ -77,9 +82,84 @@ public final class DeviceStateManagerGlobal { } /** + * Returns the set of supported device states. + * + * @see DeviceStateManager#getSupportedStates() + */ + public int[] getSupportedStates() { + try { + return mDeviceStateManager.getSupportedDeviceStates(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Submits a {@link DeviceStateRequest request} to modify the device state. + * + * @see DeviceStateManager#requestState(DeviceStateRequest, + * Executor, DeviceStateRequest.Callback) + * @see DeviceStateRequest + */ + public void requestState(@NonNull DeviceStateRequest request, + @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) { + if (callback == null && executor != null) { + throw new IllegalArgumentException("Callback must be supplied with executor."); + } else if (executor == null && callback != null) { + throw new IllegalArgumentException("Executor must be supplied with callback."); + } + + synchronized (mLock) { + registerCallbackIfNeededLocked(); + + if (findRequestTokenLocked(request) != null) { + // This request has already been submitted. + return; + } + + // Add the request wrapper to the mRequests array before requesting the state as the + // callback could be triggered immediately if the mDeviceStateManager IBinder is in the + // same process as this instance. + IBinder token = new Binder(); + mRequests.put(token, new DeviceStateRequestWrapper(request, callback, executor)); + + try { + mDeviceStateManager.requestState(token, request.getState(), request.getFlags()); + } catch (RemoteException ex) { + mRequests.remove(token); + throw ex.rethrowFromSystemServer(); + } + } + } + + /** + * Cancels a {@link DeviceStateRequest request} previously submitted with a call to + * {@link #requestState(DeviceStateRequest, DeviceStateRequest.Callback, Executor)}. + * + * @see DeviceStateManager#cancelRequest(DeviceStateRequest) + */ + public void cancelRequest(@NonNull DeviceStateRequest request) { + synchronized (mLock) { + registerCallbackIfNeededLocked(); + + final IBinder token = findRequestTokenLocked(request); + if (token == null) { + // This request has not been submitted. + return; + } + + try { + mDeviceStateManager.cancelRequest(token); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + } + + /** * Registers a listener to receive notifications about changes in device state. * - * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor) + * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener) */ @VisibleForTesting(visibility = Visibility.PACKAGE) public void registerDeviceStateListener(@NonNull DeviceStateListener listener, @@ -112,7 +192,7 @@ public final class DeviceStateManagerGlobal { * Unregisters a listener previously registered with * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}. * - * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor) + * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener) */ @VisibleForTesting(visibility = Visibility.PACKAGE) public void unregisterDeviceStateListener(DeviceStateListener listener) { @@ -144,6 +224,17 @@ public final class DeviceStateManagerGlobal { return -1; } + @Nullable + private IBinder findRequestTokenLocked(@NonNull DeviceStateRequest request) { + for (int i = 0; i < mRequests.size(); i++) { + if (mRequests.valueAt(i).mRequest.equals(request)) { + return mRequests.keyAt(i); + } + } + return null; + } + + /** Handles a call from the server that the device state has changed. */ private void handleDeviceStateChanged(int newDeviceState) { ArrayList<DeviceStateListenerWrapper> listeners; synchronized (mLock) { @@ -156,11 +247,68 @@ public final class DeviceStateManagerGlobal { } } + /** + * Handles a call from the server that a request for the supplied {@code token} has become + * active. + */ + private void handleRequestActive(IBinder token) { + DeviceStateRequestWrapper request; + synchronized (mLock) { + request = mRequests.get(token); + } + if (request != null) { + request.notifyRequestActive(); + } + } + + /** + * Handles a call from the server that a request for the supplied {@code token} has become + * suspended. + */ + private void handleRequestSuspended(IBinder token) { + DeviceStateRequestWrapper request; + synchronized (mLock) { + request = mRequests.get(token); + } + if (request != null) { + request.notifyRequestSuspended(); + } + } + + /** + * Handles a call from the server that a request for the supplied {@code token} has become + * canceled. + */ + private void handleRequestCanceled(IBinder token) { + DeviceStateRequestWrapper request; + synchronized (mLock) { + request = mRequests.remove(token); + } + if (request != null) { + request.notifyRequestCanceled(); + } + } + private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub { @Override public void onDeviceStateChanged(int deviceState) { handleDeviceStateChanged(deviceState); } + + @Override + public void onRequestActive(IBinder token) { + handleRequestActive(token); + } + + @Override + public void onRequestSuspended(IBinder token) { + handleRequestSuspended(token); + } + + @Override + public void onRequestCanceled(IBinder token) { + handleRequestCanceled(token); + } } private static final class DeviceStateListenerWrapper { @@ -176,4 +324,43 @@ public final class DeviceStateManagerGlobal { mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState)); } } + + private static final class DeviceStateRequestWrapper { + private final DeviceStateRequest mRequest; + @Nullable + private final DeviceStateRequest.Callback mCallback; + @Nullable + private final Executor mExecutor; + + DeviceStateRequestWrapper(@NonNull DeviceStateRequest request, + @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) { + mRequest = request; + mCallback = callback; + mExecutor = executor; + } + + void notifyRequestActive() { + if (mCallback == null) { + return; + } + + mExecutor.execute(() -> mCallback.onRequestActivated(mRequest)); + } + + void notifyRequestSuspended() { + if (mCallback == null) { + return; + } + + mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest)); + } + + void notifyRequestCanceled() { + if (mCallback == null) { + return; + } + + mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest)); + } + } } diff --git a/core/java/android/hardware/devicestate/DeviceStateRequest.java b/core/java/android/hardware/devicestate/DeviceStateRequest.java new file mode 100644 index 000000000000..70f7002597ed --- /dev/null +++ b/core/java/android/hardware/devicestate/DeviceStateRequest.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.devicestate; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.TestApi; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; + +/** + * A request to alter the state of the device managed by {@link DeviceStateManager}. + * <p> + * Once constructed, a {@link DeviceStateRequest request} can be submitted with a call to + * {@link DeviceStateManager#requestState(DeviceStateRequest, Executor, + * DeviceStateRequest.Callback)}. + * <p> + * By default, the request is kept active until a call to + * {@link DeviceStateManager#cancelRequest(DeviceStateRequest)} or until one of the following + * occurs: + * <ul> + * <li>Another processes submits a request succeeding this request in which case the request + * will be suspended until the interrupting request is canceled. + * <li>The requested state has become unsupported. + * <li>The process submitting the request dies. + * </ul> + * However, this behavior can be changed by setting flags on the request. For example, the + * {@link #FLAG_CANCEL_WHEN_BASE_CHANGES} flag will extend this behavior to also cancel the + * request whenever the base (non-override) device state changes. + * + * @see DeviceStateManager + * + * @hide + */ +@TestApi +public final class DeviceStateRequest { + /** + * Flag that indicates the request should be canceled automatically when the base + * (non-override) device state changes. Useful when the requestor only wants the request to + * remain active while the base state remains constant and automatically cancel when the user + * manipulates the device into a different state. + */ + public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1 << 0; + + /** @hide */ + @IntDef(prefix = {"FLAG_"}, flag = true, value = { + FLAG_CANCEL_WHEN_BASE_CHANGES, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RequestFlags {} + + /** + * Creates a new {@link Builder} for a {@link DeviceStateRequest}. Must be one of the supported + * states for the device which can be queried with a call to + * {@link DeviceStateManager#getSupportedStates()}. + * + * @param requestedState the device state being requested. + */ + @NonNull + public static Builder newBuilder(int requestedState) { + return new Builder(requestedState); + } + + /** + * Builder for {@link DeviceStateRequest}. An instance can be obtained through + * {@link #newBuilder(int)}. + */ + public static final class Builder { + private final int mRequestedState; + private int mFlags; + + private Builder(int requestedState) { + mRequestedState = requestedState; + } + + /** + * Sets the flag bits provided within {@code flags} with all other bits remaining + * unchanged. + */ + @NonNull + public Builder setFlags(@RequestFlags int flags) { + mFlags |= flags; + return this; + } + + /** + * Returns a new {@link DeviceStateRequest} object whose state matches the state set on the + * builder. + */ + @NonNull + public DeviceStateRequest build() { + return new DeviceStateRequest(mRequestedState, mFlags); + } + } + + /** Callback to track the status of a request. */ + public interface Callback { + /** + * Called to indicate the request has become active and the device state will match the + * requested state. + * <p> + * Guaranteed to be called after a call to + * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)} with a state + * matching the requested state. + */ + default void onRequestActivated(@NonNull DeviceStateRequest request) {} + + /** + * Called to indicate the request has been temporarily suspended. + * <p> + * Guaranteed to be called before a call to + * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}. + */ + default void onRequestSuspended(@NonNull DeviceStateRequest request) {} + + /** + * Called to indicate the request has been canceled. The request can be resubmitted with + * another call to {@link DeviceStateManager#requestState(DeviceStateRequest, Executor, + * DeviceStateRequest.Callback)}. + * <p> + * Guaranteed to be called before a call to + * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}. + * <p> + * Note: A call to {@link #onRequestSuspended(DeviceStateRequest)} is not guaranteed to + * occur before this method. + */ + default void onRequestCanceled(@NonNull DeviceStateRequest request) {} + } + + private final int mRequestedState; + @RequestFlags + private final int mFlags; + + private DeviceStateRequest(int requestedState, @RequestFlags int flags) { + mRequestedState = requestedState; + mFlags = flags; + } + + public int getState() { + return mRequestedState; + } + + @RequestFlags + public int getFlags() { + return mFlags; + } +} diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl index a157b3311ca5..323ad21e4884 100644 --- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl +++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl @@ -20,5 +20,45 @@ import android.hardware.devicestate.IDeviceStateManagerCallback; /** @hide */ interface IDeviceStateManager { + /** + * Registers a callback to receive notifications from the device state manager. Only one + * callback can be registered per-process. + * <p> + * As the callback mechanism is used to alert the caller of changes to request status a callback + * <b>MUST</b> be registered before calling {@link #requestState(IBinder, int, int)} or + * {@link #cancelRequest(IBinder)}. Otherwise an exception will be thrown. + * + * @throws SecurityException if a callback is already registered for the calling process. + */ void registerCallback(in IDeviceStateManagerCallback callback); + + /** Returns the array of supported device state identifiers. */ + int[] getSupportedDeviceStates(); + + /** + * Requests that the device enter the supplied {@code state}. A callback <b>MUST</b> have been + * previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a + * call to this method. + * + * @param token the request token previously registered with + * {@link #requestState(IBinder, int, int)} + * + * @throws IllegalStateException if a callback has not yet been registered for the calling + * process. + * @throws IllegalStateException if the supplied {@code token} has already been registered. + * @throws IllegalArgumentException if the supplied {@code state} is not supported. + */ + void requestState(IBinder token, int state, int flags); + + /** + * Cancels a request previously submitted with a call to + * {@link #requestState(IBinder, int, int)}. + * + * @param token the request token previously registered with + * {@link #requestState(IBinder, int, int)} + * + * @throws IllegalStateException if the supplied {@code token} has not been previously + * requested. + */ + void cancelRequest(IBinder token); } diff --git a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl index d1c581361b62..ee2a071741ef 100644 --- a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl +++ b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl @@ -18,5 +18,42 @@ package android.hardware.devicestate; /** @hide */ interface IDeviceStateManagerCallback { + /** + * Called in response to a change in device state. Guaranteed to be called once with the initial + * value on registration of the callback. + * + * @param deviceState the new state of the device. + */ oneway void onDeviceStateChanged(int deviceState); + + /** + * Called to notify the callback that a request has become active. Guaranteed to be called + * after a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming active + * resulted in a device state change. + * + * @param token the request token previously registered with + * {@link IDeviceStateManager#requestState(IBinder, int, int)} + */ + oneway void onRequestActive(IBinder token); + + /** + * Called to notify the callback that a request has become suspended. Guaranteed to be called + * before a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming + * suspended resulted in a device state change. + * + * @param token the request token previously registered with + * {@link IDeviceStateManager#requestState(IBinder, int, int)} + */ + oneway void onRequestSuspended(IBinder token); + + /** + * Called to notify the callback that a request has become canceled. No further callbacks will + * be triggered for this request. Guaranteed to be called before a subsequent call to + * {@link #onDeviceStateChanged(int)} if the request becoming canceled resulted in a device + * state change. + * + * @param token the request token previously registered with + * {@link IDeviceStateManager#requestState(IBinder, int, int)} + */ + oneway void onRequestCanceled(IBinder token); } diff --git a/core/java/android/hardware/face/FaceAuthenticationFrame.java b/core/java/android/hardware/face/FaceAuthenticationFrame.java new file mode 100644 index 000000000000..f39d63411825 --- /dev/null +++ b/core/java/android/hardware/face/FaceAuthenticationFrame.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.face; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Data model for a frame captured during face authentication. + * + * @hide + */ +public final class FaceAuthenticationFrame implements Parcelable { + @NonNull private final FaceDataFrame mData; + + /** + * Data model for a frame captured during face authentication. + * + * @param data Information about the current frame. + */ + public FaceAuthenticationFrame(@NonNull FaceDataFrame data) { + mData = data; + } + + /** + * @return Information about the current frame. + */ + @NonNull + public FaceDataFrame getData() { + return mData; + } + + private FaceAuthenticationFrame(@NonNull Parcel source) { + mData = source.readParcelable(FaceDataFrame.class.getClassLoader()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mData, flags); + } + + public static final Creator<FaceAuthenticationFrame> CREATOR = + new Creator<FaceAuthenticationFrame>() { + + @Override + public FaceAuthenticationFrame createFromParcel(Parcel source) { + return new FaceAuthenticationFrame(source); + } + + @Override + public FaceAuthenticationFrame[] newArray(int size) { + return new FaceAuthenticationFrame[size]; + } + }; +} diff --git a/core/java/android/hardware/face/FaceDataFrame.java b/core/java/android/hardware/face/FaceDataFrame.java new file mode 100644 index 000000000000..3a0e09b70b50 --- /dev/null +++ b/core/java/android/hardware/face/FaceDataFrame.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.face; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}. + * + * @hide + */ +public final class FaceDataFrame implements Parcelable { + private final int mAcquiredInfo; + private final int mVendorCode; + private final float mPan; + private final float mTilt; + private final float mDistance; + private final boolean mIsCancellable; + + /** + * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}. + * + * @param acquiredInfo An integer corresponding to a known acquired message. + * @param vendorCode An integer representing a custom vendor-specific message. Ignored unless + * {@code acquiredInfo} is {@code FACE_ACQUIRED_VENDOR}. + * @param pan The horizontal pan of the detected face. Values in the range [-1, 1] indicate a + * good capture. + * @param tilt The vertical tilt of the detected face. Values in the range [-1, 1] indicate a + * good capture. + * @param distance The distance of the detected face from the device. Values in the range + * [-1, 1] indicate a good capture. + * @param isCancellable Whether the ongoing face operation should be canceled. + */ + public FaceDataFrame( + int acquiredInfo, + int vendorCode, + float pan, + float tilt, + float distance, + boolean isCancellable) { + mAcquiredInfo = acquiredInfo; + mVendorCode = vendorCode; + mPan = pan; + mTilt = tilt; + mDistance = distance; + mIsCancellable = isCancellable; + } + + /** + * @return An integer corresponding to a known acquired message. + * + * @see android.hardware.biometrics.BiometricFaceConstants + */ + public int getAcquiredInfo() { + return mAcquiredInfo; + } + + /** + * @return An integer representing a custom vendor-specific message. Ignored unless + * {@code acquiredInfo} is {@link + * android.hardware.biometrics.BiometricFaceConstants#FACE_ACQUIRED_VENDOR}. + * + * @see android.hardware.biometrics.BiometricFaceConstants + */ + public int getVendorCode() { + return mVendorCode; + } + + /** + * @return The horizontal pan of the detected face. Values in the range [-1, 1] indicate a good + * capture. + */ + public float getPan() { + return mPan; + } + + /** + * @return The vertical tilt of the detected face. Values in the range [-1, 1] indicate a good + * capture. + */ + public float getTilt() { + return mTilt; + } + + /** + * @return The distance of the detected face from the device. Values in the range [-1, 1] + * indicate a good capture. + */ + public float getDistance() { + return mDistance; + } + + /** + * @return Whether the ongoing face operation should be canceled. + */ + public boolean isCancellable() { + return mIsCancellable; + } + + private FaceDataFrame(@NonNull Parcel source) { + mAcquiredInfo = source.readInt(); + mVendorCode = source.readInt(); + mPan = source.readFloat(); + mTilt = source.readFloat(); + mDistance = source.readFloat(); + mIsCancellable = source.readBoolean(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mAcquiredInfo); + dest.writeInt(mVendorCode); + dest.writeFloat(mPan); + dest.writeFloat(mTilt); + dest.writeFloat(mDistance); + dest.writeBoolean(mIsCancellable); + } + + public static final Creator<FaceDataFrame> CREATOR = new Creator<FaceDataFrame>() { + @Override + public FaceDataFrame createFromParcel(Parcel source) { + return new FaceDataFrame(source); + } + + @Override + public FaceDataFrame[] newArray(int size) { + return new FaceDataFrame[size]; + } + }; +} diff --git a/core/java/android/hardware/face/FaceEnrollCell.java b/core/java/android/hardware/face/FaceEnrollCell.java new file mode 100644 index 000000000000..8415419577e2 --- /dev/null +++ b/core/java/android/hardware/face/FaceEnrollCell.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.face; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A matrix cell, corresponding to a desired face image, that may be captured during enrollment. + * + * @hide + */ +public final class FaceEnrollCell implements Parcelable { + private final int mX; + private final int mY; + private final int mZ; + + /** + * A matrix cell, corresponding to a desired face image, that may be captured during enrollment. + * + * @param x The horizontal coordinate of this cell. + * @param y The vertical coordinate of this cell. + * @param z The depth coordinate of this cell. + */ + public FaceEnrollCell(int x, int y, int z) { + mX = x; + mY = y; + mZ = z; + } + + /** + * @return The horizontal coordinate of this cell. + */ + public int getX() { + return mX; + } + + /** + * @return The vertical coordinate of this cell. + */ + public int getY() { + return mY; + } + + /** + * @return The depth coordinate of this cell. + */ + public int getZ() { + return mZ; + } + + private FaceEnrollCell(@NonNull Parcel source) { + mX = source.readInt(); + mY = source.readInt(); + mZ = source.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mX); + dest.writeInt(mY); + dest.writeInt(mZ); + } + + public static final Creator<FaceEnrollCell> CREATOR = new Creator<FaceEnrollCell>() { + @Override + public FaceEnrollCell createFromParcel(Parcel source) { + return new FaceEnrollCell(source); + } + + @Override + public FaceEnrollCell[] newArray(int size) { + return new FaceEnrollCell[size]; + } + }; +} diff --git a/core/java/android/hardware/face/FaceEnrollFrame.java b/core/java/android/hardware/face/FaceEnrollFrame.java new file mode 100644 index 000000000000..551139d240a3 --- /dev/null +++ b/core/java/android/hardware/face/FaceEnrollFrame.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.face; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Data model for a frame captured during face enrollment. + * + * @hide + */ +public final class FaceEnrollFrame implements Parcelable { + @Nullable private final FaceEnrollCell mCell; + @FaceEnrollStage private final int mStage; + @NonNull private final FaceDataFrame mData; + + /** + * Data model for a frame captured during face enrollment. + * + * @param cell The cell captured during this frame of enrollment, if any. + * @param stage An integer representing the current stage of enrollment. + * @param data Information about the current frame. + */ + public FaceEnrollFrame( + @Nullable FaceEnrollCell cell, + @FaceEnrollStage int stage, + @NonNull FaceDataFrame data) { + mCell = cell; + mStage = stage; + mData = data; + } + + /** + * @return The cell captured during this frame of enrollment, if any. + */ + @Nullable + public FaceEnrollCell getCell() { + return mCell; + } + + /** + * @return An integer representing the current stage of enrollment. + */ + @FaceEnrollStage + public int getStage() { + return mStage; + } + + /** + * @return Information about the current frame. + */ + @NonNull + public FaceDataFrame getData() { + return mData; + } + + private FaceEnrollFrame(@NonNull Parcel source) { + mCell = source.readParcelable(FaceEnrollCell.class.getClassLoader()); + mStage = source.readInt(); + mData = source.readParcelable(FaceDataFrame.class.getClassLoader()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mCell, flags); + dest.writeInt(mStage); + dest.writeParcelable(mData, flags); + } + + public static final Creator<FaceEnrollFrame> CREATOR = new Creator<FaceEnrollFrame>() { + @Override + public FaceEnrollFrame createFromParcel(Parcel source) { + return new FaceEnrollFrame(source); + } + + @Override + public FaceEnrollFrame[] newArray(int size) { + return new FaceEnrollFrame[size]; + } + }; +} diff --git a/core/java/android/hardware/face/FaceEnrollStage.java b/core/java/android/hardware/face/FaceEnrollStage.java new file mode 100644 index 000000000000..03dba551aae2 --- /dev/null +++ b/core/java/android/hardware/face/FaceEnrollStage.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.face; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A stage that may occur during face enrollment. + * + * @hide + */ +@Retention(RetentionPolicy.SOURCE) +@IntDef({ + FaceEnrollStage.FIRST_FRAME_RECEIVED, + FaceEnrollStage.WAITING_FOR_CENTERING, + FaceEnrollStage.HOLD_STILL_IN_CENTER, + FaceEnrollStage.ENROLLING_MOVEMENT_1, + FaceEnrollStage.ENROLLING_MOVEMENT_2, + FaceEnrollStage.ENROLLMENT_FINISHED +}) +public @interface FaceEnrollStage { + /** + * Enrollment has just begun. No action is needed from the user yet. + */ + int FIRST_FRAME_RECEIVED = 0; + + /** + * The user must center their face in the frame. + */ + int WAITING_FOR_CENTERING = 1; + + /** + * The user must keep their face centered in the frame. + */ + int HOLD_STILL_IN_CENTER = 2; + + /** + * The user must follow a first set of movement instructions. + */ + int ENROLLING_MOVEMENT_1 = 3; + + /** + * The user must follow a second set of movement instructions. + */ + int ENROLLING_MOVEMENT_2 = 4; + + /** + * Enrollment has completed. No more action is needed from the user. + */ + int ENROLLMENT_FINISHED = 5; +} diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index d93286531465..188a2a47fca0 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -93,17 +93,26 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing private static final int MSG_UDFPS_POINTER_UP = 109; /** - * Request authentication with any single sensor. * @hide */ - public static final int SENSOR_ID_ANY = -1; + public static final int ENROLL_FIND_SENSOR = 1; + /** + * @hide + */ + public static final int ENROLL_ENROLL = 2; /** * @hide */ - @IntDef({SENSOR_ID_ANY}) + @IntDef({ENROLL_FIND_SENSOR, ENROLL_ENROLL}) @Retention(RetentionPolicy.SOURCE) - public @interface SensorId {} + public @interface EnrollReason {} + + /** + * Request authentication with any single sensor. + * @hide + */ + public static final int SENSOR_ID_ANY = -1; private IFingerprintService mService; private Context mContext; @@ -508,8 +517,8 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT}) public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, - @NonNull AuthenticationCallback callback, Handler handler, @SensorId int sensorId, - int userId) { + @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId) { + if (callback == null) { throw new IllegalArgumentException("Must supply an authentication callback"); } @@ -590,7 +599,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(MANAGE_FINGERPRINT) public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId, - EnrollmentCallback callback, boolean shouldLogMetrics) { + EnrollmentCallback callback, @EnrollReason int enrollReason) { if (userId == UserHandle.USER_CURRENT) { userId = getCurrentUserId(); } @@ -611,7 +620,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing try { mEnrollmentCallback = callback; mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver, - mContext.getOpPackageName(), shouldLogMetrics); + mContext.getOpPackageName(), enrollReason); } catch (RemoteException e) { Slog.w(TAG, "Remote exception in enroll: ", e); // Though this may not be a hardware issue, it will cause apps to give up or try @@ -653,15 +662,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(MANAGE_FINGERPRINT) public void generateChallenge(int userId, GenerateChallengeCallback callback) { - final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties = - getSensorPropertiesInternal(); - if (fingerprintSensorProperties.isEmpty()) { + final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor(); + if (sensorProps == null) { Slog.e(TAG, "No sensors"); return; } - - final int sensorId = fingerprintSensorProperties.get(0).sensorId; - generateChallenge(sensorId, userId, callback); + generateChallenge(sensorProps.sensorId, userId, callback); } /** @@ -681,18 +687,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(MANAGE_FINGERPRINT) public void revokeChallenge(int userId, long challenge) { - if (mService != null) try { - final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties = - getSensorPropertiesInternal(); - if (fingerprintSensorProperties.isEmpty()) { - Slog.e(TAG, "No sensors"); - return; + if (mService != null) { + try { + final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor(); + if (sensorProps == null) { + Slog.e(TAG, "No sensors"); + return; + } + mService.revokeChallenge(mToken, sensorProps.sensorId, userId, + mContext.getOpPackageName(), challenge); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } - final int sensorId = fingerprintSensorProperties.get(0).sensorId; - mService.revokeChallenge(mToken, sensorId, userId, mContext.getOpPackageName(), - challenge); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); } } @@ -1161,6 +1167,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } + @Nullable + private FingerprintSensorPropertiesInternal getFirstFingerprintSensor() { + final List<FingerprintSensorPropertiesInternal> allSensors = getSensorPropertiesInternal(); + return allSensors.isEmpty() ? null : allSensors.get(0); + } + private void cancelEnrollment() { if (mService != null) try { mService.cancelEnrollment(mToken); diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java index f097651e1894..663a70452b24 100644 --- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java +++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java @@ -109,9 +109,10 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna this.sensorLocationY = props[1]; this.sensorRadius = props[2]; } else { - this.sensorLocationX = 0; - this.sensorLocationY = 0; - this.sensorRadius = 0; + // Fake coordinates that could be used for the fake UDFPS mode. + this.sensorLocationX = 540; + this.sensorLocationY = 1636; + this.sensorRadius = 130; } } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 3657a83039ad..8888247e2823 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -78,7 +78,7 @@ interface IFingerprintService { // Start fingerprint enrollment void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver, - String opPackageName, boolean shouldLogMetrics); + String opPackageName, int enrollReason); // Cancel enrollment in progress void cancelEnrollment(IBinder token); diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl index c093489d4494..81c7d894ee09 100644 --- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl +++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl @@ -21,10 +21,11 @@ package android.hardware.fingerprint; */ oneway interface IUdfpsOverlayController { const int REASON_UNKNOWN = 0; - const int REASON_ENROLL = 1; - const int REASON_AUTH_BP = 2; // BiometricPrompt - const int REASON_AUTH_FPM_KEYGUARD = 3; // FingerprintManager usage from Keyguard - const int REASON_AUTH_FPM_OTHER = 4; // Other FingerprintManager usage + const int REASON_ENROLL_FIND_SENSOR = 1; + const int REASON_ENROLL_ENROLLING = 2; + const int REASON_AUTH_BP = 3; // BiometricPrompt + const int REASON_AUTH_FPM_KEYGUARD = 4; // FingerprintManager usage from Keyguard + const int REASON_AUTH_FPM_OTHER = 5; // Other FingerprintManager usage // Shows the overlay. void showUdfpsOverlay(int sensorId, int reason); diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index c69c47f80d01..eaa38f3e862c 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -26,6 +26,7 @@ import android.os.CombinedVibrationEffect; import android.hardware.input.IInputSensorEventListener; import android.hardware.input.InputSensorInfo; import android.os.IBinder; +import android.os.IVibratorStateListener; import android.os.VibrationEffect; import android.view.InputDevice; import android.view.InputEvent; @@ -92,6 +93,8 @@ interface IInputManager { void cancelVibrate(int deviceId, IBinder token); int[] getVibratorIds(int deviceId); boolean isVibrating(int deviceId); + boolean registerVibratorStateListener(int deviceId, in IVibratorStateListener listener); + boolean unregisterVibratorStateListener(int deviceId, in IVibratorStateListener listener); // Input device battery query. int getBatteryStatus(int deviceId); diff --git a/core/java/android/hardware/input/InputDeviceVibrator.java b/core/java/android/hardware/input/InputDeviceVibrator.java index c60d6ce46fdb..f4d8a65d54c6 100644 --- a/core/java/android/hardware/input/InputDeviceVibrator.java +++ b/core/java/android/hardware/input/InputDeviceVibrator.java @@ -18,10 +18,18 @@ package android.hardware.input; import android.annotation.CallbackExecutor; import android.annotation.NonNull; +import android.app.ActivityThread; +import android.content.Context; import android.os.Binder; +import android.os.IVibratorStateListener; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; import java.util.concurrent.Executor; @@ -29,12 +37,18 @@ import java.util.concurrent.Executor; * Vibrator implementation that communicates with the input device vibrators. */ final class InputDeviceVibrator extends Vibrator { + private static final String TAG = "InputDeviceVibrator"; + // mDeviceId represents InputDevice ID the vibrator belongs to private final int mDeviceId; private final int mVibratorId; private final Binder mToken; private final InputManager mInputManager; + @GuardedBy("mDelegates") + private final ArrayMap<OnVibratorStateChangedListener, + OnVibratorStateChangedListenerDelegate> mDelegates = new ArrayMap<>(); + InputDeviceVibrator(InputManager inputManager, int deviceId, int vibratorId) { mInputManager = inputManager; mDeviceId = deviceId; @@ -42,6 +56,23 @@ final class InputDeviceVibrator extends Vibrator { mToken = new Binder(); } + private class OnVibratorStateChangedListenerDelegate extends + IVibratorStateListener.Stub { + private final Executor mExecutor; + private final OnVibratorStateChangedListener mListener; + + OnVibratorStateChangedListenerDelegate(@NonNull OnVibratorStateChangedListener listener, + @NonNull Executor executor) { + mExecutor = executor; + mListener = listener; + } + + @Override + public void onVibrating(boolean isVibrating) { + mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating)); + } + } + @Override public boolean hasVibrator() { return true; @@ -52,25 +83,73 @@ final class InputDeviceVibrator extends Vibrator { return mInputManager.isVibrating(mDeviceId); } - /* TODO: b/161634264 Support Vibrator listener API in input devices */ + /** + * Adds a listener for vibrator state changes. Callbacks will be executed on the main thread. + * If the listener was previously added and not removed, this call will be ignored. + * + * @param listener listener to be added + */ @Override public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { - throw new UnsupportedOperationException( - "addVibratorStateListener not supported in InputDeviceVibrator"); + Preconditions.checkNotNull(listener); + Context context = ActivityThread.currentApplication(); + addVibratorStateListener(context.getMainExecutor(), listener); } + /** + * Adds a listener for vibrator state change. If the listener was previously added and not + * removed, this call will be ignored. + * + * @param listener Listener to be added. + * @param executor The {@link Executor} on which the listener's callbacks will be executed on. + */ @Override public void addVibratorStateListener( @NonNull @CallbackExecutor Executor executor, @NonNull OnVibratorStateChangedListener listener) { - throw new UnsupportedOperationException( - "addVibratorStateListener not supported in InputDeviceVibrator"); + Preconditions.checkNotNull(listener); + Preconditions.checkNotNull(executor); + + synchronized (mDelegates) { + // If listener is already registered, reject and return. + if (mDelegates.containsKey(listener)) { + Log.w(TAG, "Listener already registered."); + return; + } + + final OnVibratorStateChangedListenerDelegate delegate = + new OnVibratorStateChangedListenerDelegate(listener, executor); + if (!mInputManager.registerVibratorStateListener(mDeviceId, delegate)) { + Log.w(TAG, "Failed to register vibrate state listener"); + return; + } + mDelegates.put(listener, delegate); + + } } + /** + * Removes the listener for vibrator state changes. If the listener was not previously + * registered, this call will do nothing. + * + * @param listener Listener to be removed. + */ @Override public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { - throw new UnsupportedOperationException( - "removeVibratorStateListener not supported in InputDeviceVibrator"); + Preconditions.checkNotNull(listener); + + synchronized (mDelegates) { + // Check if the listener is registered, otherwise will return. + if (mDelegates.containsKey(listener)) { + final OnVibratorStateChangedListenerDelegate delegate = mDelegates.get(listener); + + if (!mInputManager.unregisterVibratorStateListener(mDeviceId, delegate)) { + Log.w(TAG, "Failed to unregister vibrate state listener"); + return; + } + mDelegates.remove(listener); + } + } } @Override diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 185c59d8ccfc..8a01c660ebd0 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -35,6 +35,7 @@ import android.os.Build; import android.os.CombinedVibrationEffect; import android.os.Handler; import android.os.IBinder; +import android.os.IVibratorStateListener; import android.os.InputEventInjectionSync; import android.os.Looper; import android.os.Message; @@ -1484,6 +1485,32 @@ public final class InputManager { } /** + * Register input device vibrator state listener + * + * @hide + */ + public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) { + try { + return mIm.registerVibratorStateListener(deviceId, listener); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Unregister input device vibrator state listener + * + * @hide + */ + public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) { + try { + return mIm.unregisterVibratorStateListener(deviceId, listener); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** * Gets a sensor manager service associated with an input device, always create a new instance. * @return The sensor manager, never null. * @hide diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 44a2e97e6f04..7e2be01feb01 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1732,7 +1732,7 @@ public class InputMethodService extends AbstractInputMethodService { // If app window has portrait orientation, regardless of what display orientation // is, IME shouldn't use fullscreen-mode. || (mInputEditorInfo.internalImeOptions - & EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT) != 0) { + & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) != 0) { return false; } return true; diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 84a2acc165c4..b016ed67c4d9 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -82,4 +82,5 @@ interface INetworkPolicyManager { boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork); boolean isUidRestrictedOnMeteredNetworks(int uid); + boolean checkUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered, boolean isBackgroundRestricted); } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index ed169e75bd37..3e6237d99011 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -464,6 +464,31 @@ public class NetworkPolicyManager { } /** + * Figure out if networking is blocked for a given set of conditions. + * + * This is used by ConnectivityService via passing stale copies of conditions, so it must not + * take any locks. + * + * @param uid The target uid. + * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService. + * @param isNetworkMetered True if the network is metered. + * @param isBackgroundRestricted True if data saver is enabled. + * + * @return true if networking is blocked for the UID under the specified conditions. + * + * @hide + */ + public boolean checkUidNetworkingBlocked(int uid, int uidRules, + boolean isNetworkMetered, boolean isBackgroundRestricted) { + try { + return mService.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered, + isBackgroundRestricted); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Check that the given uid is restricted from doing networking on metered networks. * * @param uid The target uid. diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java index 6a8e3f9c01f2..5e56164cc82c 100644 --- a/core/java/android/net/OemNetworkPreferences.java +++ b/core/java/android/net/OemNetworkPreferences.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ package android.net; import android.annotation.IntDef; import android.annotation.NonNull; +import android.os.Bundle; import android.os.Parcelable; -import android.util.SparseArray; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; /** @hide */ @@ -60,16 +60,16 @@ public final class OemNetworkPreferences implements Parcelable { public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4; @NonNull - private final SparseArray<List<String>> mNetworkMappings; + private final Bundle mNetworkMappings; @NonNull - public SparseArray<List<String>> getNetworkPreferences() { - return mNetworkMappings.clone(); + public Map<String, Integer> getNetworkPreferences() { + return convertToUnmodifiableMap(mNetworkMappings); } - private OemNetworkPreferences(@NonNull SparseArray<List<String>> networkMappings) { + private OemNetworkPreferences(@NonNull final Bundle networkMappings) { Objects.requireNonNull(networkMappings); - mNetworkMappings = networkMappings.clone(); + mNetworkMappings = (Bundle) networkMappings.clone(); } @Override @@ -99,26 +99,45 @@ public final class OemNetworkPreferences implements Parcelable { * @hide */ public static final class Builder { - private final SparseArray<List<String>> mNetworkMappings; + private final Bundle mNetworkMappings; public Builder() { - mNetworkMappings = new SparseArray<>(); + mNetworkMappings = new Bundle(); + } + + public Builder(@NonNull final OemNetworkPreferences preferences) { + Objects.requireNonNull(preferences); + mNetworkMappings = (Bundle) preferences.mNetworkMappings.clone(); } /** - * Add a network preference for a list of packages. + * Add a network preference for a given package. Previously stored values for the given + * package will be overwritten. * - * @param preference the desired network preference to use - * @param packages full package names (e.g.: "com.google.apps.contacts") for apps to use - * the given preference + * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app + * to use the given preference + * @param preference the desired network preference to use * @return The builder to facilitate chaining. */ @NonNull - public Builder addNetworkPreference(@OemNetworkPreference final int preference, - @NonNull List<String> packages) { - Objects.requireNonNull(packages); - mNetworkMappings.put(preference, - Collections.unmodifiableList(new ArrayList<>(packages))); + public Builder addNetworkPreference(@NonNull final String packageName, + @OemNetworkPreference final int preference) { + Objects.requireNonNull(packageName); + mNetworkMappings.putInt(packageName, preference); + return this; + } + + /** + * Remove a network preference for a given package. + * + * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app to + * remove a preference for. + * @return The builder to facilitate chaining. + */ + @NonNull + public Builder removeNetworkPreference(@NonNull final String packageName) { + Objects.requireNonNull(packageName); + mNetworkMappings.remove(packageName); return this; } @@ -131,6 +150,14 @@ public final class OemNetworkPreferences implements Parcelable { } } + private static Map<String, Integer> convertToUnmodifiableMap(@NonNull final Bundle bundle) { + final Map<String, Integer> networkPreferences = new HashMap<>(); + for (final String key : bundle.keySet()) { + networkPreferences.put(key, bundle.getInt(key)); + } + return Collections.unmodifiableMap(networkPreferences); + } + /** @hide */ @IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = { OEM_NETWORK_PREFERENCE_DEFAULT, @@ -168,7 +195,7 @@ public final class OemNetworkPreferences implements Parcelable { @Override public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { - dest.writeSparseArray(mNetworkMappings); + dest.writeBundle(mNetworkMappings); } @Override @@ -187,7 +214,7 @@ public final class OemNetworkPreferences implements Parcelable { @Override public OemNetworkPreferences createFromParcel(@NonNull android.os.Parcel in) { return new OemNetworkPreferences( - in.readSparseArray(getClass().getClassLoader())); + in.readBundle(getClass().getClassLoader())); } }; } diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index 7e50ebc419dd..2119e7b951cd 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -326,6 +326,19 @@ public final class UserHandle implements Parcelable { } /** + * Returns the uid that is composed from the userHandle and the appId. + * + * @param userHandle the UserHandle to compose the uid + * @param appId the AppId to compose the uid + * @return the uid that is composed from the userHandle and the appId + * @hide + */ + @SystemApi + public static int getUid(@NonNull UserHandle userHandle, @AppIdInt int appId) { + return getUid(userHandle.getIdentifier(), appId); + } + + /** * Returns the app id (or base uid) for a given uid, stripping out the user id from it. * @hide */ diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 77183ac21d09..ea1ce3728365 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1694,10 +1694,13 @@ public class UserManager { } /** - * @hide - * @return Whether the device is running in a headless system user mode. It means the headless - * user (system user) runs system services and system UI, but is not associated with any real - * person. Secondary users can be created to be associated with real person. + * Checks whether the device is running in a headless system user mode. + * + * <p>Headless system user mode means the {@link #isSystemUser() system user} runs system + * services and some system UI, but it is not associated with any real person and additional + * users must be created to be associated with real persons. + * + * @return whether the device is running in a headless system user mode. */ public static boolean isHeadlessSystemUserMode() { return RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER; diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 7e7057fe56cb..0ff68fc582d8 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -304,20 +304,16 @@ public final class IncrementalManager { } /** - * Called when a callback wants to stop listen to the loading progress of an installed package. - * Decrease the count of the callbacks on the associated to the corresponding storage. - * If the count becomes zero, unregister the storage listener. + * Called to stop all listeners from listening to loading progress of an installed package. * @param codePath Path of the installed package - * @return True if the package name and associated storage id are valid. False otherwise. */ - public boolean unregisterLoadingProgressCallback(@NonNull String codePath, - @NonNull IPackageLoadingProgressCallback callback) { + public void unregisterLoadingProgressCallbacks(@NonNull String codePath) { final IncrementalStorage storage = openStorage(codePath); if (storage == null) { // storage does not exist, package not installed - return false; + return; } - return mLoadingProgressCallbacks.unregisterCallback(storage, callback); + mLoadingProgressCallbacks.cleanUpCallbacks(storage); } private static class LoadingProgressCallbacks extends IStorageLoadingProgressListener.Stub { @@ -325,7 +321,6 @@ public final class IncrementalManager { private final SparseArray<RemoteCallbackList<IPackageLoadingProgressCallback>> mCallbacks = new SparseArray<>(); - // TODO(b/165841827): disable callbacks when app state changes to fully loaded public void cleanUpCallbacks(@NonNull IncrementalStorage storage) { final int storageId = storage.getId(); final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 9603f4d43d8b..a29fcb17b083 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -13580,28 +13580,6 @@ public final class Settings { public static final String POWER_BUTTON_VERY_LONG_PRESS = "power_button_very_long_press"; - - /** - * Keyguard should be on the left hand side of the screen, for wide screen layouts. - * - * @hide - */ - public static final int ONE_HANDED_KEYGUARD_SIDE_LEFT = 0; - - /** - * Keyguard should be on the right hand side of the screen, for wide screen layouts. - * - * @hide - */ - public static final int ONE_HANDED_KEYGUARD_SIDE_RIGHT = 1; - /** - * In one handed mode, which side the keyguard should be on. Allowable values are one of - * the ONE_HANDED_KEYGUARD_SIDE_* constants. - * - * @hide - */ - public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side"; - /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. diff --git a/core/java/android/rotationresolver/RotationResolverInternal.java b/core/java/android/rotationresolver/RotationResolverInternal.java index db879a7e3f8f..1f1a66c17d97 100644 --- a/core/java/android/rotationresolver/RotationResolverInternal.java +++ b/core/java/android/rotationresolver/RotationResolverInternal.java @@ -46,7 +46,6 @@ public abstract class RotationResolverInternal { * error is captured. {@link RotationResolverCallbackInternal} * @param proposedRotation the screen rotation that is proposed by the system. * @param currentRotation the current screen rotation. - * @param packageName the package name of the current activity that is running in foreground. * @param timeoutMillis the timeout in millisecond for the query. If the query doesn't get * fulfilled within this amount of time. It will be discarded and the * callback will receive a failure result code {@link @@ -55,8 +54,7 @@ public abstract class RotationResolverInternal { */ public abstract void resolveRotation(@NonNull RotationResolverCallbackInternal callback, @Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation, - String packageName, @DurationMillisLong long timeoutMillis, - @NonNull CancellationSignal cancellationSignal); + @DurationMillisLong long timeoutMillis, @NonNull CancellationSignal cancellationSignal); /** * Internal interfaces for the rotation resolver callback. diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index c39b8c5eb6d1..a79b197d3faa 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -130,6 +130,15 @@ public final class KeymasterDefs { public static final int KM_TAG_ASSOCIATED_DATA = Tag.ASSOCIATED_DATA; // KM_BYTES | 1000; public static final int KM_TAG_NONCE = Tag.NONCE; // KM_BYTES | 1001; public static final int KM_TAG_MAC_LENGTH = Tag.MAC_LENGTH; // KM_UINT | 1003; + public static final int KM_TAG_RESET_SINCE_ID_ROTATION = + Tag.RESET_SINCE_ID_ROTATION; // KM_BOOL | 1004 + public static final int KM_TAG_CONFIRMATION_TOKEN = Tag.CONFIRMATION_TOKEN; // KM_BYTES | 1005; + public static final int KM_TAG_CERTIFICATE_SERIAL = Tag.CERTIFICATE_SERIAL; // KM_UINT | 1006; + public static final int KM_TAG_CERTIFICATE_SUBJECT = Tag.CERTIFICATE_SUBJECT; // KM_UINT | 1007; + public static final int KM_TAG_CERTIFICATE_NOT_BEFORE = + Tag.CERTIFICATE_NOT_BEFORE; // KM_DATE | 1008; + public static final int KM_TAG_CERTIFICATE_NOT_AFTER = + Tag.CERTIFICATE_NOT_AFTER; // KM_DATE | 1009; // Algorithm values. public static final int KM_ALGORITHM_RSA = Algorithm.RSA; @@ -317,6 +326,10 @@ public final class KeymasterDefs { ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68; public static final int KM_ERROR_DEVICE_LOCKED = ErrorCode.DEVICE_LOCKED; // -72; + public static final int KM_ERROR_MISSING_NOT_BEFORE = + ErrorCode.MISSING_NOT_BEFORE; // -80; + public static final int KM_ERROR_MISSING_NOT_AFTER = + ErrorCode.MISSING_NOT_AFTER; // -80; public static final int KM_ERROR_UNIMPLEMENTED = ErrorCode.UNIMPLEMENTED; // -100; public static final int KM_ERROR_VERSION_MISMATCH = diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index 44daeff76997..2b7cb1db174a 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -41,7 +41,7 @@ oneway interface INotificationListener void onListenerHintsChanged(int hints); void onInterruptionFilterChanged(int interruptionFilter); - // companion device managers only + // companion device managers and assistants only void onNotificationChannelModification(String pkgName, in UserHandle user, in NotificationChannel channel, int modificationType); void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType); diff --git a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java index 4ebaa96f4be2..ad49ffd57dc2 100644 --- a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java +++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java @@ -87,7 +87,9 @@ public abstract class ResumeOnRebootService extends Service { * Implementation for wrapping the opaque blob used for resume-on-reboot prior to * reboot. The service should not assume any structure of the blob to be wrapped. The * implementation should wrap the opaque blob in a reasonable time or throw {@link IOException} - * if it's unable to complete the action. + * if it's unable to complete the action due to retry-able errors (e.g network errors) + * and {@link IllegalArgumentException} if {@code wrapBlob} fails due to fatal errors + * (e.g corrupted blob). * * @param blob The opaque blob with size on the order of 100 bytes. * @param lifeTimeInMillis The life time of the blob. This must be strictly enforced by the @@ -95,7 +97,8 @@ public abstract class ResumeOnRebootService extends Service { * this function after expiration should * fail. * @return Wrapped blob to be persisted across reboot with size on the order of 100 bytes. - * @throws IOException if the implementation is unable to wrap the blob successfully. + * @throws IOException if the implementation is unable to wrap the blob successfully due to + * retry-able errors. */ @NonNull public abstract byte[] onWrap(@NonNull byte[] blob, @DurationMillisLong long lifeTimeInMillis) @@ -106,12 +109,13 @@ public abstract class ResumeOnRebootService extends Service { * operation would happen after reboot during direct boot mode (i.e before device is unlocked * for the first time). The implementation should unwrap the wrapped blob in a reasonable time * and returns the result or throw {@link IOException} if it's unable to complete the action - * and {@link IllegalArgumentException} if {@code unwrapBlob} fails because the wrappedBlob is - * stale. + * due to retry-able errors (e.g network error) and {@link IllegalArgumentException} + * if {@code unwrapBlob} fails due to fatal errors (e.g stale or corrupted blob). * * @param wrappedBlob The wrapped blob with size on the order of 100 bytes. * @return Unwrapped blob used for resume-on-reboot with the size on the order of 100 bytes. - * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully. + * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully + * due to retry-able errors. */ @NonNull public abstract byte[] onUnwrap(@NonNull byte[] wrappedBlob) throws IOException; diff --git a/core/java/android/service/rotationresolver/RotationResolutionRequest.java b/core/java/android/service/rotationresolver/RotationResolutionRequest.java index 94a6052c246d..8e76e2fc9202 100644 --- a/core/java/android/service/rotationresolver/RotationResolutionRequest.java +++ b/core/java/android/service/rotationresolver/RotationResolutionRequest.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import android.view.Surface; /** * This class represents a request to an {@link RotationResolverService}. The request contains @@ -54,7 +55,7 @@ public final class RotationResolutionRequest implements Parcelable { mTimeoutMillis = timeoutMillis; } - public int getProposedRotation() { + @Surface.Rotation public int getProposedRotation() { return mProposedRotation; } diff --git a/core/java/android/service/rotationresolver/RotationResolverService.java b/core/java/android/service/rotationresolver/RotationResolverService.java index 593a642b4a38..604dd0ac8298 100644 --- a/core/java/android/service/rotationresolver/RotationResolverService.java +++ b/core/java/android/service/rotationresolver/RotationResolverService.java @@ -146,11 +146,8 @@ public abstract class RotationResolverService extends Service { } mPendingCallback = new RotationResolverCallbackWrapper(callback, this); mCancellationSignal = CancellationSignal.fromTransport(transport); - try { - onResolveRotation(request, mCancellationSignal, mPendingCallback); - } catch (UnsupportedOperationException e) { - reportFailures(callback, ROTATION_RESULT_FAILURE_CANCELLED); - } + + onResolveRotation(request, mCancellationSignal, mPendingCallback); } @MainThread diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java index 0123c368583c..a750b689ee02 100644 --- a/core/java/android/service/storage/ExternalStorageService.java +++ b/core/java/android/service/storage/ExternalStorageService.java @@ -158,7 +158,7 @@ public abstract class ExternalStorageService extends Service { * @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed * @param bytes number of bytes which need to be freed */ - public void onFreeCacheRequested(@NonNull UUID volumeUuid, @BytesLong long bytes) { + public void onFreeCache(@NonNull UUID volumeUuid, @BytesLong long bytes) throws IOException { throw new UnsupportedOperationException("onFreeCacheRequested not implemented"); } @@ -202,7 +202,7 @@ public abstract class ExternalStorageService extends Service { RemoteCallback callback) { mHandler.post(() -> { try { - onFreeCacheRequested(StorageManager.convert(volumeUuid), bytes); + onFreeCache(StorageManager.convert(volumeUuid), bytes); sendResult(sessionId, null /* throwable */, callback); } catch (Throwable t) { sendResult(sessionId, t, callback); diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java index f7710e6a82ce..ff03cc14e73b 100644 --- a/core/java/android/service/voice/VoiceInteractionServiceInfo.java +++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java @@ -18,6 +18,7 @@ package android.service.voice; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AppGlobals; import android.content.ComponentName; import android.content.pm.PackageManager; @@ -44,6 +45,7 @@ public class VoiceInteractionServiceInfo { private ServiceInfo mServiceInfo; private String mSessionService; private String mRecognitionService; + private String mHotwordDetectionService; private String mSettingsActivity; private boolean mSupportsAssist; private boolean mSupportsLaunchFromKeyguard; @@ -133,6 +135,8 @@ public class VoiceInteractionServiceInfo { false); mSupportsLocalInteraction = array.getBoolean(com.android.internal. R.styleable.VoiceInteractionService_supportsLocalInteraction, false); + mHotwordDetectionService = array.getString(com.android.internal.R.styleable + .VoiceInteractionService_hotwordDetectionService); array.recycle(); if (mSessionService == null) { mParseError = "No sessionService specified"; @@ -181,4 +185,9 @@ public class VoiceInteractionServiceInfo { public boolean getSupportsLocalInteraction() { return mSupportsLocalInteraction; } + + @Nullable + public String getHotwordDetectionService() { + return mHotwordDetectionService; + } } diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 16b45c30b69f..7f45b384ca5f 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -475,9 +475,8 @@ public class DynamicLayout extends Layout { mObjects.insertAt(0, dirs); - final int baseLength = mBase.length(); - // Update from 0 characters to whatever the real text is - reflow(mBase, 0, 0, baseLength); + // Update from 0 characters to whatever the displayed text is + reflow(mBase, 0, 0, mDisplay.length()); if (mBase instanceof Spannable) { if (mWatcher == null) @@ -485,6 +484,7 @@ public class DynamicLayout extends Layout { // Strip out any watchers for other DynamicLayouts. final Spannable sp = (Spannable) mBase; + final int baseLength = mBase.length(); final ChangeWatcher[] spans = sp.getSpans(0, baseLength, ChangeWatcher.class); for (int i = 0; i < spans.length; i++) { sp.removeSpan(spans[i]); diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 85911fffc1bf..f99d4300a848 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -977,6 +977,9 @@ public class StaticLayout extends Layout { calculateEllipsis(start, end, measured, widthStart, ellipsisWidth, ellipsize, j, textWidth, paint, forceEllipsis); + } else { + mLines[mColumns * j + ELLIPSIS_START] = 0; + mLines[mColumns * j + ELLIPSIS_COUNT] = 0; } } diff --git a/core/java/android/tracing/ITracingServiceProxy.aidl b/core/java/android/tracing/ITracingServiceProxy.aidl new file mode 100644 index 000000000000..4520db3915a2 --- /dev/null +++ b/core/java/android/tracing/ITracingServiceProxy.aidl @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.tracing; + +/** + * Binder interface for the TracingServiceProxy running in system_server. + * + * {@hide} + */ +interface ITracingServiceProxy +{ + /** + * Notifies system tracing app that a tracing session has ended. If a session is repurposed + * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but + * there is no buffer available to dump. + */ + oneway void notifyTraceSessionEnded(boolean sessionStolen); +} diff --git a/core/java/android/tracing/OWNERS b/core/java/android/tracing/OWNERS new file mode 100644 index 000000000000..f5de4eb05c54 --- /dev/null +++ b/core/java/android/tracing/OWNERS @@ -0,0 +1,2 @@ +cfijalkovich@google.com +carmenjackson@google.com diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 790773fd83c5..b22921233f05 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -65,7 +65,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true"); DEFAULT_FLAGS.put("settings_tether_all_in_one", "false"); - DEFAULT_FLAGS.put("settings_silky_home", "true"); + DEFAULT_FLAGS.put("settings_silky_home", "false"); DEFAULT_FLAGS.put("settings_contextual_home", "false"); DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false"); } diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java index 5ac95d49c1bb..c0d818774ba0 100644 --- a/core/java/android/uwb/RangingManager.java +++ b/core/java/android/uwb/RangingManager.java @@ -94,7 +94,8 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { synchronized (this) { if (!hasSession(sessionHandle)) { Log.w(TAG, - "onRangingOpened - received unexpected SessionHandle: " + sessionHandle); + "onRangingOpenedFailed - received unexpected SessionHandle: " + + sessionHandle); return; } @@ -124,7 +125,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { @RangingChangeReason int reason, PersistableBundle params) { synchronized (this) { if (!hasSession(sessionHandle)) { - Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: " + Log.w(TAG, "onRangingReconfigureFailed - received unexpected SessionHandle: " + sessionHandle); return; } diff --git a/core/java/android/uwb/SessionHandle.java b/core/java/android/uwb/SessionHandle.java index 928fcbdcf1c7..b23f5ad603ff 100644 --- a/core/java/android/uwb/SessionHandle.java +++ b/core/java/android/uwb/SessionHandle.java @@ -19,6 +19,8 @@ package android.uwb; import android.os.Parcel; import android.os.Parcelable; +import java.util.Objects; + /** * @hide */ @@ -73,6 +75,11 @@ public final class SessionHandle implements Parcelable { } @Override + public int hashCode() { + return Objects.hashCode(mId); + } + + @Override public String toString() { return "SessionHandle [id=" + mId + "]"; } diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 41680647ad57..0ba1dfee16f3 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -25,8 +25,8 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.app.KeyguardManager; -import android.app.WindowConfiguration; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; @@ -59,8 +59,12 @@ import java.util.List; * an application window, excluding the system decorations. The application display area may * be smaller than the real display area because the system subtracts the space needed * for decor elements such as the status bar. Use {@link WindowMetrics#getBounds()} to query the - * application window bounds. Generally, use {@link WindowManager#getCurrentWindowMetrics()} to - * query the metrics and perform UI-related actions.</li> + * application window bounds.</li> + * <li>The real display area specifies the part of the display that contains content + * including the system decorations. Even so, the real display area may be smaller than the + * physical size of the display if the window manager is emulating a smaller display + * using (adb shell wm size). Use the following methods to query the + * real display area: {@link #getRealSize}, {@link #getRealMetrics}.</li> * </ul> * </p><p> * A logical display does not necessarily represent a particular physical display device @@ -673,9 +677,9 @@ public final class Display { @UnsupportedAppUsage public DisplayAdjustments getDisplayAdjustments() { if (mResources != null) { - final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments(); - if (!mDisplayAdjustments.equals(currentAdjustments)) { - mDisplayAdjustments = new DisplayAdjustments(currentAdjustments); + final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments(); + if (!mDisplayAdjustments.equals(currentAdjustements)) { + mDisplayAdjustments = new DisplayAdjustments(currentAdjustements); } } @@ -1213,34 +1217,30 @@ public final class Display { } /** - * Provides the largest {@link Point outSize} an app may expect in the current system state, - * without subtracting any window decor. + * Gets the real size of the display without subtracting any window decor or + * applying any compatibility scale factors. * <p> - * The size describes the largest potential area the window might occupy. The size is adjusted - * based on the current rotation of the display. + * The size is adjusted based on the current rotation of the display. * </p><p> * The real size may be smaller than the physical size of the screen when the * window manager is emulating a smaller display (using adb shell wm size). - * </p> + * </p><p> + * In general, {@link #getRealSize(Point)} and {@link WindowManager#getMaximumWindowMetrics()} + * report the same bounds except that certain areas of the display may not be available to + * windows created in the {@link WindowManager}'s {@link Context}. + * + * For example, imagine a device which has a multi-task mode that limits windows to half of the + * screen. In this case, {@link WindowManager#getMaximumWindowMetrics()} reports the + * bounds of the screen half where the window is located, while {@link #getRealSize(Point)} + * still reports the bounds of the whole display. * * @param outSize Set to the real size of the display. + * + * @see WindowManager#getMaximumWindowMetrics() */ public void getRealSize(Point outSize) { synchronized (this) { updateDisplayInfoLocked(); - if (shouldReportMaxBounds()) { - final Rect bounds = mResources.getConfiguration() - .windowConfiguration.getMaxBounds(); - outSize.x = bounds.width(); - outSize.y = bounds.height(); - if (DEBUG) { - Log.d(TAG, "getRealSize determined from max bounds: " + outSize - + " for uid " + Process.myUid()); - } - // Skip adjusting by fixed rotation, since if it is necessary, the configuration - // should already reflect the expected rotation. - return; - } outSize.x = mDisplayInfo.logicalWidth; outSize.y = mDisplayInfo.logicalHeight; if (mMayAdjustByFixedRotation) { @@ -1250,11 +1250,9 @@ public final class Display { } /** - * Provides the largest {@link DisplayMetrics outMetrics} an app may expect in the current - * system state, without subtracting any window decor. + * Gets display metrics based on the real size of this display. * <p> - * The size describes the largest potential area the window might occupy. The size is adjusted - * based on the current rotation of the display. + * The size is adjusted based on the current rotation of the display. * </p><p> * The real size may be smaller than the physical size of the screen when the * window manager is emulating a smaller display (using adb shell wm size). @@ -1265,18 +1263,6 @@ public final class Display { public void getRealMetrics(DisplayMetrics outMetrics) { synchronized (this) { updateDisplayInfoLocked(); - if (shouldReportMaxBounds()) { - mDisplayInfo.getMaxBoundsMetrics(outMetrics, - CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, - mResources.getConfiguration()); - if (DEBUG) { - Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics - + " for uid " + Process.myUid()); - } - // Skip adjusting by fixed rotation, since if it is necessary, the configuration - // should already reflect the expected rotation. - return; - } mDisplayInfo.getLogicalMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); if (mMayAdjustByFixedRotation) { @@ -1286,20 +1272,6 @@ public final class Display { } /** - * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the - * display dimensions. The max bounds field may be smaller than the logical dimensions - * when apps need to be sandboxed. - * @return {@code true} when max bounds should be applied. - */ - private boolean shouldReportMaxBounds() { - if (mResources == null) { - return false; - } - final Configuration config = mResources.getConfiguration(); - return config != null && !config.windowConfiguration.getMaxBounds().isEmpty(); - } - - /** * Gets the state of the display, such as whether it is on or off. * * @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON}, diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java index 5d4a4e52975a..e6cd25275ca2 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -58,11 +58,11 @@ public abstract class DisplayEventReceiver { public static final int VSYNC_SOURCE_SURFACE_FLINGER = 1; /** - * Specifies to generate config changed events from Surface Flinger. + * Specifies to generate mode changed events from Surface Flinger. * <p> * Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h */ - public static final int EVENT_REGISTRATION_CONFIG_CHANGED_FLAG = 0x1; + public static final int EVENT_REGISTRATION_MODE_CHANGED_FLAG = 0x1; /** * Specifies to generate frame rate override events from Surface Flinger. @@ -197,14 +197,14 @@ public abstract class DisplayEventReceiver { } /** - * Called when a display config changed event is received. + * Called when a display mode changed event is received. * * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()} * timebase. * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair. - * @param configId The new config Id + * @param modeId The new mode Id */ - public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { + public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) { } /** @@ -273,8 +273,8 @@ public abstract class DisplayEventReceiver { // Called from native code. @SuppressWarnings("unused") - private void dispatchConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { - onConfigChanged(timestampNanos, physicalDisplayId, configId); + private void dispatchModeChanged(long timestampNanos, long physicalDisplayId, int modeId) { + onModeChanged(timestampNanos, physicalDisplayId, modeId); } // Called from native code. diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 8a445041a1f2..2a00b5a2e513 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -24,7 +24,6 @@ import static android.view.DisplayInfoProto.LOGICAL_WIDTH; import static android.view.DisplayInfoProto.NAME; import android.annotation.Nullable; -import android.app.WindowConfiguration; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -616,29 +615,11 @@ public final class DisplayInfo implements Parcelable { getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight); } - /** - * Populates {@code outMetrics} with details of the logical display. Bounds are limited - * by the logical size of the display. - * - * @param outMetrics the {@link DisplayMetrics} to be populated - * @param compatInfo the {@link CompatibilityInfo} to be applied - * @param configuration the {@link Configuration} - */ public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo, Configuration configuration) { getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight); } - /** - * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from - * {@link WindowConfiguration#getMaxBounds()} - */ - public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo, - Configuration configuration) { - Rect bounds = configuration.windowConfiguration.getMaxBounds(); - getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height()); - } - public int getNaturalWidth() { return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ? logicalWidth : logicalHeight; diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl index 423e23d2bc08..1f64fb8ca2ec 100644 --- a/core/java/android/view/IRemoteAnimationRunner.aidl +++ b/core/java/android/view/IRemoteAnimationRunner.aidl @@ -30,11 +30,15 @@ oneway interface IRemoteAnimationRunner { /** * Called when the process needs to start the remote animation. * + * @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values. * @param apps The list of apps to animate. + * @param wallpapers The list of wallpapers to animate. + * @param nonApps The list of non-app windows such as Bubbles to animate. * @param finishedCallback The callback to invoke when the animation is finished. */ @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) - void onAnimationStart(in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers, + void onAnimationStart(int transit, in RemoteAnimationTarget[] apps, + in RemoteAnimationTarget[] wallpapers, in RemoteAnimationTarget[] nonApps, in IRemoteAnimationFinishedCallback finishedCallback); /** diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index ae8afca9b5c5..62f4b864c7a8 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -124,16 +124,10 @@ interface IWindowManager * * @param token Token to be registered. * @param type Window type to be used with this token. - * @param options A bundle used to pass window-related options. * @param displayId The ID of the display where this token should be added. - * @param packageName The name of package to request to add window token. Could be {@code null} - * if callers holds the MANAGE_APP_TOKENS permission. - * @return {@link WindowManagerGlobal#ADD_OKAY} if the addition was successful, an error code - * otherwise. + * @param options A bundle used to pass window-related options. */ - int addWindowTokenWithOptions(IBinder token, int type, int displayId, in Bundle options, - String packageName); - void addWindowToken(IBinder token, int type, int displayId); + void addWindowToken(IBinder token, int type, int displayId, in Bundle options); /** * Remove window token on a specific display. * @@ -784,6 +778,10 @@ interface IWindowManager /** * Registers a listener for a {@link android.app.WindowContext} to handle configuration changes * from the server side. + * <p> + * Note that this API should be invoked after calling + * {@link android.app.WindowTokenClient#attachContext(WindowContext)} + * </p> * * @param clientToken the window context's token * @param type Window type of the window context diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java index 0939336132a8..6a34a1520fa3 100644 --- a/core/java/android/view/InsetsAnimationThreadControlRunner.java +++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java @@ -111,6 +111,9 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro mControl = new InsetsAnimationControlImpl(controls, frame, state, listener, types, mCallbacks, durationMs, interpolator, animationType, translator); InsetsAnimationThread.getHandler().post(() -> { + if (mControl.isCancelled()) { + return; + } Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types); listener.onReady(mControl, types); diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index fe6b6e4d6fd1..219190f554ea 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -604,13 +604,13 @@ public class InsetsState implements Parcelable { return Type.CAPTION_BAR; case ITYPE_IME: return Type.IME; - case ITYPE_TOP_GESTURES: - case ITYPE_BOTTOM_GESTURES: case ITYPE_TOP_MANDATORY_GESTURES: case ITYPE_BOTTOM_MANDATORY_GESTURES: case ITYPE_LEFT_MANDATORY_GESTURES: case ITYPE_RIGHT_MANDATORY_GESTURES: return Type.MANDATORY_SYSTEM_GESTURES; + case ITYPE_TOP_GESTURES: + case ITYPE_BOTTOM_GESTURES: case ITYPE_LEFT_GESTURES: case ITYPE_RIGHT_GESTURES: return Type.SYSTEM_GESTURES; diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 24bc30874318..f8c4d1587f22 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -928,7 +928,9 @@ public class Surface implements Parcelable { * seamless transition is one that doesn't have any visual interruptions, such as a black * screen for a second or two. True indicates that any frame rate changes caused by this * request should be seamless. False indicates that non-seamless refresh rates are also - * acceptable. + * acceptable. Non-seamless switches might be used when the benefit of matching the content's + * frame rate outweighs the cost of the transition, for example when displaying + * long-running video content. * * @throws IllegalArgumentException If frameRate or compatibility are invalid. */ diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index acd25077fb5a..6a629ca4e462 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -161,8 +161,8 @@ public final class SurfaceControl implements Parcelable { int L, int T, int R, int B); private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken, int width, int height); - private static native SurfaceControl.DisplayInfo nativeGetDisplayInfo(IBinder displayToken); - private static native SurfaceControl.DisplayConfig[] nativeGetDisplayConfigs( + private static native DisplayInfo nativeGetDisplayInfo(IBinder displayToken); + private static native DisplayMode[] nativeGetDisplayModes( IBinder displayToken); private static native DisplayedContentSamplingAttributes nativeGetDisplayedContentSamplingAttributes(IBinder displayToken); @@ -170,13 +170,13 @@ public final class SurfaceControl implements Parcelable { boolean enable, int componentMask, int maxFrames); private static native DisplayedContentSample nativeGetDisplayedContentSample( IBinder displayToken, long numFrames, long timestamp); - private static native int nativeGetActiveConfig(IBinder displayToken); - private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken, - SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs); - private static native SurfaceControl.DesiredDisplayConfigSpecs - nativeGetDesiredDisplayConfigSpecs(IBinder displayToken); + private static native int nativeGetActiveDisplayMode(IBinder displayToken); + private static native boolean nativeSetDesiredDisplayModeSpecs(IBinder displayToken, + DesiredDisplayModeSpecs desiredDisplayModeSpecs); + private static native DesiredDisplayModeSpecs + nativeGetDesiredDisplayModeSpecs(IBinder displayToken); private static native int[] nativeGetDisplayColorModes(IBinder displayToken); - private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries( + private static native DisplayPrimaries nativeGetDisplayNativePrimaries( IBinder displayToken); private static native int[] nativeGetCompositionDataspaces(); private static native int nativeGetActiveColorMode(IBinder displayToken); @@ -188,8 +188,6 @@ public final class SurfaceControl implements Parcelable { IBinder displayToken, int mode); private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject, long barrierObject, long frame); - private static native void nativeReparentChildren(long transactionObj, long nativeObject, - long newParentObject); private static native void nativeReparent(long transactionObj, long nativeObject, long newParentNativeObject); @@ -1745,11 +1743,11 @@ public final class SurfaceControl implements Parcelable { * * @hide */ - public static final class DisplayConfig { + public static final class DisplayMode { /** * Invalid display config id. */ - public static final int INVALID_DISPLAY_CONFIG_ID = -1; + public static final int INVALID_DISPLAY_MODE_ID = -1; public int width; public int height; @@ -1766,7 +1764,7 @@ public final class SurfaceControl implements Parcelable { * configs within the same group can be done seamlessly in most cases. * @see: android.hardware.graphics.composer@2.4::IComposerClient::Attribute::CONFIG_GROUP */ - public int configGroup; + public int group; @Override public String toString() { @@ -1777,7 +1775,7 @@ public final class SurfaceControl implements Parcelable { + ", refreshRate=" + refreshRate + ", appVsyncOffsetNanos=" + appVsyncOffsetNanos + ", presentationDeadlineNanos=" + presentationDeadlineNanos - + ", configGroup=" + configGroup + "}"; + + ", group=" + group + "}"; } } @@ -1804,21 +1802,21 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - public static SurfaceControl.DisplayConfig[] getDisplayConfigs(IBinder displayToken) { + public static DisplayMode[] getDisplayModes(IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } - return nativeGetDisplayConfigs(displayToken); + return nativeGetDisplayModes(displayToken); } /** * @hide */ - public static int getActiveConfig(IBinder displayToken) { + public static int getActiveDisplayMode(IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } - return nativeGetActiveConfig(displayToken); + return nativeGetActiveDisplayMode(displayToken); } /** @@ -1865,8 +1863,8 @@ public final class SurfaceControl implements Parcelable { * * @hide */ - public static final class DesiredDisplayConfigSpecs { - public int defaultConfig; + public static final class DesiredDisplayModeSpecs { + public int defaultMode; /** * The primary refresh rate range represents display manager's general guidance on the * display configs surface flinger will consider when switching refresh rates. Unless @@ -1891,16 +1889,16 @@ public final class SurfaceControl implements Parcelable { */ public boolean allowGroupSwitching; - public DesiredDisplayConfigSpecs() {} + public DesiredDisplayModeSpecs() {} - public DesiredDisplayConfigSpecs(DesiredDisplayConfigSpecs other) { + public DesiredDisplayModeSpecs(DesiredDisplayModeSpecs other) { copyFrom(other); } - public DesiredDisplayConfigSpecs(int defaultConfig, boolean allowGroupSwitching, + public DesiredDisplayModeSpecs(int defaultMode, boolean allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, float appRequestRefreshRateMax) { - this.defaultConfig = defaultConfig; + this.defaultMode = defaultMode; this.allowGroupSwitching = allowGroupSwitching; this.primaryRefreshRateMin = primaryRefreshRateMin; this.primaryRefreshRateMax = primaryRefreshRateMax; @@ -1910,14 +1908,14 @@ public final class SurfaceControl implements Parcelable { @Override public boolean equals(@Nullable Object o) { - return o instanceof DesiredDisplayConfigSpecs && equals((DesiredDisplayConfigSpecs) o); + return o instanceof DesiredDisplayModeSpecs && equals((DesiredDisplayModeSpecs) o); } /** * Tests for equality. */ - public boolean equals(DesiredDisplayConfigSpecs other) { - return other != null && defaultConfig == other.defaultConfig + public boolean equals(DesiredDisplayModeSpecs other) { + return other != null && defaultMode == other.defaultMode && primaryRefreshRateMin == other.primaryRefreshRateMin && primaryRefreshRateMax == other.primaryRefreshRateMax && appRequestRefreshRateMin == other.appRequestRefreshRateMin @@ -1932,8 +1930,8 @@ public final class SurfaceControl implements Parcelable { /** * Copies the supplied object's values to this object. */ - public void copyFrom(DesiredDisplayConfigSpecs other) { - defaultConfig = other.defaultConfig; + public void copyFrom(DesiredDisplayModeSpecs other) { + defaultMode = other.defaultMode; primaryRefreshRateMin = other.primaryRefreshRateMin; primaryRefreshRateMax = other.primaryRefreshRateMax; appRequestRefreshRateMin = other.appRequestRefreshRateMin; @@ -1944,7 +1942,7 @@ public final class SurfaceControl implements Parcelable { public String toString() { return String.format("defaultConfig=%d primaryRefreshRateRange=[%.0f %.0f]" + " appRequestRefreshRateRange=[%.0f %.0f]", - defaultConfig, primaryRefreshRateMin, primaryRefreshRateMax, + defaultMode, primaryRefreshRateMin, primaryRefreshRateMax, appRequestRefreshRateMin, appRequestRefreshRateMax); } } @@ -1952,25 +1950,31 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - public static boolean setDesiredDisplayConfigSpecs(IBinder displayToken, - SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs) { + public static boolean setDesiredDisplayModeSpecs(IBinder displayToken, + DesiredDisplayModeSpecs desiredDisplayModeSpecs) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } + if (desiredDisplayModeSpecs == null) { + throw new IllegalArgumentException("desiredDisplayModeSpecs must not be null"); + } + if (desiredDisplayModeSpecs.defaultMode < 0) { + throw new IllegalArgumentException("defaultMode must be non-negative"); + } - return nativeSetDesiredDisplayConfigSpecs(displayToken, desiredDisplayConfigSpecs); + return nativeSetDesiredDisplayModeSpecs(displayToken, desiredDisplayModeSpecs); } /** * @hide */ - public static SurfaceControl.DesiredDisplayConfigSpecs getDesiredDisplayConfigSpecs( + public static DesiredDisplayModeSpecs getDesiredDisplayModeSpecs( IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } - return nativeGetDesiredDisplayConfigSpecs(displayToken); + return nativeGetDesiredDisplayModeSpecs(displayToken); } /** @@ -2041,7 +2045,7 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - public static SurfaceControl.DisplayPrimaries getDisplayNativePrimaries( + public static DisplayPrimaries getDisplayNativePrimaries( IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); @@ -2970,15 +2974,6 @@ public final class SurfaceControl implements Parcelable { } /** - * @hide - */ - public Transaction reparentChildren(SurfaceControl sc, SurfaceControl newParent) { - checkPreconditions(sc); - nativeReparentChildren(mNativeObject, sc.mNativeObject, newParent.mNativeObject); - return this; - } - - /** * Re-parents a given layer to a new parent. Children inherit transform (position, scaling) * crop, visibility, and Z-ordering from their parents, as if the children were pixels within the * parent Surface. @@ -3252,7 +3247,10 @@ public final class SurfaceControl implements Parcelable { * interruptions, such as a black screen for a second or two. True * indicates that any frame rate changes caused by this request * should be seamless. False indicates that non-seamless refresh - * rates are also acceptable. + * rates are also acceptable. Non-seamless switches might be + * used when the benefit of matching the content's frame rate + * outweighs the cost of the transition, for example when + * displaying long-running video content. * @return This transaction object. */ @NonNull diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index f01964822897..18029af6a85e 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -162,7 +162,6 @@ public class SurfaceControlViewHost { @NonNull WindowlessWindowManager wwm, boolean useSfChoreographer) { mWm = wwm; mViewRoot = new ViewRootImpl(c, d, mWm, useSfChoreographer); - mViewRoot.forceDisableBLAST(); mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); } @@ -188,7 +187,6 @@ public class SurfaceControlViewHost { mWm = new WindowlessWindowManager(context.getResources().getConfiguration(), mSurfaceControl, hostToken); mViewRoot = new ViewRootImpl(context, display, mWm); - mViewRoot.forceDisableBLAST(); mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); } diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java index b10370aa5d4c..acbcbfad1a75 100644 --- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java +++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java @@ -85,12 +85,13 @@ public class SyncRtSurfaceTransactionApplier { for (int i = params.length - 1; i >= 0; i--) { SurfaceParams surfaceParams = params[i]; SurfaceControl surface = surfaceParams.surface; - if (frame > 0) { - t.deferTransactionUntil(surface, mTargetSc, frame); - } applyParams(t, surfaceParams, mTmpFloat9); } - t.apply(); + if (mTargetViewRootImpl != null) { + mTargetViewRootImpl.mergeWithNextTransaction(t, frame); + } else { + t.apply(); + } } public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 749c0dfbaaaa..e1ccc51c71e1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -8740,14 +8740,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Populates a {@link ViewStructure} for content capture. * - * <p>This method is called after a view is that is eligible for content capture - * (for example, if it {@link #isImportantForAutofill()}, an intelligence service is enabled for - * the user, and the activity rendering the view is enabled for content capture) is laid out and - * is visible. - * - * <p>The populated structure is then passed to the service through + * <p>This method is called after a view that is eligible for content capture + * (for example, if it {@link #isImportantForContentCapture()}, an intelligence service is + * enabled for the user, and the activity rendering the view is enabled for content capture) + * is laid out and is visible. The populated structure is then passed to the service through * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}. * + * <p>The default implementation of this method sets the most relevant properties based on + * related {@link View} methods, and views in the standard Android widgets library also + * override it to set their relevant properties. Therefore, if overriding this method, it + * is recommended to call {@code super.onProvideContentCaptureStructure()}. + * * <p><b>Note: </b>views that manage a virtual structure under this view must populate just * the node representing this view and return right away, then asynchronously report (not * necessarily in the UI thread) when the children nodes appear, disappear or have their text @@ -8755,7 +8758,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}, * {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and * {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence)} - * respectively. The structure for the a child must be created using + * respectively. The structure for a child must be created using * {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the * {@code autofillId} for a child can be obtained either through * {@code childStructure.getAutofillId()} or @@ -8900,7 +8903,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Called when assist structure is being retrieved from a view as part of * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} to - * generate additional virtual structure under this view. The defaullt implementation + * generate additional virtual structure under this view. The default implementation * uses {@link #getAccessibilityNodeProvider()} to try to generate this from the * view's virtual accessibility nodes, if any. You can override this for a more * optimal implementation providing this data. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 18ef80ce9772..56d98e78303e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -315,7 +315,7 @@ public final class ViewRootImpl implements ViewParent, * In that case we receive a call back from {@link ActivityThread} and this flag is used to * preserve the initial value. * - * @see #performConfigurationChange(Configuration, Configuration, boolean, int) + * @see #performConfigurationChange(MergedConfiguration, boolean, int) */ private boolean mForceNextConfigUpdate; @@ -10137,9 +10137,11 @@ public final class ViewRootImpl implements ViewParent, * Merges the transaction passed in with the next transaction in BLASTBufferQueue. This ensures * you can add transactions to the upcoming frame. */ - void mergeWithNextTransaction(Transaction t, long frameNumber) { + public void mergeWithNextTransaction(Transaction t, long frameNumber) { if (mBlastBufferQueue != null) { mBlastBufferQueue.mergeWithNextTransaction(t, frameNumber); + } else { + t.apply(); } } } diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index dd0ab6503a8a..47ac1ee5b339 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -116,14 +116,6 @@ public final class WindowManagerGlobal { */ public static final int RELAYOUT_INSETS_PENDING = 0x1; - /** - * Flag for relayout: the client may be currently using the current surface, - * so if it is to be destroyed as a part of the relayout the destroy must - * be deferred until later. The client will call performDeferredDestroy() - * when it is okay. - */ - public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2; - public static final int ADD_FLAG_IN_TOUCH_MODE = 0x1; public static final int ADD_FLAG_APP_VISIBLE = 0x2; public static final int ADD_FLAG_USE_TRIPLE_BUFFERING = 0x4; @@ -147,7 +139,6 @@ public final class WindowManagerGlobal { public static final int ADD_INVALID_DISPLAY = -9; public static final int ADD_INVALID_TYPE = -10; public static final int ADD_INVALID_USER = -11; - public static final int ADD_TOO_MANY_TOKENS = -12; @UnsupportedAppUsage private static WindowManagerGlobal sDefaultWindowManager; diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index b85f10799210..3aedda1a6bd3 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -71,7 +71,7 @@ public class WindowlessWindowManager implements IWindowSession { new HashMap<IBinder, ResizeCompleteCallback>(); private final SurfaceSession mSurfaceSession = new SurfaceSession(); - private final SurfaceControl mRootSurface; + protected final SurfaceControl mRootSurface; private final Configuration mConfiguration; private final IWindowSession mRealWm; private final IBinder mHostInputToken; @@ -126,7 +126,7 @@ public class WindowlessWindowManager implements IWindowSession { } } - protected void attachToParentSurface(SurfaceControl.Builder b) { + protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { b.setParent(mRootSurface); } @@ -140,10 +140,10 @@ public class WindowlessWindowManager implements IWindowSession { InsetsSourceControl[] outActiveControls) { final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession) .setFormat(attrs.format) - .setBufferSize(getSurfaceWidth(attrs), getSurfaceHeight(attrs)) + .setBLASTLayer() .setName(attrs.getTitle().toString()) .setCallsite("WindowlessWindowManager.addToDisplay"); - attachToParentSurface(b); + attachToParentSurface(window, b); final SurfaceControl sc = b.build(); if (((attrs.inputFeatures & @@ -162,7 +162,11 @@ public class WindowlessWindowManager implements IWindowSession { mStateForWindow.put(window.asBinder(), state); } - return WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE; + final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE | + WindowManagerGlobal.ADD_FLAG_USE_BLAST; + + // Include whether the window is in touch mode. + return isInTouchMode() ? res | WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE : res; } /** @@ -210,6 +214,26 @@ public class WindowlessWindowManager implements IWindowSession { return !PixelFormat.formatHasAlpha(attrs.format); } + private boolean isInTouchMode() { + try { + return WindowManagerGlobal.getWindowSession().getInTouchMode(); + } catch (RemoteException e) { + Log.e(TAG, "Unable to check if the window is in touch mode", e); + } + return false; + } + + /** Access to package members for SystemWindow leashing + * @hide + */ + protected IBinder getWindowBinder(View rootView) { + final ViewRootImpl root = rootView.getViewRootImpl(); + if (root == null) { + return null; + } + return root.mWindow.asBinder(); + } + /** @hide */ @Nullable protected SurfaceControl getSurfaceControl(View rootView) { @@ -254,8 +278,8 @@ public class WindowlessWindowManager implements IWindowSession { WindowManager.LayoutParams attrs = state.mParams; if (viewFlags == View.VISIBLE) { - t.setBufferSize(sc, getSurfaceWidth(attrs), getSurfaceHeight(attrs)) - .setOpaque(sc, isOpaque(attrs)).show(sc).apply(); + outSurfaceSize.set(getSurfaceWidth(attrs), getSurfaceHeight(attrs)); + t.setOpaque(sc, isOpaque(attrs)).show(sc).apply(); outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout"); } else { t.hide(sc).apply(); @@ -276,7 +300,8 @@ public class WindowlessWindowManager implements IWindowSession { } } - return 0; + // Include whether the window is in touch mode. + return isInTouchMode() ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0; } @Override diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 415b3a766d16..37220fe6870b 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -164,12 +164,12 @@ public class BaseInputConnection implements InputConnection { /** * Default implementation calls {@link #finishComposingText()} and - * {@code setImeTemporarilyConsumesInput(false)}. + * {@code setImeConsumesInput(false)}. */ @CallSuper public void closeConnection() { finishComposingText(); - setImeTemporarilyConsumesInput(false); + setImeConsumesInput(false); } /** diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index 2df75f6570db..bde4cb7c4fc3 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -299,7 +299,7 @@ public class EditorInfo implements InputType, Parcelable { * {@link EditorInfo} is using {@link Configuration#ORIENTATION_PORTRAIT} mode. * @hide */ - public static final int IME_FLAG_APP_WINDOW_PORTRAIT = 0x80000000; + public static final int IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT = 0x00000001; /** * Generic unspecified type for {@link #imeOptions}. @@ -321,7 +321,6 @@ public class EditorInfo implements InputType, Parcelable { * 1 1 IME_ACTION_NEXT * 11 IME_ACTION_DONE * 111 IME_ACTION_PREVIOUS - * 1 IME_FLAG_APP_WINDOW_PORTRAIT * 1 IME_FLAG_NO_PERSONALIZED_LEARNING * 1 IME_FLAG_NO_FULLSCREEN * 1 IME_FLAG_NAVIGATE_PREVIOUS @@ -356,7 +355,7 @@ public class EditorInfo implements InputType, Parcelable { * Masks for {@link internalImeOptions} * * <pre> - * 1 IME_FLAG_APP_WINDOW_PORTRAIT + * 1 IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT * |-------|-------|-------|-------|</pre> */ @@ -984,6 +983,7 @@ public class EditorInfo implements InputType, Parcelable { dest.writeInt(inputType); dest.writeInt(imeOptions); dest.writeString(privateImeOptions); + dest.writeInt(internalImeOptions); TextUtils.writeToParcel(actionLabel, dest, flags); dest.writeInt(actionId); dest.writeInt(initialSelStart); @@ -1019,6 +1019,7 @@ public class EditorInfo implements InputType, Parcelable { res.inputType = source.readInt(); res.imeOptions = source.readInt(); res.privateImeOptions = source.readString(); + res.internalImeOptions = source.readInt(); res.actionLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); res.actionId = source.readInt(); res.initialSelStart = source.readInt(); diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java index 6300320f5eb5..0ab4e05227ba 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java +++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java @@ -19,6 +19,10 @@ package android.view.inputmethod; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.LocaleList; @@ -69,7 +73,8 @@ public final class InlineSuggestionsRequest implements Parcelable { /** * The IME provided locales for the request. If non-empty, the inline suggestions should - * return languages from the supported locales. If not provided, it'll default to system locale. + * return languages from the supported locales. If not provided, it'll default to be empty if + * target SDK is S or above, and default to system locale otherwise. * * <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions * to have one locale to guarantee consistent UI rendering.</p> @@ -156,7 +161,18 @@ public final class InlineSuggestionsRequest implements Parcelable { return ActivityThread.currentPackageName(); } + /** + * The {@link InlineSuggestionsRequest#getSupportedLocales()} now returns empty locale list when + * it's not set, instead of the default system locale. + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) + private static final long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY = 169273070L; + private static LocaleList defaultSupportedLocales() { + if (CompatChanges.isChangeEnabled(IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY)) { + return LocaleList.getEmptyLocaleList(); + } return LocaleList.getDefault(); } @@ -189,7 +205,7 @@ public final class InlineSuggestionsRequest implements Parcelable { - // Code below generated by codegen v1.0.15. + // Code below generated by codegen v1.0.22. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -264,7 +280,8 @@ public final class InlineSuggestionsRequest implements Parcelable { /** * The IME provided locales for the request. If non-empty, the inline suggestions should - * return languages from the supported locales. If not provided, it'll default to system locale. + * return languages from the supported locales. If not provided, it'll default to be empty if + * target SDK is S or above, and default to system locale otherwise. * * <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions * to have one locale to guarantee consistent UI rendering.</p> @@ -522,7 +539,8 @@ public final class InlineSuggestionsRequest implements Parcelable { /** * The IME provided locales for the request. If non-empty, the inline suggestions should - * return languages from the supported locales. If not provided, it'll default to system locale. + * return languages from the supported locales. If not provided, it'll default to be empty if + * target SDK is S or above, and default to system locale otherwise. * * <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions * to have one locale to guarantee consistent UI rendering.</p> @@ -622,10 +640,10 @@ public final class InlineSuggestionsRequest implements Parcelable { } @DataClass.Generated( - time = 1595457701315L, - codegenVersion = "1.0.15", + time = 1612206506050L, + codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java", - inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index f3111bdc7471..34a60bbe7642 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -1004,20 +1004,19 @@ public interface InputConnection { @Nullable Bundle opts); /** - * Called by the input method to indicate that it temporarily consumes all input for itself, - * or no longer does so. + * Called by the input method to indicate that it consumes all input for itself, or no longer + * does so. * - * <p>Editors should reflect that they are temporarily not receiving input by hiding the - * cursor if {@code imeTemporarilyConsumesInput} is {@code true}, and resume showing the - * cursor if it is {@code false}. + * <p>Editors should reflect that they are not receiving input by hiding the cursor if + * {@code imeConsumesInput} is {@code true}, and resume showing the cursor if it is + * {@code false}. * - * @param imeTemporarilyConsumesInput {@code true} when the IME is temporarily consuming input - * and the cursor should be hidden, {@code false} when input to the editor resumes and the - * cursor should be shown again. + * @param imeConsumesInput {@code true} when the IME is consuming input and the cursor should be + * hidden, {@code false} when input to the editor resumes and the cursor should be shown again. * @return {@code true} on success, {@code false} if the input connection is no longer valid, or * the protocol is not supported. */ - default boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { + default boolean setImeConsumesInput(boolean imeConsumesInput) { return false; } } diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index b29149fc1fa0..b1501a4c035c 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -341,7 +341,7 @@ public class InputConnectionWrapper implements InputConnection { * @throws NullPointerException if the target is {@code null}. */ @Override - public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { - return mTarget.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput); + public boolean setImeConsumesInput(boolean imeConsumesInput) { + return mTarget.setImeConsumesInput(imeConsumesInput); } } diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java index 046f75fd37ac..2452e4c6d56a 100644 --- a/core/java/android/widget/CheckBox.java +++ b/core/java/android/widget/CheckBox.java @@ -18,6 +18,7 @@ package android.widget; import android.content.Context; import android.util.AttributeSet; +import android.widget.RemoteViews.RemoteView; /** * <p> @@ -52,6 +53,7 @@ import android.util.AttributeSet; * {@link android.R.styleable#View View Attributes} * </p> */ +@RemoteView public class CheckBox extends CompoundButton { public CheckBox(Context context) { this(context, null); diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 135ff9fcd989..63f8ee7528f2 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -27,11 +27,13 @@ import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; +import android.view.RemotableViewMethod; import android.view.SoundEffectConstants; import android.view.ViewDebug; import android.view.ViewHierarchyEncoder; @@ -275,6 +277,7 @@ public abstract class CompoundButton extends Button implements Checkable { * @param resId the resource identifier of the drawable * @attr ref android.R.styleable#CompoundButton_button */ + @RemotableViewMethod(asyncImpl = "setButtonDrawableAsync") public void setButtonDrawable(@DrawableRes int resId) { final Drawable d; if (resId != 0) { @@ -285,6 +288,12 @@ public abstract class CompoundButton extends Button implements Checkable { setButtonDrawable(d); } + /** @hide **/ + public Runnable setButtonDrawableAsync(@DrawableRes int resId) { + Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId); + return () -> setButtonDrawable(drawable); + } + /** * Sets a drawable as the compound button image. * @@ -336,6 +345,23 @@ public abstract class CompoundButton extends Button implements Checkable { } /** + * Sets the button of this CompoundButton to the specified Icon. + * + * @param icon an Icon holding the desired button, or {@code null} to clear + * the button + */ + @RemotableViewMethod(asyncImpl = "setButtonIconAsync") + public void setButtonIcon(@Nullable Icon icon) { + setButtonDrawable(icon == null ? null : icon.loadDrawable(getContext())); + } + + /** @hide **/ + public Runnable setButtonIconAsync(@Nullable Icon icon) { + Drawable button = icon == null ? null : icon.loadDrawable(getContext()); + return () -> setButtonDrawable(button); + } + + /** * Applies a tint to the button drawable. Does not modify the current tint * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. * <p> @@ -350,6 +376,7 @@ public abstract class CompoundButton extends Button implements Checkable { * @see #setButtonTintList(ColorStateList) * @see Drawable#setTintList(ColorStateList) */ + @RemotableViewMethod public void setButtonTintList(@Nullable ColorStateList tint) { mButtonTintList = tint; mHasButtonTint = true; @@ -394,6 +421,7 @@ public abstract class CompoundButton extends Button implements Checkable { * @see #getButtonTintMode() * @see Drawable#setTintBlendMode(BlendMode) */ + @RemotableViewMethod public void setButtonTintBlendMode(@Nullable BlendMode tintMode) { mButtonBlendMode = tintMode; mHasButtonBlendMode = true; diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java index a04d7c34c444..9b3503433e56 100644 --- a/core/java/android/widget/RadioButton.java +++ b/core/java/android/widget/RadioButton.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.content.Context; import android.util.AttributeSet; import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.RemoteViews.RemoteView; import com.android.internal.R; @@ -49,6 +50,7 @@ import com.android.internal.R; * {@link android.R.styleable#View View Attributes} * </p> */ +@RemoteView public class RadioButton extends CompoundButton { public RadioButton(Context context) { diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java index 4722fdc7818f..d445fdc01564 100644 --- a/core/java/android/widget/RadioGroup.java +++ b/core/java/android/widget/RadioGroup.java @@ -31,6 +31,7 @@ import android.view.ViewStructure; import android.view.accessibility.AccessibilityNodeInfo; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; +import android.widget.RemoteViews.RemoteView; import com.android.internal.R; @@ -59,6 +60,7 @@ import com.android.internal.R; * @see RadioButton * */ +@RemoteView public class RadioGroup extends LinearLayout { private static final String LOG_TAG = RadioGroup.class.getSimpleName(); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index b47a0acc4fae..dfef7ca825a1 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -132,6 +132,13 @@ import java.util.function.Consumer; * <li>{@link android.widget.TextClock}</li> * <li>{@link android.widget.TextView}</li> * </ul> + * <p>As of API 31, the following widgets and layouts may also be used:</p> + * <ul> + * <li>{@link android.widget.CheckBox}</li> + * <li>{@link android.widget.RadioButton}</li> + * <li>{@link android.widget.RadioGroup}</li> + * <li>{@link android.widget.Switch}</li> + * </ul> * <p>Descendants of these classes are not supported.</p> */ public class RemoteViews implements Parcelable, Filter { @@ -185,6 +192,8 @@ public class RemoteViews implements Parcelable, Filter { private static final int REMOVE_FROM_PARENT_ACTION_TAG = 23; private static final int RESOURCE_REFLECTION_ACTION_TAG = 24; private static final int COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG = 25; + private static final int SET_COMPOUND_BUTTON_CHECKED_TAG = 26; + private static final int SET_RADIO_GROUP_CHECKED = 27; /** @hide **/ @IntDef(prefix = "MARGIN_", value = { @@ -2552,6 +2561,87 @@ public class RemoteViews implements Parcelable, Filter { } } + private static class SetCompoundButtonCheckedAction extends Action { + + private final boolean mChecked; + + SetCompoundButtonCheckedAction(@IdRes int viewId, boolean checked) { + this.viewId = viewId; + mChecked = checked; + } + + SetCompoundButtonCheckedAction(Parcel in) { + viewId = in.readInt(); + mChecked = in.readBoolean(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(viewId); + dest.writeBoolean(mChecked); + } + + @Override + public void apply(View root, ViewGroup rootParent, OnClickHandler handler) + throws ActionException { + final View target = root.findViewById(viewId); + if (target == null) return; + + if (!(target instanceof CompoundButton)) { + Log.w(LOG_TAG, "Cannot set checked to view " + + viewId + " because it is not a CompoundButton"); + return; + } + + ((CompoundButton) target).setChecked(mChecked); + } + + @Override + public int getActionTag() { + return SET_COMPOUND_BUTTON_CHECKED_TAG; + } + } + + private static class SetRadioGroupCheckedAction extends Action { + + @IdRes private final int mCheckedId; + + SetRadioGroupCheckedAction(@IdRes int viewId, @IdRes int checkedId) { + this.viewId = viewId; + mCheckedId = checkedId; + } + + SetRadioGroupCheckedAction(Parcel in) { + viewId = in.readInt(); + mCheckedId = in.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(viewId); + dest.writeInt(mCheckedId); + } + + @Override + public void apply(View root, ViewGroup rootParent, OnClickHandler handler) + throws ActionException { + final View target = root.findViewById(viewId); + if (target == null) return; + + if (!(target instanceof RadioGroup)) { + Log.w(LOG_TAG, "Cannot check " + viewId + " because it's not a RadioGroup"); + return; + } + + ((RadioGroup) target).check(mCheckedId); + } + + @Override + public int getActionTag() { + return SET_RADIO_GROUP_CHECKED; + } + } + /** * Create a new RemoteViews object that will display the views contained * in the specified layout file. @@ -2766,6 +2856,10 @@ public class RemoteViews implements Parcelable, Filter { return new ResourceReflectionAction(parcel); case COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG: return new ComplexUnitDimensionReflectionAction(parcel); + case SET_COMPOUND_BUTTON_CHECKED_TAG: + return new SetCompoundButtonCheckedAction(parcel); + case SET_RADIO_GROUP_CHECKED: + return new SetRadioGroupCheckedAction(parcel); default: throw new ActionException("Tag " + tag + " not found"); } @@ -3846,6 +3940,26 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to calling {@link android.widget.CompoundButton#setChecked(boolean)}. + * + * @param viewId The id of the view whose property to set. + * @param checked true to check the button, false to uncheck it. + */ + public void setCompoundButtonChecked(@IdRes int viewId, boolean checked) { + addAction(new SetCompoundButtonCheckedAction(viewId, checked)); + } + + /** + * Equivalent to calling {@link android.widget.RadioGroup#check(int)}. + * + * @param viewId The id of the view whose property to set. + * @param checkedId The unique id of the radio button to select in the group. + */ + public void setRadioGroupChecked(@IdRes int viewId, @IdRes int checkedId) { + addAction(new SetRadioGroupCheckedAction(viewId, checkedId)); + } + + /** * Provides an alternate layout ID, which can be used to inflate this view. This layout will be * used by the host when the widgets displayed on a light-background where foreground elements * and text can safely draw using a dark color without any additional background protection. diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 3295fd2ea1c3..d3600ef9f557 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -35,6 +35,7 @@ import android.graphics.Rect; import android.graphics.Region.Op; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.os.Build; import android.os.Build.VERSION_CODES; import android.text.Layout; @@ -48,12 +49,14 @@ import android.util.FloatProperty; import android.util.MathUtils; import android.view.Gravity; import android.view.MotionEvent; +import android.view.RemotableViewMethod; import android.view.SoundEffectConstants; import android.view.VelocityTracker; import android.view.ViewConfiguration; import android.view.ViewStructure; import android.view.accessibility.AccessibilityEvent; import android.view.inspector.InspectableProperty; +import android.widget.RemoteViews.RemoteView; import com.android.internal.R; @@ -84,6 +87,7 @@ import com.android.internal.R; * @attr ref android.R.styleable#Switch_thumbTextPadding * @attr ref android.R.styleable#Switch_track */ +@RemoteView public class Switch extends CompoundButton { private static final int THUMB_ANIMATION_DURATION = 250; @@ -441,6 +445,7 @@ public class Switch extends CompoundButton { * * @attr ref android.R.styleable#Switch_switchPadding */ + @RemotableViewMethod public void setSwitchPadding(int pixels) { mSwitchPadding = pixels; requestLayout(); @@ -466,6 +471,7 @@ public class Switch extends CompoundButton { * * @attr ref android.R.styleable#Switch_switchMinWidth */ + @RemotableViewMethod public void setSwitchMinWidth(int pixels) { mSwitchMinWidth = pixels; requestLayout(); @@ -491,6 +497,7 @@ public class Switch extends CompoundButton { * * @attr ref android.R.styleable#Switch_thumbTextPadding */ + @RemotableViewMethod public void setThumbTextPadding(int pixels) { mThumbTextPadding = pixels; requestLayout(); @@ -533,10 +540,17 @@ public class Switch extends CompoundButton { * * @attr ref android.R.styleable#Switch_track */ + @RemotableViewMethod(asyncImpl = "setTrackResourceAsync") public void setTrackResource(@DrawableRes int resId) { setTrackDrawable(getContext().getDrawable(resId)); } + /** @hide **/ + public Runnable setTrackResourceAsync(@DrawableRes int resId) { + Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId); + return () -> setTrackDrawable(drawable); + } + /** * Get the drawable used for the track that the switch slides within. * @@ -550,6 +564,23 @@ public class Switch extends CompoundButton { } /** + * Set the drawable used for the track that the switch slides within to the specified Icon. + * + * @param icon an Icon holding the desired track, or {@code null} to clear + * the track + */ + @RemotableViewMethod(asyncImpl = "setTrackIconAsync") + public void setTrackIcon(@Nullable Icon icon) { + setTrackDrawable(icon == null ? null : icon.loadDrawable(getContext())); + } + + /** @hide **/ + public Runnable setTrackIconAsync(@Nullable Icon icon) { + Drawable track = icon == null ? null : icon.loadDrawable(getContext()); + return () -> setTrackDrawable(track); + } + + /** * Applies a tint to the track drawable. Does not modify the current * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default. * <p> @@ -563,6 +594,7 @@ public class Switch extends CompoundButton { * @see #getTrackTintList() * @see Drawable#setTintList(ColorStateList) */ + @RemotableViewMethod public void setTrackTintList(@Nullable ColorStateList tint) { mTrackTintList = tint; mHasTrackTint = true; @@ -607,6 +639,7 @@ public class Switch extends CompoundButton { * @see #getTrackTintMode() * @see Drawable#setTintBlendMode(BlendMode) */ + @RemotableViewMethod public void setTrackTintBlendMode(@Nullable BlendMode blendMode) { mTrackBlendMode = blendMode; mHasTrackTintMode = true; @@ -686,10 +719,17 @@ public class Switch extends CompoundButton { * * @attr ref android.R.styleable#Switch_thumb */ + @RemotableViewMethod(asyncImpl = "setThumbResourceAsync") public void setThumbResource(@DrawableRes int resId) { setThumbDrawable(getContext().getDrawable(resId)); } + /** @hide **/ + public Runnable setThumbResourceAsync(@DrawableRes int resId) { + Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId); + return () -> setThumbDrawable(drawable); + } + /** * Get the drawable used for the switch "thumb" - the piece that the user * can physically touch and drag along the track. @@ -704,6 +744,24 @@ public class Switch extends CompoundButton { } /** + * Set the drawable used for the switch "thumb" - the piece that the user + * can physically touch and drag along the track - to the specified Icon. + * + * @param icon an Icon holding the desired thumb, or {@code null} to clear + * the thumb + */ + @RemotableViewMethod(asyncImpl = "setThumbIconAsync") + public void setThumbIcon(@Nullable Icon icon) { + setThumbDrawable(icon == null ? null : icon.loadDrawable(getContext())); + } + + /** @hide **/ + public Runnable setThumbIconAsync(@Nullable Icon icon) { + Drawable track = icon == null ? null : icon.loadDrawable(getContext()); + return () -> setThumbDrawable(track); + } + + /** * Applies a tint to the thumb drawable. Does not modify the current * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default. * <p> @@ -717,6 +775,7 @@ public class Switch extends CompoundButton { * @see #getThumbTintList() * @see Drawable#setTintList(ColorStateList) */ + @RemotableViewMethod public void setThumbTintList(@Nullable ColorStateList tint) { mThumbTintList = tint; mHasThumbTint = true; @@ -761,6 +820,7 @@ public class Switch extends CompoundButton { * @see #getThumbTintMode() * @see Drawable#setTintBlendMode(BlendMode) */ + @RemotableViewMethod public void setThumbTintBlendMode(@Nullable BlendMode blendMode) { mThumbBlendMode = blendMode; mHasThumbTintMode = true; @@ -822,6 +882,7 @@ public class Switch extends CompoundButton { * * @attr ref android.R.styleable#Switch_splitTrack */ + @RemotableViewMethod public void setSplitTrack(boolean splitTrack) { mSplitTrack = splitTrack; invalidate(); @@ -852,6 +913,7 @@ public class Switch extends CompoundButton { * * @attr ref android.R.styleable#Switch_textOn */ + @RemotableViewMethod public void setTextOn(CharSequence textOn) { mTextOn = textOn; requestLayout(); @@ -875,6 +937,7 @@ public class Switch extends CompoundButton { * * @attr ref android.R.styleable#Switch_textOff */ + @RemotableViewMethod public void setTextOff(CharSequence textOff) { mTextOff = textOff; requestLayout(); @@ -889,6 +952,7 @@ public class Switch extends CompoundButton { * @param showText {@code true} to display on/off text * @attr ref android.R.styleable#Switch_showText */ + @RemotableViewMethod public void setShowText(boolean showText) { if (mShowText != showText) { mShowText = showText; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 8cfbca88c596..fe37c5350511 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -497,9 +497,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private TextUtils.TruncateAt mEllipsize; // A flag to indicate the cursor was hidden by IME. - private boolean mImeTemporarilyConsumesInput; + private boolean mImeIsConsumingInput; - // Whether cursor is visible without regard to {@link mImeTemporarilyConsumesInput}. + // Whether cursor is visible without regard to {@link mImeConsumesInput}. // {code true} is the default value. private boolean mCursorVisibleFromAttr = true; @@ -8750,7 +8750,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } if (getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT) { - outAttrs.internalImeOptions |= EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT; + outAttrs.internalImeOptions |= EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT; } if (isMultilineInputType(outAttrs.inputType)) { // Multi-line text editors should always show an enter key. @@ -10506,8 +10506,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Set whether the cursor is visible. The default is true. Note that this property only - * makes sense for editable TextView. If IME is temporarily consuming the input, the cursor will - * be always invisible, visibility will be updated as the last state when IME does not consume + * makes sense for editable TextView. If IME is consuming the input, the cursor will always be + * invisible, visibility will be updated as the last state when IME does not consume * the input anymore. * * @see #isCursorVisible() @@ -10521,20 +10521,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Sets the IME is temporarily consuming the input and make the cursor invisible if - * {@code imeTemporarilyConsumesInput} is {@code true}. Otherwise, make the cursor visible. + * Sets the IME is consuming the input and make the cursor invisible if {@code imeConsumesInput} + * is {@code true}. Otherwise, make the cursor visible. * - * @param imeTemporarilyConsumesInput {@code true} if IME is temporarily consuming the input + * @param imeConsumesInput {@code true} if IME is consuming the input * * @hide */ - public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { - mImeTemporarilyConsumesInput = imeTemporarilyConsumesInput; + public void setImeConsumesInput(boolean imeConsumesInput) { + mImeIsConsumingInput = imeConsumesInput; updateCursorVisibleInternal(); } private void updateCursorVisibleInternal() { - boolean visible = mCursorVisibleFromAttr && !mImeTemporarilyConsumesInput; + boolean visible = mCursorVisibleFromAttr && !mImeIsConsumingInput; if (visible && mEditor == null) return; // visible is the default value with no edit data createEditorIfNeeded(); if (mEditor.mCursorVisible != visible) { @@ -10550,7 +10550,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * @return whether or not the cursor is visible (assuming this TextView is editable). This - * method may return {@code false} when the IME is temporarily consuming the input even if the + * method may return {@code false} when the IME is consuming the input even if the * {@code mEditor.mCursorVisible} attribute is {@code true} or {@code #setCursorVisible(true)} * is called. * diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java index 015238788191..c2ee6461e5e1 100644 --- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java +++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java @@ -53,8 +53,10 @@ public class HeavyWeightSwitcherActivity extends Activity { public static final String KEY_CUR_TASK = "cur_task"; /** Package of newly requested heavy-weight app. */ public static final String KEY_NEW_APP = "new_app"; + public static final String KEY_ACTIVITY_OPTIONS = "activity_options"; IntentSender mStartIntent; + Bundle mActivityOptions; boolean mHasResult; String mCurApp; int mCurTask; @@ -65,8 +67,9 @@ public class HeavyWeightSwitcherActivity extends Activity { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); - + mStartIntent = (IntentSender)getIntent().getParcelableExtra(KEY_INTENT); + mActivityOptions = getIntent().getBundleExtra(KEY_ACTIVITY_OPTIONS); mHasResult = getIntent().getBooleanExtra(KEY_HAS_RESULT, false); mCurApp = getIntent().getStringExtra(KEY_CUR_APP); mCurTask = getIntent().getIntExtra(KEY_CUR_TASK, 0); @@ -148,9 +151,9 @@ public class HeavyWeightSwitcherActivity extends Activity { if (mHasResult) { startIntentSenderForResult(mStartIntent, -1, null, Intent.FLAG_ACTIVITY_FORWARD_RESULT, - Intent.FLAG_ACTIVITY_FORWARD_RESULT, 0); + Intent.FLAG_ACTIVITY_FORWARD_RESULT, 0, mActivityOptions); } else { - startIntentSenderForResult(mStartIntent, -1, null, 0, 0, 0); + startIntentSenderForResult(mStartIntent, -1, null, 0, 0, 0, mActivityOptions); } } catch (IntentSender.SendIntentException ex) { Log.w("HeavyWeightSwitcherActivity", "Failure starting", ex); diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index a5eb5f607c12..7aca36af919d 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -179,9 +179,10 @@ interface IPlatformCompat { * * @param changeId the ID of the change that was overridden * @param packageName the app package name that was overridden + * @return {@code true} if an override existed * @throws SecurityException if overriding changes is not permitted */ - void clearOverrideForTest(long changeId, String packageName); + boolean clearOverrideForTest(long changeId, String packageName); /** * Enables all compatibility changes that have enabledSinceTargetSdk == diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java index fc95275c2e1b..af21f8183d3a 100644 --- a/core/java/com/android/internal/infra/ServiceConnector.java +++ b/core/java/com/android/internal/infra/ServiceConnector.java @@ -726,4 +726,43 @@ public interface ServiceConnector<I extends IInterface> { } } } + + /** + * A {@link ServiceConnector} that doesn't connect to anything. + * + * @param <T> the type of the {@link IInterface ipc interface} for the remote service + */ + class NoOp<T extends IInterface> extends AndroidFuture<Object> implements ServiceConnector<T> { + { + completeExceptionally(new IllegalStateException("ServiceConnector is a no-op")); + } + + @Override + public boolean run(@NonNull VoidJob<T> job) { + return false; + } + + @Override + public AndroidFuture<Void> post(@NonNull VoidJob<T> job) { + return (AndroidFuture) this; + } + + @Override + public <R> AndroidFuture<R> postForResult(@NonNull Job<T, R> job) { + return (AndroidFuture) this; + } + + @Override + public <R> AndroidFuture<R> postAsync(@NonNull Job<T, CompletableFuture<R>> job) { + return (AndroidFuture) this; + } + + @Override + public AndroidFuture<T> connect() { + return (AndroidFuture) this; + } + + @Override + public void unbind() {} + } } diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 3d896c866c8b..6e9bc84156f1 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -18,10 +18,12 @@ package com.android.internal.jank; import static com.android.internal.jank.FrameTracker.ChoreographerWrapper; import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR; @@ -114,6 +116,8 @@ public class InteractionJankMonitor { public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = 22; public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = 23; public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24; + public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25; + public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26; private static final int NO_STATSD_LOGGING = -1; @@ -147,6 +151,8 @@ public class InteractionJankMonitor { UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL, }; private static volatile InteractionJankMonitor sInstance; @@ -191,6 +197,8 @@ public class InteractionJankMonitor { CUJ_LOCKSCREEN_PIN_DISAPPEAR, CUJ_LOCKSCREEN_TRANSITION_FROM_AOD, CUJ_LOCKSCREEN_TRANSITION_TO_AOD, + CUJ_LAUNCHER_OPEN_ALL_APPS, + CUJ_LAUNCHER_ALL_APPS_SCROLL, }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { @@ -457,6 +465,10 @@ public class InteractionJankMonitor { return "CUJ_LOCKSCREEN_TRANSITION_FROM_AOD"; case CUJ_LOCKSCREEN_TRANSITION_TO_AOD: return "CUJ_LOCKSCREEN_TRANSITION_TO_AOD"; + case CUJ_LAUNCHER_OPEN_ALL_APPS : + return "CUJ_LAUNCHER_OPEN_ALL_APPS"; + case CUJ_LAUNCHER_ALL_APPS_SCROLL: + return "CUJ_LAUNCHER_ALL_APPS_SCROLL"; } return "UNKNOWN"; } diff --git a/core/java/com/android/internal/os/CPU_OWNERS b/core/java/com/android/internal/os/CPU_OWNERS new file mode 100644 index 000000000000..5d3473cc0c9e --- /dev/null +++ b/core/java/com/android/internal/os/CPU_OWNERS @@ -0,0 +1,3 @@ +connoro@google.com +dplotnikov@google.com +rslawik@google.com # Android Telemetry diff --git a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java index fa552e3603c6..50331e3338dc 100644 --- a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java +++ b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java @@ -24,10 +24,7 @@ public final class KernelCpuTotalBpfMapReader { } /** Returns whether total CPU time is measured. */ - public static boolean isSupported() { - // TODO(b/174245730): Implement this check. - return true; - } + public static native boolean isSupported(); /** Reads total CPU time from bpf map. */ public static native boolean read(Callback callback); diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS index 1b07aa0cf0b7..0aa54f556b92 100644 --- a/core/java/com/android/internal/os/OWNERS +++ b/core/java/com/android/internal/os/OWNERS @@ -1,5 +1,6 @@ per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS per-file *Zygote* = file:/ZYGOTE_OWNERS +per-file *Cpu* = file:CPU_OWNERS # BatteryStats per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index d196d4af2e42..c8afea9b0982 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -1091,4 +1091,11 @@ public final class Zygote { * fully-feature Memory Tagging, rather than the static Tagged Pointers. */ public static native boolean nativeSupportsTaggedPointers(); + + /** + * Returns the current native tagging level, as one of the + * MEMORY_TAG_LEVEL_* constants. Returns zero if no tagging is present, or + * we failed to determine the level. + */ + public static native int nativeCurrentTaggingLevel(); } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 94e21e5a5965..f2ed50ef6a07 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -787,9 +787,19 @@ public class ZygoteInit { Zygote.applyInvokeWithSystemProperty(parsedArgs); if (Zygote.nativeSupportsMemoryTagging()) { - /* The system server is more privileged than regular app processes, so it has async - * tag checks enabled on hardware that supports memory tagging. */ - parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC; + /* The system server has ASYNC MTE by default, in order to allow + * system services to specify their own MTE level later, as you + * can't re-enable MTE once it's disabled. */ + String mode = SystemProperties.get("arm64.memtag.process.system_server", "async"); + if (mode.equals("async")) { + parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC; + } else if (mode.equals("sync")) { + parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_SYNC; + } else if (!mode.equals("off")) { + /* When we have an invalid memory tag level, keep the current level. */ + parsedArgs.mRuntimeFlags |= Zygote.nativeCurrentTaggingLevel(); + Slog.e(TAG, "Unknown memory tag level for the system server: \"" + mode + "\""); + } } else if (Zygote.nativeSupportsTaggedPointers()) { /* Enable pointer tagging in the system server. Hardware support for this is present * in all ARMv8 CPUs. */ diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index a4ce027501b8..585ddf6ddf98 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -492,10 +492,12 @@ class ZygoteServer { long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp; if (elapsedTimeMs >= mUsapPoolRefillDelayMs) { - // Normalize the poll timeout value when the time between one poll event and the - // next pushes us over the delay value. This prevents poll receiving a 0 - // timeout value, which would result in it returning immediately. - pollTimeoutMs = -1; + // The refill delay has elapsed during the period between poll invocations. + // We will now check for any currently ready file descriptors before refilling + // the USAP pool. + pollTimeoutMs = 0; + mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; + mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED; } else if (elapsedTimeMs <= 0) { // This can occur if the clock used by currentTimeMillis is reset, which is @@ -517,9 +519,11 @@ class ZygoteServer { } if (pollReturnValue == 0) { - // The poll timeout has been exceeded. This only occurs when we have finished the - // USAP pool refill delay period. - + // The poll returned zero results either when the timeout value has been exceeded + // or when a non-blocking poll is issued and no FDs are ready. In either case it + // is time to refill the pool. This will result in a duplicate assignment when + // the non-blocking poll returns zero results, but it avoids an additional + // conditional in the else branch. mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED; diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index e7b7bf4a5b52..19506a325d9a 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -79,7 +79,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_CLOSE_CONNECTION = 150; private static final int DO_COMMIT_CONTENT = 160; private static final int DO_GET_SURROUNDING_TEXT = 41; - private static final int DO_SET_IME_TEMPORARILY_CONSUMES_INPUT = 170; + private static final int DO_SET_IME_CONSUMES_INPUT = 170; @GuardedBy("mLock") @@ -268,13 +268,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } /** - * Dispatches the request for setting ime temporarily consumes input. + * Dispatches the request for setting ime consumes input. * - * <p>See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}. + * <p>See {@link InputConnection#setImeConsumesInput(boolean)}. */ - public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { - dispatchMessage(obtainMessageB(DO_SET_IME_TEMPORARILY_CONSUMES_INPUT, - imeTemporarilyConsumesInput)); + public void setImeConsumesInput(boolean imeConsumesInput) { + dispatchMessage(obtainMessageB(DO_SET_IME_CONSUMES_INPUT, imeConsumesInput)); } void dispatchMessage(Message msg) { @@ -822,17 +821,17 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } return; } - case DO_SET_IME_TEMPORARILY_CONSUMES_INPUT: { + case DO_SET_IME_CONSUMES_INPUT: { Trace.traceBegin(Trace.TRACE_TAG_INPUT, - "InputConnection#setImeTemporarilyConsumesInput"); + "InputConnection#setImeConsumesInput"); try { InputConnection ic = getInputConnection(); if (ic == null || !isActive()) { Log.w(TAG, - "setImeTemporarilyConsumesInput on inactive InputConnection"); + "setImeConsumesInput on inactive InputConnection"); return; } - ic.setImeTemporarilyConsumesInput(msg.arg1 == 1); + ic.setImeConsumesInput(msg.arg1 == 1); } finally { Trace.traceEnd(Trace.TRACE_TAG_INPUT); } diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index 586404c53f18..b06b4e5351c2 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -86,5 +86,5 @@ import com.android.internal.inputmethod.ISurroundingTextResultCallback; void getSurroundingText(int beforeLength, int afterLength, int flags, ISurroundingTextResultCallback callback); - void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput); + void setImeConsumesInput(boolean imeConsumesInput); } diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 84c92ca83f36..0e9d13595124 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -525,12 +525,12 @@ public class InputConnectionWrapper implements InputConnection { } /** - * See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}. + * See {@link InputConnection#setImeConsumesInput(boolean)}. */ @AnyThread - public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { + public boolean setImeConsumesInput(boolean imeConsumesInput) { try { - mIInputContext.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput); + mIInputContext.setImeConsumesInput(imeConsumesInput); return true; } catch (RemoteException e) { return false; diff --git a/core/java/com/android/internal/widget/DisableImageView.java b/core/java/com/android/internal/widget/DisableImageView.java new file mode 100644 index 000000000000..0d9bf7168ebf --- /dev/null +++ b/core/java/com/android/internal/widget/DisableImageView.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget; + +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.util.AttributeSet; +import android.widget.ImageView; +import android.widget.RemoteViews; + +/** + * ImageView which applies a saturation image filter, used by AppWidgets to represent disabled icon + */ +@RemoteViews.RemoteView +public class DisableImageView extends ImageView { + + public DisableImageView(Context context) { + this(context, null, 0, 0); + } + + public DisableImageView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0, 0); + } + + public DisableImageView(Context context, @Nullable AttributeSet attrs, + int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public DisableImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + // Apply the disabled filter + ColorMatrix brightnessMatrix = new ColorMatrix(); + float brightnessF = 0.5f; + int brightnessI = (int) (255 * brightnessF); + // Brightness: C-new = C-old*(1-amount) + amount + float scale = 1f - brightnessF; + float[] mat = brightnessMatrix.getArray(); + mat[0] = scale; + mat[6] = scale; + mat[12] = scale; + mat[4] = brightnessI; + mat[9] = brightnessI; + mat[14] = brightnessI; + + ColorMatrix filterMatrix = new ColorMatrix(); + filterMatrix.setSaturation(0); + filterMatrix.preConcat(brightnessMatrix); + setColorFilter(new ColorMatrixColorFilter(filterMatrix)); + } +} diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index 4ccf9ce91f27..3d054a5e773c 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -245,11 +245,11 @@ public class EditableInputConnection extends BaseInputConnection } @Override - public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { + public boolean setImeConsumesInput(boolean imeConsumesInput) { if (mTextView == null) { - return super.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput); + return super.setImeConsumesInput(imeConsumesInput); } - mTextView.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput); + mTextView.setImeConsumesInput(imeConsumesInput); return true; } diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS index 3fc3933e9a1c..d284d5167843 100644 --- a/core/java/com/android/internal/widget/OWNERS +++ b/core/java/com/android/internal/widget/OWNERS @@ -11,6 +11,7 @@ per-file *Notification* = file:/services/core/java/com/android/server/notificati per-file *Messaging* = file:/services/core/java/com/android/server/notification/OWNERS per-file *Message* = file:/services/core/java/com/android/server/notification/OWNERS per-file *Conversation* = file:/services/core/java/com/android/server/notification/OWNERS +per-file *People* = file:/services/core/java/com/android/server/notification/OWNERS per-file *ImageResolver* = file:/services/core/java/com/android/server/notification/OWNERS per-file CallLayout.java = file:/services/core/java/com/android/server/notification/OWNERS per-file CachingIconView.java = file:/services/core/java/com/android/server/notification/OWNERS diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index b4d8e506c61a..95999a716707 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -23,7 +23,6 @@ import android.content.pm.IPackageManager; import android.os.Build; import android.os.DropBoxManager; import android.os.Environment; -import android.os.FileObserver; import android.os.FileUtils; import android.os.RecoverySystem; import android.os.RemoteException; @@ -74,7 +73,6 @@ public class BootReceiver extends BroadcastReceiver { SystemProperties.getInt("ro.debuggable", 0) == 1 ? 196608 : 65536; private static final int GMSCORE_LASTK_LOG_SIZE = 196608; - private static final File TOMBSTONE_DIR = new File("/data/tombstones"); private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE"; // The pre-froyo package and class of the system updater, which @@ -85,9 +83,6 @@ public class BootReceiver extends BroadcastReceiver { private static final String OLD_UPDATER_CLASS = "com.google.android.systemupdater.SystemUpdateReceiver"; - // Keep a reference to the observer so the finalizer doesn't disable it. - private static FileObserver sTombstoneObserver = null; - private static final String LOG_FILES_FILE = "log-files.xml"; private static final AtomicFile sFile = new AtomicFile(new File( Environment.getDataSystemDirectory(), LOG_FILES_FILE), "log-files"); @@ -153,7 +148,7 @@ public class BootReceiver extends BroadcastReceiver { Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS); } - private String getPreviousBootHeaders() { + private static String getPreviousBootHeaders() { try { return FileUtils.readTextFile(lastHeaderFile, 0, null); } catch (IOException e) { @@ -161,7 +156,7 @@ public class BootReceiver extends BroadcastReceiver { } } - private String getCurrentBootHeaders() throws IOException { + private static String getCurrentBootHeaders() throws IOException { return new StringBuilder(512) .append("Build: ").append(Build.FINGERPRINT).append("\n") .append("Hardware: ").append(Build.BOARD).append("\n") @@ -175,7 +170,7 @@ public class BootReceiver extends BroadcastReceiver { } - private String getBootHeadersToLogAndUpdate() throws IOException { + private static String getBootHeadersToLogAndUpdate() throws IOException { final String oldHeaders = getPreviousBootHeaders(); final String newHeaders = getCurrentBootHeaders(); @@ -247,38 +242,27 @@ public class BootReceiver extends BroadcastReceiver { logFsMountTime(); addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK"); logSystemServerShutdownTimeMetrics(); + writeTimestamps(timestamps); + } - // Scan existing tombstones (in case any new ones appeared) - File[] tombstoneFiles = TOMBSTONE_DIR.listFiles(); - for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) { - if (tombstoneFiles[i].isFile()) { - addFileToDropBox(db, timestamps, headers, tombstoneFiles[i].getPath(), - LOG_SIZE, "SYSTEM_TOMBSTONE"); - } + /** + * Add a tombstone to the DropBox. + * + * @param ctx Context + * @param tombstone path to the tombstone + */ + public static void addTombstoneToDropBox(Context ctx, File tombstone) { + final DropBoxManager db = ctx.getSystemService(DropBoxManager.class); + final String bootReason = SystemProperties.get("ro.boot.bootreason", null); + HashMap<String, Long> timestamps = readTimestamps(); + try { + final String headers = getBootHeadersToLogAndUpdate(); + addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE, + TAG_TOMBSTONE); + } catch (IOException e) { + Slog.e(TAG, "Can't log tombstone", e); } - writeTimestamps(timestamps); - - // Start watching for new tombstone files; will record them as they occur. - // This gets registered with the singleton file observer thread. - sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CREATE) { - @Override - public void onEvent(int event, String path) { - HashMap<String, Long> timestamps = readTimestamps(); - try { - File file = new File(TOMBSTONE_DIR, path); - if (file.isFile() && file.getName().startsWith("tombstone_")) { - addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE, - TAG_TOMBSTONE); - } - } catch (IOException e) { - Slog.e(TAG, "Can't log tombstone", e); - } - writeTimestamps(timestamps); - } - }; - - sTombstoneObserver.startWatching(); } private static void addLastkToDropBox( @@ -761,7 +745,7 @@ public class BootReceiver extends BroadcastReceiver { } } - private void writeTimestamps(HashMap<String, Long> timestamps) { + private static void writeTimestamps(HashMap<String, Long> timestamps) { synchronized (sFile) { final FileOutputStream stream; try { diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 0b48e72a4acd..8edc8a186c59 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -207,9 +207,9 @@ cc_library_shared { ], shared_libs: [ - "audioclient-types-aidl-unstable-cpp", - "audioflinger-aidl-unstable-cpp", - "av-types-aidl-unstable-cpp", + "audioclient-types-aidl-cpp", + "audioflinger-aidl-cpp", + "av-types-aidl-cpp", "libandroidicu", "libbpf_android", "libnetdbpf", diff --git a/core/jni/OWNERS b/core/jni/OWNERS index 2279d5796c02..35d1d7bd7946 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -5,6 +5,9 @@ per-file *Camera*,*camera* = shuzhenwang@google.com, yinchiayeh@google.com, zhij # Connectivity per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com +# CPU +per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS + # Display per-file android_hardware_display_* = file:/services/core/java/com/android/server/display/OWNERS diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index 6337680147a5..8977b97fc541 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -40,7 +40,7 @@ static struct { jmethodID dispatchVsync; jmethodID dispatchHotplug; - jmethodID dispatchConfigChanged; + jmethodID dispatchModeChanged; jmethodID dispatchFrameRateOverrides; struct { @@ -69,8 +69,8 @@ private: void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, VsyncEventData vsyncEventData) override; void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; - void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, - int32_t configId, nsecs_t vsyncPeriod) override; + void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId, + nsecs_t vsyncPeriod) override; void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, std::vector<FrameRateOverride> overrides) override; void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) override {} @@ -129,20 +129,19 @@ void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, PhysicalDisp mMessageQueue->raiseAndClearException(env, "dispatchHotplug"); } -void NativeDisplayEventReceiver::dispatchConfigChanged( - nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, nsecs_t) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); +void NativeDisplayEventReceiver::dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, + int32_t modeId, nsecs_t) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); - ScopedLocalRef<jobject> receiverObj(env, - jniGetReferent(env, mReceiverWeakGlobal)); - if (receiverObj.get()) { - ALOGV("receiver %p ~ Invoking config changed handler.", this); - env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchConfigChanged, - timestamp, displayId.value, configId); - ALOGV("receiver %p ~ Returned from config changed handler.", this); - } + ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); + if (receiverObj.get()) { + ALOGV("receiver %p ~ Invoking mode changed handler.", this); + env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchModeChanged, + timestamp, displayId.value, modeId); + ALOGV("receiver %p ~ Returned from mode changed handler.", this); + } - mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged"); + mMessageQueue->raiseAndClearException(env, "dispatchModeChanged"); } void NativeDisplayEventReceiver::dispatchFrameRateOverrides( @@ -173,7 +172,7 @@ void NativeDisplayEventReceiver::dispatchFrameRateOverrides( ALOGV("receiver %p ~ Returned from FrameRateOverride handler.", this); } - mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged"); + mMessageQueue->raiseAndClearException(env, "dispatchModeChanged"); } static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj, @@ -243,8 +242,9 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { "(JJIJJ)V"); gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V"); - gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env, - gDisplayEventReceiverClassInfo.clazz, "dispatchConfigChanged", "(JJI)V"); + gDisplayEventReceiverClassInfo.dispatchModeChanged = + GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeChanged", + "(JJI)V"); gDisplayEventReceiverClassInfo.dispatchFrameRateOverrides = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchFrameRateOverrides", diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 05fcaec82f84..7a3366acce27 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -44,8 +44,8 @@ #include <ui/BlurRegion.h> #include <ui/ConfigStoreTypes.h> #include <ui/DeviceProductInfo.h> -#include <ui/DisplayConfig.h> #include <ui/DisplayInfo.h> +#include <ui/DisplayMode.h> #include <ui/DisplayedFrameStats.h> #include <ui/FrameStats.h> #include <ui/GraphicTypes.h> @@ -98,8 +98,8 @@ static struct { jfieldID refreshRate; jfieldID appVsyncOffsetNanos; jfieldID presentationDeadlineNanos; - jfieldID configGroup; -} gDisplayConfigClassInfo; + jfieldID group; +} gDisplayModeClassInfo; static struct { jfieldID bottom; @@ -202,13 +202,13 @@ static struct { static struct { jclass clazz; jmethodID ctor; - jfieldID defaultConfig; + jfieldID defaultMode; jfieldID allowGroupSwitching; jfieldID primaryRefreshRateMin; jfieldID primaryRefreshRateMax; jfieldID appRequestRefreshRateMin; jfieldID appRequestRefreshRateMax; -} gDesiredDisplayConfigSpecsClassInfo; +} gDesiredDisplayModeSpecsClassInfo; static struct { jclass clazz; @@ -1028,101 +1028,97 @@ static jobject nativeGetDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) return object; } -static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz, jobject tokenObj) { - Vector<DisplayConfig> configs; +static jobjectArray nativeGetDisplayModes(JNIEnv* env, jclass clazz, jobject tokenObj) { + Vector<ui::DisplayMode> modes; if (const auto token = ibinderForJavaObject(env, tokenObj); !token || - SurfaceComposerClient::getDisplayConfigs(token, &configs) != NO_ERROR || - configs.isEmpty()) { + SurfaceComposerClient::getDisplayModes(token, &modes) != NO_ERROR || modes.isEmpty()) { return nullptr; } - jobjectArray configArray = - env->NewObjectArray(configs.size(), gDisplayConfigClassInfo.clazz, nullptr); - - for (size_t c = 0; c < configs.size(); ++c) { - const DisplayConfig& config = configs[c]; - jobject object = - env->NewObject(gDisplayConfigClassInfo.clazz, gDisplayConfigClassInfo.ctor); - env->SetIntField(object, gDisplayConfigClassInfo.width, config.resolution.getWidth()); - env->SetIntField(object, gDisplayConfigClassInfo.height, config.resolution.getHeight()); - env->SetFloatField(object, gDisplayConfigClassInfo.xDpi, config.xDpi); - env->SetFloatField(object, gDisplayConfigClassInfo.yDpi, config.yDpi); - - env->SetFloatField(object, gDisplayConfigClassInfo.refreshRate, config.refreshRate); - env->SetLongField(object, gDisplayConfigClassInfo.appVsyncOffsetNanos, - config.appVsyncOffset); - env->SetLongField(object, gDisplayConfigClassInfo.presentationDeadlineNanos, - config.presentationDeadline); - env->SetIntField(object, gDisplayConfigClassInfo.configGroup, config.configGroup); - env->SetObjectArrayElement(configArray, static_cast<jsize>(c), object); + jobjectArray modesArray = + env->NewObjectArray(modes.size(), gDisplayModeClassInfo.clazz, nullptr); + + for (size_t c = 0; c < modes.size(); ++c) { + const ui::DisplayMode& mode = modes[c]; + jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor); + env->SetIntField(object, gDisplayModeClassInfo.width, mode.resolution.getWidth()); + env->SetIntField(object, gDisplayModeClassInfo.height, mode.resolution.getHeight()); + env->SetFloatField(object, gDisplayModeClassInfo.xDpi, mode.xDpi); + env->SetFloatField(object, gDisplayModeClassInfo.yDpi, mode.yDpi); + + env->SetFloatField(object, gDisplayModeClassInfo.refreshRate, mode.refreshRate); + env->SetLongField(object, gDisplayModeClassInfo.appVsyncOffsetNanos, mode.appVsyncOffset); + env->SetLongField(object, gDisplayModeClassInfo.presentationDeadlineNanos, + mode.presentationDeadline); + env->SetIntField(object, gDisplayModeClassInfo.group, mode.group); + env->SetObjectArrayElement(modesArray, static_cast<jsize>(c), object); env->DeleteLocalRef(object); } - return configArray; + return modesArray; } -static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj, - jobject desiredDisplayConfigSpecs) { +static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj, + jobject DesiredDisplayModeSpecs) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == nullptr) return JNI_FALSE; - jint defaultConfig = env->GetIntField(desiredDisplayConfigSpecs, - gDesiredDisplayConfigSpecsClassInfo.defaultConfig); + size_t defaultMode = + static_cast<size_t>(env->GetIntField(DesiredDisplayModeSpecs, + gDesiredDisplayModeSpecsClassInfo.defaultMode)); jboolean allowGroupSwitching = - env->GetBooleanField(desiredDisplayConfigSpecs, - gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching); + env->GetBooleanField(DesiredDisplayModeSpecs, + gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching); jfloat primaryRefreshRateMin = - env->GetFloatField(desiredDisplayConfigSpecs, - gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin); + env->GetFloatField(DesiredDisplayModeSpecs, + gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMin); jfloat primaryRefreshRateMax = - env->GetFloatField(desiredDisplayConfigSpecs, - gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax); + env->GetFloatField(DesiredDisplayModeSpecs, + gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMax); jfloat appRequestRefreshRateMin = - env->GetFloatField(desiredDisplayConfigSpecs, - gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMin); + env->GetFloatField(DesiredDisplayModeSpecs, + gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMin); jfloat appRequestRefreshRateMax = - env->GetFloatField(desiredDisplayConfigSpecs, - gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax); - - size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs(token, defaultConfig, - allowGroupSwitching, - primaryRefreshRateMin, - primaryRefreshRateMax, - appRequestRefreshRateMin, - appRequestRefreshRateMax); + env->GetFloatField(DesiredDisplayModeSpecs, + gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax); + + size_t result = SurfaceComposerClient::setDesiredDisplayModeSpecs(token, defaultMode, + allowGroupSwitching, + primaryRefreshRateMin, + primaryRefreshRateMax, + appRequestRefreshRateMin, + appRequestRefreshRateMax); return result == NO_ERROR ? JNI_TRUE : JNI_FALSE; } -static jobject nativeGetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj) { +static jobject nativeGetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == nullptr) return nullptr; - int32_t defaultConfig; + size_t defaultMode; bool allowGroupSwitching; float primaryRefreshRateMin; float primaryRefreshRateMax; float appRequestRefreshRateMin; float appRequestRefreshRateMax; - if (SurfaceComposerClient::getDesiredDisplayConfigSpecs(token, &defaultConfig, - &allowGroupSwitching, - &primaryRefreshRateMin, - &primaryRefreshRateMax, - &appRequestRefreshRateMin, - &appRequestRefreshRateMax) != - NO_ERROR) { + if (SurfaceComposerClient::getDesiredDisplayModeSpecs(token, &defaultMode, &allowGroupSwitching, + &primaryRefreshRateMin, + &primaryRefreshRateMax, + &appRequestRefreshRateMin, + &appRequestRefreshRateMax) != NO_ERROR) { return nullptr; } - return env->NewObject(gDesiredDisplayConfigSpecsClassInfo.clazz, - gDesiredDisplayConfigSpecsClassInfo.ctor, defaultConfig, - allowGroupSwitching, primaryRefreshRateMin, primaryRefreshRateMax, - appRequestRefreshRateMin, appRequestRefreshRateMax); + return env->NewObject(gDesiredDisplayModeSpecsClassInfo.clazz, + gDesiredDisplayModeSpecsClassInfo.ctor, defaultMode, allowGroupSwitching, + primaryRefreshRateMin, primaryRefreshRateMax, appRequestRefreshRateMin, + appRequestRefreshRateMax); } -static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) { +static jint nativeGetActiveDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObj) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == NULL) return -1; - return static_cast<jint>(SurfaceComposerClient::getActiveConfig(token)); + return static_cast<jint>(SurfaceComposerClient::getActiveDisplayModeId(token)); } static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) { @@ -1409,16 +1405,6 @@ static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong transac transaction->deferTransactionUntil_legacy(ctrl, barrier, frameNumber); } -static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong transactionObj, - jlong nativeObject, - jlong newParentObject) { - - auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); - auto newParent = reinterpret_cast<SurfaceControl *>(newParentObject); - auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); - transaction->reparentChildren(ctrl, newParent); -} - static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jlong newParentObject) { @@ -1794,16 +1780,16 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetDisplaySize }, {"nativeGetDisplayInfo", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayInfo;", (void*)nativeGetDisplayInfo }, - {"nativeGetDisplayConfigs", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayConfig;", - (void*)nativeGetDisplayConfigs }, - {"nativeGetActiveConfig", "(Landroid/os/IBinder;)I", - (void*)nativeGetActiveConfig }, - {"nativeSetDesiredDisplayConfigSpecs", - "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;)Z", - (void*)nativeSetDesiredDisplayConfigSpecs }, - {"nativeGetDesiredDisplayConfigSpecs", - "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;", - (void*)nativeGetDesiredDisplayConfigSpecs }, + {"nativeGetDisplayModes", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayMode;", + (void*)nativeGetDisplayModes }, + {"nativeGetActiveDisplayMode", "(Landroid/os/IBinder;)I", + (void*)nativeGetActiveDisplayMode }, + {"nativeSetDesiredDisplayModeSpecs", + "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;)Z", + (void*)nativeSetDesiredDisplayModeSpecs }, + {"nativeGetDesiredDisplayModeSpecs", + "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;", + (void*)nativeGetDesiredDisplayModeSpecs }, {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I", (void*)nativeGetDisplayColorModes}, {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;", @@ -1838,8 +1824,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetProtectedContentSupport }, {"nativeDeferTransactionUntil", "(JJJJ)V", (void*)nativeDeferTransactionUntil }, - {"nativeReparentChildren", "(JJJ)V", - (void*)nativeReparentChildren } , {"nativeReparent", "(JJJ)V", (void*)nativeReparent }, {"nativeCaptureDisplay", @@ -1914,19 +1898,19 @@ int register_android_view_SurfaceControl(JNIEnv* env) GetFieldIDOrDie(env, infoClazz, "deviceProductInfo", "Landroid/hardware/display/DeviceProductInfo;"); - jclass configClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayConfig"); - gDisplayConfigClassInfo.clazz = MakeGlobalRefOrDie(env, configClazz); - gDisplayConfigClassInfo.ctor = GetMethodIDOrDie(env, configClazz, "<init>", "()V"); - gDisplayConfigClassInfo.width = GetFieldIDOrDie(env, configClazz, "width", "I"); - gDisplayConfigClassInfo.height = GetFieldIDOrDie(env, configClazz, "height", "I"); - gDisplayConfigClassInfo.xDpi = GetFieldIDOrDie(env, configClazz, "xDpi", "F"); - gDisplayConfigClassInfo.yDpi = GetFieldIDOrDie(env, configClazz, "yDpi", "F"); - gDisplayConfigClassInfo.refreshRate = GetFieldIDOrDie(env, configClazz, "refreshRate", "F"); - gDisplayConfigClassInfo.appVsyncOffsetNanos = - GetFieldIDOrDie(env, configClazz, "appVsyncOffsetNanos", "J"); - gDisplayConfigClassInfo.presentationDeadlineNanos = - GetFieldIDOrDie(env, configClazz, "presentationDeadlineNanos", "J"); - gDisplayConfigClassInfo.configGroup = GetFieldIDOrDie(env, configClazz, "configGroup", "I"); + jclass modeClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayMode"); + gDisplayModeClassInfo.clazz = MakeGlobalRefOrDie(env, modeClazz); + gDisplayModeClassInfo.ctor = GetMethodIDOrDie(env, modeClazz, "<init>", "()V"); + gDisplayModeClassInfo.width = GetFieldIDOrDie(env, modeClazz, "width", "I"); + gDisplayModeClassInfo.height = GetFieldIDOrDie(env, modeClazz, "height", "I"); + gDisplayModeClassInfo.xDpi = GetFieldIDOrDie(env, modeClazz, "xDpi", "F"); + gDisplayModeClassInfo.yDpi = GetFieldIDOrDie(env, modeClazz, "yDpi", "F"); + gDisplayModeClassInfo.refreshRate = GetFieldIDOrDie(env, modeClazz, "refreshRate", "F"); + gDisplayModeClassInfo.appVsyncOffsetNanos = + GetFieldIDOrDie(env, modeClazz, "appVsyncOffsetNanos", "J"); + gDisplayModeClassInfo.presentationDeadlineNanos = + GetFieldIDOrDie(env, modeClazz, "presentationDeadlineNanos", "J"); + gDisplayModeClassInfo.group = GetFieldIDOrDie(env, modeClazz, "group", "I"); jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect"); gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I"); @@ -2017,24 +2001,23 @@ int register_android_view_SurfaceControl(JNIEnv* env) gDisplayPrimariesClassInfo.white = GetFieldIDOrDie(env, displayPrimariesClazz, "white", "Landroid/view/SurfaceControl$CieXyz;"); - jclass desiredDisplayConfigSpecsClazz = - FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayConfigSpecs"); - gDesiredDisplayConfigSpecsClassInfo.clazz = - MakeGlobalRefOrDie(env, desiredDisplayConfigSpecsClazz); - gDesiredDisplayConfigSpecsClassInfo.ctor = - GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IZFFFF)V"); - gDesiredDisplayConfigSpecsClassInfo.defaultConfig = - GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "defaultConfig", "I"); - gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching = - GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "allowGroupSwitching", "Z"); - gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin = - GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMin", "F"); - gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax = - GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMax", "F"); - gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMin = - GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "appRequestRefreshRateMin", "F"); - gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax = - GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "appRequestRefreshRateMax", "F"); + jclass DesiredDisplayModeSpecsClazz = + FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayModeSpecs"); + gDesiredDisplayModeSpecsClassInfo.clazz = MakeGlobalRefOrDie(env, DesiredDisplayModeSpecsClazz); + gDesiredDisplayModeSpecsClassInfo.ctor = + GetMethodIDOrDie(env, gDesiredDisplayModeSpecsClassInfo.clazz, "<init>", "(IZFFFF)V"); + gDesiredDisplayModeSpecsClassInfo.defaultMode = + GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "defaultMode", "I"); + gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching = + GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "allowGroupSwitching", "Z"); + gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMin = + GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRefreshRateMin", "F"); + gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMax = + GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRefreshRateMax", "F"); + gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMin = + GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMin", "F"); + gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax = + GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMax", "F"); jclass captureArgsClazz = FindClassOrDie(env, "android/view/SurfaceControl$CaptureArgs"); gCaptureArgsClassInfo.pixelFormat = GetFieldIDOrDie(env, captureArgsClazz, "mPixelFormat", "I"); diff --git a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp index 72492381e31a..d8446ca2881d 100644 --- a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp +++ b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp @@ -20,6 +20,10 @@ namespace android { +static jboolean KernelCpuTotalBpfMapReader_isSupported(JNIEnv *, jobject) { + return android::bpf::isTrackingUidTimesSupported() ? JNI_TRUE : JNI_FALSE; +} + static jboolean KernelCpuTotalBpfMapReader_read(JNIEnv *env, jobject, jobject callback) { jclass callbackClass = env->GetObjectClass(callback); jmethodID callbackMethod = env->GetMethodID(callbackClass, "accept", "(IIJ)V"); @@ -47,6 +51,7 @@ static jboolean KernelCpuTotalBpfMapReader_read(JNIEnv *env, jobject, jobject ca static const JNINativeMethod methods[] = { {"read", "(Lcom/android/internal/os/KernelCpuTotalBpfMapReader$Callback;)Z", (void *)KernelCpuTotalBpfMapReader_read}, + {"isSupported", "()Z", (void *)KernelCpuTotalBpfMapReader_isSupported}, }; int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv *env) { diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 3d1c38d4b5ca..e8017253fc29 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -2506,6 +2506,36 @@ static jboolean com_android_internal_os_Zygote_nativeSupportsTaggedPointers(JNIE #endif } +static jint com_android_internal_os_Zygote_nativeCurrentTaggingLevel(JNIEnv* env, jclass) { +#if defined(__aarch64__) + int level = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); + if (level < 0) { + ALOGE("Failed to get memory tag level: %s", strerror(errno)); + return 0; + } else if (!(level & PR_TAGGED_ADDR_ENABLE)) { + return 0; + } + // TBI is only possible on non-MTE hardware. + if (!mte_supported()) { + return MEMORY_TAG_LEVEL_TBI; + } + + switch (level & PR_MTE_TCF_MASK) { + case PR_MTE_TCF_NONE: + return 0; + case PR_MTE_TCF_SYNC: + return MEMORY_TAG_LEVEL_SYNC; + case PR_MTE_TCF_ASYNC: + return MEMORY_TAG_LEVEL_ASYNC; + default: + ALOGE("Unknown memory tagging level: %i", level); + return 0; + } +#else // defined(__aarch64__) + return 0; +#endif // defined(__aarch64__) +} + static const JNINativeMethod gMethods[] = { {"nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/" @@ -2545,6 +2575,8 @@ static const JNINativeMethod gMethods[] = { (void*)com_android_internal_os_Zygote_nativeSupportsMemoryTagging}, {"nativeSupportsTaggedPointers", "()Z", (void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers}, + {"nativeCurrentTaggingLevel", "()I", + (void*)com_android_internal_os_Zygote_nativeCurrentTaggingLevel}, }; int register_com_android_internal_os_Zygote(JNIEnv* env) { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 269ccbbbfaec..827bf7b70cbc 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -690,10 +690,6 @@ <!-- Made protected in S (was added in R) --> <protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" /> - <!-- Added in S --> - <protected-broadcast android:name="android.app.action.MANAGED_PROFILE_CREATED" /> - <protected-broadcast android:name="android.app.action.PROVISIONED_MANAGED_DEVICE" /> - <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> <!-- ====================================================================== --> @@ -2539,7 +2535,7 @@ <permission android:name="android.permission.REAL_GET_TASKS" android:protectionLevel="signature|privileged" /> - <!-- Allows an application to start a task from a ActivityManager#RecentTaskInfo. + <!-- @TestApi Allows an application to start a task from a ActivityManager#RecentTaskInfo. @hide --> <permission android:name="android.permission.START_TASKS_FROM_RECENTS" android:protectionLevel="signature|privileged|recents" /> @@ -5431,7 +5427,7 @@ <permission android:name="android.permission.INPUT_CONSUMER" android:protectionLevel="signature" /> - <!-- @hide Allows an application to control the system's device state managed by the + <!-- @hide @TestApi Allows an application to control the system's device state managed by the {@link android.service.devicestate.DeviceStateManagerService}. For example, on foldable devices this would grant access to toggle between the folded and unfolded states. --> <permission android:name="android.permission.CONTROL_DEVICE_STATE" diff --git a/core/res/res/layout/notification_template_part_chronometer.xml b/core/res/res/layout/notification_template_part_chronometer.xml index c5ffbeadc525..e4ebc76c113a 100644 --- a/core/res/res/layout/notification_template_part_chronometer.xml +++ b/core/res/res/layout/notification_template_part_chronometer.xml @@ -15,7 +15,7 @@ --> <Chronometer android:id="@+id/chronometer" xmlns:android="http://schemas.android.com/apk/res/android" - android:textAppearance="@style/TextAppearance.Material.Notification.Time" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="4dp" diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml index 39e1bbb467b4..9e1692f23a1e 100644 --- a/core/res/res/layout/work_widget_mask_view.xml +++ b/core/res/res/layout/work_widget_mask_view.xml @@ -22,7 +22,8 @@ Copyright (C) 2015 The Android Open Source Project android:importantForAccessibility="noHideDescendants" android:clickable="true"> - <ImageView android:id="@+id/work_widget_app_icon" + <com.android.internal.widget.DisableImageView + android:id="@+id/work_widget_app_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index eb867090f8ba..14df77527a08 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8500,6 +8500,9 @@ interaction requests from an Activity. This flag is new in {@link android.os.Build.VERSION_CODES#N} and not used in previous versions. --> <attr name="supportsLocalInteraction" format="boolean" /> + <!-- The service that provides {@link android.service.voice.HotwordDetectionService}. + @hide @SystemApi --> + <attr name="hotwordDetectionService" format="string" /> </declare-styleable> <!-- Use <code>voice-enrollment-application</code> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3790aa4fb072..8c5f454d204d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3477,6 +3477,11 @@ <!-- The alarm window (in milliseconds) that JobScheduler uses to enter the idle state --> <integer name="config_jobSchedulerIdleWindowSlop">300000</integer> + <!-- If true, jobs from background user will be restricted --> + <bool name="config_jobSchedulerRestrictBackgroundUser">false</bool> + <!-- The length of grace period after user becomes background user --> + <integer name="config_jobSchedulerUserGracePeriod">60000</integer> + <!-- If true, all guest users created on the device will be ephemeral. --> <bool name="config_guestUserEphemeral">false</bool> @@ -4707,7 +4712,4 @@ <!-- Whether to select voice/data/sms preference without user confirmation --> <bool name="config_voice_data_sms_auto_fallback">false</bool> - - <!-- Whether to enable the one-handed keyguard on the lock screen for wide-screen devices. --> - <bool name="config_enableOneHandedKeyguard">false</bool> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index a7e8f2adab9c..30cfb8986035 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3059,6 +3059,8 @@ <public name="hand_second" /> <public name="memtagMode" /> <public name="nativeHeapZeroInit" /> + <!-- @hide @SystemApi --> + <public name="hotwordDetectionService" /> </public-group> <public-group type="drawable" first-id="0x010800b5"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 98b36c5c9cbf..9b5f67091a2a 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1585,6 +1585,8 @@ <!-- Template to be used to name enrolled fingerprints by default. --> <string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string> + <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their fingerprint. [CHAR LIMIT=70] --> + <string name="fingerprint_dialog_default_subtitle">Use your fingerprint to continue</string> <!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings --> <string-array name="fingerprint_error_vendor"> @@ -4530,9 +4532,8 @@ shown in the warning dialog about the accessibility shortcut. --> <string name="color_correction_feature_name">Color Correction</string> - <!-- TODO(b/170970602): remove translatable=false when RBC has official name and strings --> - <!-- Title of Reduce Bright Colors feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] --> - <string name="reduce_bright_colors_feature_name" translatable="false">Reduce Bright Colors</string> + <!-- Title of Reduce Brightness feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] --> + <string name="reduce_bright_colors_feature_name">Reduce Brightness</string> <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] --> <string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 80163b16ada0..c41b78e4a680 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2488,6 +2488,7 @@ <java-symbol type="string" name="fingerprint_error_lockout" /> <java-symbol type="string" name="fingerprint_error_lockout_permanent" /> <java-symbol type="string" name="fingerprint_name_template" /> + <java-symbol type="string" name="fingerprint_dialog_default_subtitle" /> <java-symbol type="string" name="fingerprint_authenticated" /> <java-symbol type="string" name="fingerprint_error_no_fingerprints" /> <java-symbol type="string" name="fingerprint_error_hw_not_present" /> @@ -2679,6 +2680,8 @@ <java-symbol type="integer" name="config_jobSchedulerInactivityIdleThreshold" /> <java-symbol type="integer" name="config_jobSchedulerIdleWindowSlop" /> + <java-symbol type="bool" name="config_jobSchedulerRestrictBackgroundUser" /> + <java-symbol type="integer" name="config_jobSchedulerUserGracePeriod" /> <java-symbol type="style" name="Animation.ImmersiveModeConfirmation" /> @@ -4175,6 +4178,4 @@ <java-symbol type="bool" name="config_telephony5gNonStandalone" /> <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" /> - - <java-symbol type="bool" name="config_enableOneHandedKeyguard" /> </resources> diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java index da7304efbd3d..48b58c6c0a1c 100644 --- a/core/tests/coretests/src/android/app/WindowContextTest.java +++ b/core/tests/coretests/src/android/app/WindowContextTest.java @@ -184,7 +184,8 @@ public class WindowContextTest { final IBinder token = windowContext.getWindowContextToken(); final IBinder existingToken = new Binder(); - mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId()); + mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId(), + null /* options */); final WindowManager.LayoutParams params = new WindowManager.LayoutParams(TYPE_INPUT_METHOD); diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index a4284a04310e..47ce2d87e69f 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -278,29 +278,29 @@ public class TextViewTest { @Test @UiThreadTest - public void setSetImeTemporarilyConsumesInput_recoveryToVisible() { + public void setSetImeConsumesInput_recoveryToVisible() { mTextView = new TextView(mActivity); mTextView.setCursorVisible(true); assertTrue(mTextView.isCursorVisible()); - mTextView.setImeTemporarilyConsumesInput(true); + mTextView.setImeConsumesInput(true); assertFalse(mTextView.isCursorVisible()); - mTextView.setImeTemporarilyConsumesInput(false); + mTextView.setImeConsumesInput(false); assertTrue(mTextView.isCursorVisible()); } @Test @UiThreadTest - public void setSetImeTemporarilyConsumesInput_recoveryToInvisible() { + public void setSetImeConsumesInput_recoveryToInvisible() { mTextView = new TextView(mActivity); mTextView.setCursorVisible(false); assertFalse(mTextView.isCursorVisible()); - mTextView.setImeTemporarilyConsumesInput(true); + mTextView.setImeConsumesInput(true); assertFalse(mTextView.isCursorVisible()); - mTextView.setImeTemporarilyConsumesInput(false); + mTextView.setImeConsumesInput(false); assertFalse(mTextView.isCursorVisible()); } diff --git a/core/tests/coretests/src/com/android/internal/os/OWNERS b/core/tests/coretests/src/com/android/internal/os/OWNERS index 4068e2bc03b7..3f8f9e29c6a0 100644 --- a/core/tests/coretests/src/com/android/internal/os/OWNERS +++ b/core/tests/coretests/src/com/android/internal/os/OWNERS @@ -1 +1,4 @@ include /BATTERY_STATS_OWNERS + +# CPU +per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java index 36f01f9a951d..e7fdfb8c19e9 100644 --- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java +++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java @@ -17,8 +17,11 @@ package android.hardware.devicestate; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; import android.annotation.Nullable; +import android.os.IBinder; import android.os.RemoteException; import androidx.test.filters.SmallTest; @@ -30,6 +33,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -41,6 +45,9 @@ import java.util.Set; @RunWith(JUnit4.class) @SmallTest public final class DeviceStateManagerGlobalTest { + private static final int DEFAULT_DEVICE_STATE = 0; + private static final int OTHER_DEVICE_STATE = 1; + private TestDeviceStateManagerService mService; private DeviceStateManagerGlobal mDeviceStateManagerGlobal; @@ -52,7 +59,7 @@ public final class DeviceStateManagerGlobalTest { @Test public void registerListener() { - mService.setDeviceState(0); + mService.setBaseState(DEFAULT_DEVICE_STATE); TestDeviceStateListener listener1 = new TestDeviceStateListener(); TestDeviceStateListener listener2 = new TestDeviceStateListener(); @@ -61,28 +68,58 @@ public final class DeviceStateManagerGlobalTest { ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateListener(listener2, ConcurrentUtils.DIRECT_EXECUTOR); - assertEquals(0, listener1.getLastReportedState().intValue()); - assertEquals(0, listener2.getLastReportedState().intValue()); + assertEquals(DEFAULT_DEVICE_STATE, listener1.getLastReportedState().intValue()); + assertEquals(DEFAULT_DEVICE_STATE, listener2.getLastReportedState().intValue()); - mService.setDeviceState(1); - assertEquals(1, listener1.getLastReportedState().intValue()); - assertEquals(1, listener2.getLastReportedState().intValue()); + mService.setBaseState(OTHER_DEVICE_STATE); + assertEquals(OTHER_DEVICE_STATE, listener1.getLastReportedState().intValue()); + assertEquals(OTHER_DEVICE_STATE, listener2.getLastReportedState().intValue()); } @Test public void unregisterListener() { - mService.setDeviceState(0); + mService.setBaseState(DEFAULT_DEVICE_STATE); TestDeviceStateListener listener = new TestDeviceStateListener(); mDeviceStateManagerGlobal.registerDeviceStateListener(listener, ConcurrentUtils.DIRECT_EXECUTOR); - assertEquals(0, listener.getLastReportedState().intValue()); + assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue()); mDeviceStateManagerGlobal.unregisterDeviceStateListener(listener); - mService.setDeviceState(1); - assertEquals(0, listener.getLastReportedState().intValue()); + mService.setBaseState(OTHER_DEVICE_STATE); + assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue()); + } + + @Test + public void submittingRequestRegisteredCallback() { + assertTrue(mService.mCallbacks.isEmpty()); + + DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build(); + mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); + + assertFalse(mService.mCallbacks.isEmpty()); + } + + @Test + public void submitRequest() { + mService.setBaseState(DEFAULT_DEVICE_STATE); + + TestDeviceStateListener listener = new TestDeviceStateListener(); + mDeviceStateManagerGlobal.registerDeviceStateListener(listener, + ConcurrentUtils.DIRECT_EXECUTOR); + + assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue()); + + DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build(); + mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); + + assertEquals(OTHER_DEVICE_STATE, listener.getLastReportedState().intValue()); + + mDeviceStateManagerGlobal.cancelRequest(request); + + assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue()); } private final class TestDeviceStateListener implements DeviceStateManager.DeviceStateListener { @@ -100,8 +137,23 @@ public final class DeviceStateManagerGlobalTest { } } - private final class TestDeviceStateManagerService extends IDeviceStateManager.Stub { - private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE; + private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub { + public static final class Request { + public final IBinder token; + public final int state; + public final int flags; + + private Request(IBinder token, int state, int flags) { + this.token = token; + this.state = state; + this.flags = flags; + } + } + + private int mBaseState = DEFAULT_DEVICE_STATE; + private int mMergedState = DEFAULT_DEVICE_STATE; + private ArrayList<Request> mRequests = new ArrayList<>(); + private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>(); @Override @@ -112,19 +164,86 @@ public final class DeviceStateManagerGlobalTest { mCallbacks.add(callback); try { - callback.onDeviceStateChanged(mDeviceState); + callback.onDeviceStateChanged(mMergedState); } catch (RemoteException e) { // Do nothing. Should never happen. } } - public void setDeviceState(int deviceState) { - boolean stateChanged = mDeviceState != deviceState; - mDeviceState = deviceState; - if (stateChanged) { + @Override + public int[] getSupportedDeviceStates() { + return new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE }; + } + + @Override + public void requestState(IBinder token, int state, int flags) { + if (!mRequests.isEmpty()) { + final Request topRequest = mRequests.get(mRequests.size() - 1); + for (IDeviceStateManagerCallback callback : mCallbacks) { + try { + callback.onRequestSuspended(topRequest.token); + } catch (RemoteException e) { + // Do nothing. Should never happen. + } + } + } + + final Request request = new Request(token, state, flags); + mRequests.add(request); + notifyStateChangedIfNeeded(); + + for (IDeviceStateManagerCallback callback : mCallbacks) { + try { + callback.onRequestActive(token); + } catch (RemoteException e) { + // Do nothing. Should never happen. + } + } + } + + @Override + public void cancelRequest(IBinder token) { + int index = -1; + for (int i = 0; i < mRequests.size(); i++) { + if (mRequests.get(i).token.equals(token)) { + index = i; + break; + } + } + + if (index == -1) { + throw new IllegalArgumentException("Unknown request: " + token); + } + + mRequests.remove(index); + for (IDeviceStateManagerCallback callback : mCallbacks) { + try { + callback.onRequestCanceled(token); + } catch (RemoteException e) { + // Do nothing. Should never happen. + } + } + notifyStateChangedIfNeeded(); + } + + public void setBaseState(int state) { + mBaseState = state; + notifyStateChangedIfNeeded(); + } + + private void notifyStateChangedIfNeeded() { + final int originalMergedState = mMergedState; + + if (!mRequests.isEmpty()) { + mMergedState = mRequests.get(mRequests.size() - 1).state; + } else { + mMergedState = mBaseState; + } + + if (mMergedState != originalMergedState) { for (IDeviceStateManagerCallback callback : mCallbacks) { try { - callback.onDeviceStateChanged(mDeviceState); + callback.onDeviceStateChanged(mMergedState); } catch (RemoteException e) { // Do nothing. Should never happen. } diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java deleted file mode 100644 index 5a3ea35b1194..000000000000 --- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.view; - -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_90; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.Rect; -import android.hardware.display.DisplayManagerGlobal; -import android.util.DisplayMetrics; -import android.view.DisplayAdjustments.FixedRotationAdjustments; - -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.dx.mockito.inline.extended.StaticMockitoSession; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.mockito.quality.Strictness; - -import java.util.function.Consumer; - -/** - * Tests for {@link Display}. - * - * <p>Build/Install/Run: - * - * atest FrameworksMockingCoreTests:android.view.DisplayTests - */ -@RunWith(AndroidJUnit4.class) -public class DisplayTests { - - private static final int APP_WIDTH = 272; - private static final int APP_HEIGHT = 700; - // Tablet size device, ROTATION_0 corresponds to portrait. - private static final int LOGICAL_WIDTH = 700; - private static final int LOGICAL_HEIGHT = 1800; - - // Bounds of the app when the device is in portrait mode. - private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT); - private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH); - - private StaticMockitoSession mMockitoSession; - - private DisplayManagerGlobal mDisplayManagerGlobal; - private Context mApplicationContext; - private DisplayInfo mDisplayInfo = new DisplayInfo(); - - @Before - public void setupTests() { - mMockitoSession = mockitoSession() - .mockStatic(DisplayManagerGlobal.class) - .strictness(Strictness.LENIENT) - .startMocking(); - - // Ensure no adjustments are set before each test. - mApplicationContext = ApplicationProvider.getApplicationContext(); - DisplayAdjustments displayAdjustments = - mApplicationContext.getResources().getDisplayAdjustments(); - displayAdjustments.setFixedRotationAdjustments(null); - mApplicationContext.getResources().overrideDisplayAdjustments(null); - mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds( - null); - mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds( - null); - mDisplayInfo.rotation = ROTATION_0; - - mDisplayManagerGlobal = mock(DisplayManagerGlobal.class); - doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt()); - } - - @After - public void teardownTests() { - if (mMockitoSession != null) { - mMockitoSession.finishMocking(); - } - Mockito.framework().clearInlineMocks(); - } - - @Test - public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() { - setDisplayInfoPortrait(mDisplayInfo); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); - assertThat(display.getDisplayAdjustments()).isEqualTo( - DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); - DisplayInfo actualDisplayInfo = new DisplayInfo(); - display.getDisplayInfo(actualDisplayInfo); - verifyDisplayInfo(actualDisplayInfo, mDisplayInfo); - } - - @Test - public void testConstructor_defaultResources_matchesDisplayInfo() { - setDisplayInfoPortrait(mDisplayInfo); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - assertThat(display.getDisplayAdjustments()).isEqualTo( - mApplicationContext.getResources().getDisplayAdjustments()); - DisplayInfo actualDisplayInfo = new DisplayInfo(); - display.getDisplayInfo(actualDisplayInfo); - verifyDisplayInfo(actualDisplayInfo, mDisplayInfo); - } - - @Test - public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() { - setDisplayInfoPortrait(mDisplayInfo); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); - assertThat(display.getRotation()).isEqualTo(ROTATION_0); - } - - @Test - public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - // GIVEN fixed rotation adjustments are rotated, but no override is set. - DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; - final FixedRotationAdjustments fixedRotationAdjustments = - new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT, - DisplayCutout.NO_CUTOUT); - displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments); - // GIVEN display is constructed with display adjustments. - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - displayAdjustments); - // THEN rotation is not adjusted since no override was set. - assertThat(display.getRotation()).isEqualTo(ROTATION_0); - } - - @Test - public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - // GIVEN fixed rotation adjustments are rotated, but no override is set. - setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90); - // GIVEN display is constructed with default resources. - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN rotation is not adjusted since no override is set. - assertThat(display.getRotation()).isEqualTo(ROTATION_0); - } - - @Test - public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - // GIVEN fixed rotation adjustments are rotated, and an override is set. - setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90); - // GIVEN display is constructed with default resources. - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN rotation is adjusted since an override is set. - assertThat(display.getRotation()).isEqualTo(ROTATION_90); - } - - @Test - public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real size matches display orientation. - verifyRealSizeIsPortrait(display); - } - - @Test - public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() { - // GIVEN display is rotated. - setDisplayInfoLandscape(mDisplayInfo); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real size matches display orientation. - verifyRealSizeIsLandscape(display); - } - - @Test - public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); - // THEN real size matches display orientation. - verifyRealSizeIsPortrait(display); - } - - @Test - public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() { - // GIVEN display is rotated. - setDisplayInfoLandscape(mDisplayInfo); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); - // THEN real size matches display orientation. - verifyRealSizeIsLandscape(display); - } - - @Test - public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() { - // GIVEN display is rotated. - setDisplayInfoLandscape(mDisplayInfo); - // GIVEN fixed rotation adjustments are rotated. - setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0); - // GIVEN display is constructed with default resources. - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real size matches display orientation. - verifyRealSizeIsLandscape(display); - } - - @Test - public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - // GIVEN fixed rotation adjustments are rotated. - setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90); - // GIVEN display is constructed with default resources. - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real size matches display orientation. - verifyRealSizeIsPortrait(display); - } - - @Test - public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() { - // GIVEN display is rotated. - setDisplayInfoLandscape(mDisplayInfo); - // GIVEN fixed rotation adjustments are rotated, and an override is set. - setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0); - // GIVEN display is constructed with default resources. - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real size matches app orientation. - verifyRealSizeIsPortrait(display); - } - - @Test - public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - // GIVEN fixed rotation adjustments are rotated, and an override is set. - setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90); - // GIVEN display is constructed with default resources. - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real size matches app orientation. - verifyRealSizeIsLandscape(display); - } - - @Test - public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - // GIVEN app is letterboxed. - setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(), - sAppBoundsPortrait); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real size matches app bounds. - verifyRealSizeMatchesApp(display, sAppBoundsPortrait); - } - - @Test - public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() { - // GIVEN display is rotated. - setDisplayInfoLandscape(mDisplayInfo); - // GIVEN app is letterboxed. - setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(), - sAppBoundsLandscape); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real size matches app bounds. - verifyRealSizeMatchesApp(display, sAppBoundsLandscape); - } - - @Test - public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real metrics matches display orientation. - verifyRealMetricsIsPortrait(display); - } - - @Test - public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() { - // GIVEN display is rotated. - setDisplayInfoLandscape(mDisplayInfo); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real metrics matches display orientation. - verifyRealMetricsIsLandscape(display); - } - - @Test - public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); - // THEN real metrics matches display orientation. - verifyRealMetricsIsPortrait(display); - } - - @Test - public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() { - // GIVEN display is rotated. - setDisplayInfoLandscape(mDisplayInfo); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); - // THEN real metrics matches display orientation. - verifyRealMetricsIsLandscape(display); - } - - @Test - public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() { - // GIVEN display is rotated. - setDisplayInfoLandscape(mDisplayInfo); - // GIVEN fixed rotation adjustments are rotated. - setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0); - // GIVEN display is constructed with default resources. - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real metrics matches display orientation. - verifyRealMetricsIsLandscape(display); - } - - @Test - public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - // GIVEN fixed rotation adjustments are rotated. - setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90); - // GIVEN display is constructed with default resources. - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real metrics matches display orientation. - verifyRealMetricsIsPortrait(display); - } - - @Test - public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() { - // GIVEN display is rotated. - setDisplayInfoLandscape(mDisplayInfo); - // GIVEN fixed rotation adjustments are rotated with an override. - setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0); - // GIVEN display is constructed with default resources. - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real metrics matches app orientation. - verifyRealMetricsIsPortrait(display); - } - - @Test - public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - // GIVEN fixed rotation adjustments are rotated. - setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90); - // GIVEN display is constructed with default resources. - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real metrics matches app orientation. - verifyRealMetricsIsLandscape(display); - } - - @Test - public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() { - // GIVEN display is not rotated. - setDisplayInfoPortrait(mDisplayInfo); - // GIVEN app is letterboxed. - setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(), - sAppBoundsPortrait); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real metrics matches app bounds. - verifyRealMetricsMatchesApp(display, sAppBoundsPortrait); - } - - @Test - public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() { - // GIVEN display is rotated. - setDisplayInfoLandscape(mDisplayInfo); - // GIVEN app is letterboxed. - setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(), - sAppBoundsLandscape); - final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, - mApplicationContext.getResources()); - // THEN real metrics matches app bounds. - verifyRealMetricsMatchesApp(display, sAppBoundsLandscape); - } - - // Given rotated display dimensions, calculate the letterboxed app bounds. - private static Rect buildAppBounds(int displayWidth, int displayHeight) { - final int midWidth = displayWidth / 2; - final int left = midWidth - (APP_WIDTH / 2); - final int right = midWidth + (APP_WIDTH / 2); - final int midHeight = displayHeight / 2; - // Coordinate system starts at top left. - final int top = midHeight - (APP_HEIGHT / 2); - final int bottom = midHeight + (APP_HEIGHT / 2); - return new Rect(left, top, right, bottom); - } - - private static void setDisplayInfoLandscape(DisplayInfo displayInfo) { - displayInfo.rotation = ROTATION_90; - // Flip width & height assignment since the device is rotated. - displayInfo.logicalWidth = LOGICAL_HEIGHT; - displayInfo.logicalHeight = LOGICAL_WIDTH; - } - - private static void setDisplayInfoPortrait(DisplayInfo displayInfo) { - displayInfo.rotation = ROTATION_0; - displayInfo.logicalWidth = LOGICAL_WIDTH; - displayInfo.logicalHeight = LOGICAL_HEIGHT; - } - - /** - * Set max bounds to be sandboxed to the app bounds, indicating the app is in - * size compat mode or letterbox. - */ - private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) { - resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds); - } - - /** - * Do not compare entire display info, since it is updated to match display the test is run on. - */ - private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) { - assertThat(actual.displayId).isEqualTo(expected.displayId); - assertThat(actual.rotation).isEqualTo(expected.rotation); - assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH); - assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT); - } - - private static void verifyRealSizeIsLandscape(Display display) { - Point size = new Point(); - display.getRealSize(size); - // Flip the width and height check since the device is rotated. - assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH)); - } - - private static void verifyRealMetricsIsLandscape(Display display) { - DisplayMetrics metrics = new DisplayMetrics(); - display.getRealMetrics(metrics); - // Flip the width and height check since the device is rotated. - assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT); - assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH); - } - - private static void verifyRealSizeIsPortrait(Display display) { - Point size = new Point(); - display.getRealSize(size); - assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT)); - } - - private static void verifyRealMetricsIsPortrait(Display display) { - DisplayMetrics metrics = new DisplayMetrics(); - display.getRealMetrics(metrics); - assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH); - assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT); - } - - private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) { - Point size = new Point(); - display.getRealSize(size); - assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height())); - } - - private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) { - DisplayMetrics metrics = new DisplayMetrics(); - display.getRealMetrics(metrics); - assertThat(metrics.widthPixels).isEqualTo(appBounds.width()); - assertThat(metrics.heightPixels).isEqualTo(appBounds.height()); - } - - private static FixedRotationAdjustments setOverrideFixedRotationAdjustments( - Resources resources, @Surface.Rotation int rotation) { - FixedRotationAdjustments fixedRotationAdjustments = - setFixedRotationAdjustments(resources, rotation); - resources.overrideDisplayAdjustments( - buildOverrideRotationAdjustments(fixedRotationAdjustments)); - return fixedRotationAdjustments; - } - - private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources, - @Surface.Rotation int rotation) { - final FixedRotationAdjustments fixedRotationAdjustments = - new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT, - DisplayCutout.NO_CUTOUT); - resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments); - return fixedRotationAdjustments; - } - - private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments( - FixedRotationAdjustments fixedRotationAdjustments) { - return consumedDisplayAdjustments - -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments); - } -} diff --git a/data/etc/platform.xml b/data/etc/platform.xml index b0ae9b98e7f5..ea42246e8262 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -165,6 +165,7 @@ <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" /> <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" /> <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="audioserver" /> + <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="audioserver" /> <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" /> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index cdc61a1c5752..0484a9ab582e 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -465,10 +465,15 @@ applications that come with the platform <permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" /> <!-- Permission required for CTS test CarrierMessagingServiceWrapperTest --> <permission name="android.permission.BIND_CARRIER_SERVICES"/> + <!-- Permission required for CTS test - MusicRecognitionManagerTest --> + <permission name="android.permission.MANAGE_MUSIC_RECOGNITION" /> <!-- Permission required for CTS test - CallLogTest --> <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/> <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/> <permission name="android.permission.MODIFY_QUIET_MODE" /> + <!-- Permission required for GTS test - GtsAssistIntentTestCases --> + <permission name="android.permission.MANAGE_SOUND_TRIGGER" /> + <permission name="android.permission.CAPTURE_AUDIO_HOTWORD" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> @@ -482,6 +487,8 @@ applications that come with the platform <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/> <!-- Permissions required for quick settings tile --> <permission name="android.permission.STATUS_BAR"/> + <!-- Permissions required to query Betterbug --> + <permission name="android.permission.QUERY_ALL_PACKAGES"/> </privapp-permissions> <privapp-permissions package="com.android.tv"> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index ac8a296123c6..222c9bdf2cb4 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1903,6 +1903,12 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "123161180": { + "message": "SEVER CHILDREN", + "level": "INFO", + "group": "WM_SHOW_TRANSACTIONS", + "at": "com\/android\/server\/wm\/WindowSurfaceController.java" + }, "140319294": { "message": "IME target changed within ActivityRecord", "level": "DEBUG", @@ -2137,12 +2143,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "332390227": { - "message": "Sandbox max bounds for uid %s to bounds %s due to letterboxing? %s mismatch with parent bounds? %s size compat mode %s", - "level": "DEBUG", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "342460966": { "message": "DRAG %s: pos=(%d,%d)", "level": "INFO", @@ -2623,12 +2623,6 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/WindowOrganizerController.java" }, - "910200295": { - "message": "Sandbox max bounds due to mismatched orientation with parent, to %s vs DisplayArea %s", - "level": "DEBUG", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/Task.java" - }, "913494177": { "message": "removeAllWindowsIfPossible: removing win=%s", "level": "WARN", diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java index a7d3f7980d37..5b79d76dd6b9 100644 --- a/graphics/java/android/graphics/ImageFormat.java +++ b/graphics/java/android/graphics/ImageFormat.java @@ -175,6 +175,39 @@ public class ImageFormat { public static final int Y16 = 0x20363159; /** + * <p>Android YUV P010 format.</p> + * + * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane + * followed immediately by a Wx(H/2) CbCr plane. Each sample is + * represented by a 16-bit little-endian value, with the lower 6 bits set + * to zero. + * + * <p>This format assumes + * <ul> + * <li>an even height</li> + * <li>a vertical stride equal to the height</li> + * </ul> + * </p> + * + * <pre> stride_in_bytes = stride * 2 </pre> + * <pre> y_size = stride_in_bytes * height </pre> + * <pre> cbcr_size = stride_in_bytes * (height / 2) </pre> + * <pre> cb_offset = y_size </pre> + * <pre> cr_offset = cb_offset + 2 </pre> + * + * <p>For example, the {@link android.media.Image} object can provide data + * in this format from a {@link android.hardware.camera2.CameraDevice} + * through a {@link android.media.ImageReader} object if this format is + * supported by {@link android.hardware.camera2.CameraDevice}.</p> + * + * @see android.media.Image + * @see android.media.ImageReader + * @see android.hardware.camera2.CameraDevice + * + */ + public static final int YCBCR_P010 = 0x36; + + /** * YCbCr format, used for video. * * <p>For the {@link android.hardware.camera2} API, the {@link #YUV_420_888} format is @@ -807,6 +840,8 @@ public class ImageFormat { case RAW_DEPTH: case RAW_SENSOR: return 16; + case YCBCR_P010: + return 20; case RAW_DEPTH10: case RAW10: return 10; @@ -839,6 +874,7 @@ public class ImageFormat { case YUV_420_888: case YUV_422_888: case YUV_444_888: + case YCBCR_P010: case FLEX_RGB_888: case FLEX_RGBA_8888: case RAW_SENSOR: diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java index 496e4707dbff..ad4c3fe86175 100644 --- a/graphics/java/android/graphics/RenderEffect.java +++ b/graphics/java/android/graphics/RenderEffect.java @@ -190,7 +190,7 @@ public final class RenderEffect { } /** - * Create a filter that applies the color filter to the provided RenderEffect + * Create a {@link RenderEffect} that applies the color filter to the provided RenderEffect * * @param colorFilter ColorFilter applied to the content in the input RenderEffect * @param renderEffect Source to be transformed by the specified {@link ColorFilter} @@ -209,7 +209,7 @@ public final class RenderEffect { } /** - * Create a filter that applies the color filter to the contents of the + * Create a {@link RenderEffect} that applies the color filter to the contents of the * {@link android.graphics.RenderNode} that this RenderEffect is installed on * @param colorFilter ColorFilter applied to the content in the input RenderEffect */ @@ -224,7 +224,7 @@ public final class RenderEffect { } /** - * {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances + * Create a {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances * combined by the specified {@link BlendMode} * * @param dst The Dst pixels used in blending @@ -248,8 +248,8 @@ public final class RenderEffect { } /** - * Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are - * treated as the source bitmap passed to 'outer', i.e. + * Create a {@link RenderEffect} that composes 'inner' with 'outer', such that the results of + * 'inner' are treated as the source bitmap passed to 'outer', i.e. * * <pre> * {@code @@ -278,6 +278,18 @@ public final class RenderEffect { ); } + /** + * Create a {@link RenderEffect} that renders the contents of the input {@link Shader}. + * This is useful to create an input for other {@link RenderEffect} types such as + * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} + * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or + * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)}. + */ + @NonNull + public static RenderEffect createShaderEffect(@NonNull Shader shader) { + return new RenderEffect(nativeCreateShaderEffect(shader.getNativeInstance())); + } + private final long mNativeRenderEffect; /* only constructed from static factory methods */ @@ -305,5 +317,6 @@ public final class RenderEffect { private static native long nativeCreateColorFilterEffect(long colorFilter, long nativeInput); private static native long nativeCreateBlendModeEffect(long dst, long src, int blendmode); private static native long nativeCreateChainEffect(long outer, long inner); + private static native long nativeCreateShaderEffect(long shader); private static native long nativeGetFinalizer(); } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 005a72661091..35e6b8595c2a 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -42,6 +42,7 @@ import android.provider.FontsContract; import android.system.ErrnoException; import android.system.OsConstants; import android.text.FontConfig; +import android.util.ArrayMap; import android.util.Base64; import android.util.LongSparseArray; import android.util.LruCache; @@ -67,7 +68,6 @@ import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -147,7 +147,7 @@ public class Typeface { */ @GuardedBy("SYSTEM_FONT_MAP_LOCK") @UnsupportedAppUsage(trackingBug = 123769347) - static final Map<String, Typeface> sSystemFontMap = new HashMap<>(); + static final Map<String, Typeface> sSystemFontMap = new ArrayMap<>(); // DirectByteBuffer object to hold sSystemFontMap's backing memory mapping. static ByteBuffer sSystemFontMapBuffer = null; @@ -1231,7 +1231,7 @@ public class Typeface { /** @hide */ @VisibleForTesting public static Map<String, Typeface> deserializeFontMap(ByteBuffer buffer) throws IOException { - Map<String, Typeface> fontMap = new HashMap<>(); + Map<String, Typeface> fontMap = new ArrayMap<>(); int typefacesBytesCount = buffer.getInt(); long[] nativePtrs = nativeReadTypefaces(buffer.slice()); if (nativePtrs == null) { diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java index 9214ff1eb088..b153c995a7f4 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -26,8 +26,7 @@ import android.graphics.Paint; import android.graphics.RectF; import android.os.LocaleList; import android.os.ParcelFileDescriptor; -import android.util.Log; -import android.util.LongSparseArray; +import android.text.TextUtils; import android.util.LongSparseLongArray; import android.util.TypedValue; @@ -44,7 +43,6 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; @@ -61,14 +59,9 @@ public final class Font { private static final int STYLE_ITALIC = 1; private static final int STYLE_NORMAL = 0; - private static final Object MAP_LOCK = new Object(); - // We need to have mapping from native ptr to Font object for later accessing from TextShape - // result since Typeface doesn't have reference to Font object and it is not always created from - // Font object. Sometimes Typeface is created in native layer only and there might not be Font - // object in Java layer. So, if not found in this cache, create new Font object for API user. - @GuardedBy("MAP_LOCK") - private static final LongSparseArray<WeakReference<Font>> FONT_PTR_MAP = - new LongSparseArray<>(); + private static final NativeAllocationRegistry BUFFER_REGISTRY = + NativeAllocationRegistry.createMalloced( + ByteBuffer.class.getClassLoader(), nGetReleaseNativeFont()); private static final Object SOURCE_ID_LOCK = new Object(); @GuardedBy("SOURCE_ID_LOCK") @@ -79,9 +72,7 @@ public final class Font { * A builder class for creating new Font. */ public static final class Builder { - private static final NativeAllocationRegistry sFontRegistry = - NativeAllocationRegistry.createMalloced(Font.class.getClassLoader(), - nGetReleaseNativeFont()); + private @Nullable ByteBuffer mBuffer; private @Nullable File mFile; @@ -484,26 +475,15 @@ public final class Font { final String filePath = mFile == null ? "" : mFile.getAbsolutePath(); long ptr; - int fontIdentifier; + final Font font; if (mFont == null) { ptr = nBuild(builderPtr, readonlyBuffer, filePath, mLocaleList, mWeight, italic, mTtcIndex); - long fontBufferPtr = nGetFontBufferAddress(ptr); - synchronized (SOURCE_ID_LOCK) { - long id = FONT_SOURCE_ID_MAP.get(fontBufferPtr, -1); - if (id == -1) { - id = FONT_SOURCE_ID_MAP.size(); - FONT_SOURCE_ID_MAP.put(fontBufferPtr, id); - } - fontIdentifier = (int) id; - } + font = new Font(ptr); } else { ptr = nClone(mFont.getNativePtr(), builderPtr, mWeight, italic, mTtcIndex); - fontIdentifier = mFont.mSourceIdentifier; + font = new Font(ptr); } - final Font font = new Font(ptr, readonlyBuffer, mFile, - new FontStyle(mWeight, slant), mTtcIndex, mAxes, mLocaleList, fontIdentifier); - sFontRegistry.registerNativeAllocation(font, ptr); return font; } @@ -525,33 +505,32 @@ public final class Font { } private final long mNativePtr; // address of the shared ptr of minikin::Font - private final @NonNull ByteBuffer mBuffer; - private final @Nullable File mFile; - private final FontStyle mFontStyle; - private final @IntRange(from = 0) int mTtcIndex; - private final @Nullable FontVariationAxis[] mAxes; - private final @NonNull String mLocaleList; - private final int mSourceIdentifier; // An identifier of font source data. + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private @NonNull ByteBuffer mBuffer = null; + @GuardedBy("mLock") + private boolean mIsFileInitialized = false; + @GuardedBy("mLock") + private @Nullable File mFile = null; + @GuardedBy("mLock") + private FontStyle mFontStyle = null; + @GuardedBy("mLock") + private @Nullable FontVariationAxis[] mAxes = null; + @GuardedBy("mLock") + private @NonNull LocaleList mLocaleList = null; + @GuardedBy("mLock") + private int mSourceIdentifier = -1; /** * Use Builder instead + * + * Caller must increment underlying minikin::Font ref count. + * + * @hide */ - private Font(long nativePtr, @NonNull ByteBuffer buffer, @Nullable File file, - @NonNull FontStyle fontStyle, @IntRange(from = 0) int ttcIndex, - @Nullable FontVariationAxis[] axes, @NonNull String localeList, - int sourceIdentifier) { - mBuffer = buffer; - mFile = file; - mFontStyle = fontStyle; + public Font(long nativePtr) { mNativePtr = nativePtr; - mTtcIndex = ttcIndex; - mAxes = axes; - mLocaleList = localeList; - mSourceIdentifier = sourceIdentifier; - - synchronized (MAP_LOCK) { - FONT_PTR_MAP.append(nGetNativeFontPtr(mNativePtr), new WeakReference<>(this)); - } } /** @@ -563,7 +542,22 @@ public final class Font { * @return a font buffer */ public @NonNull ByteBuffer getBuffer() { - return mBuffer; + synchronized (mLock) { + if (mBuffer == null) { + // Create new instance of native FontWrapper, i.e. incrementing ref count of + // minikin Font instance for keeping buffer fo ByteBuffer reference which may live + // longer than this object. + long ref = nCloneFont(mNativePtr); + ByteBuffer fromNative = nNewByteBuffer(mNativePtr); + + // Bind ByteBuffer's lifecycle with underlying font object. + BUFFER_REGISTRY.registerNativeAllocation(fromNative, ref); + + // JNI NewDirectBuffer creates writable ByteBuffer even if it is mmaped readonly. + mBuffer = fromNative.asReadOnlyBuffer(); + } + return mBuffer; + } } /** @@ -574,7 +568,16 @@ public final class Font { * @return a file path of the font */ public @Nullable File getFile() { - return mFile; + synchronized (mLock) { + if (!mIsFileInitialized) { + String path = nGetFontPath(mNativePtr); + if (!TextUtils.isEmpty(path)) { + mFile = new File(path); + } + mIsFileInitialized = true; + } + return mFile; + } } /** @@ -585,7 +588,16 @@ public final class Font { * @return a font style */ public @NonNull FontStyle getStyle() { - return mFontStyle; + synchronized (mLock) { + if (mFontStyle == null) { + int packedStyle = nGetPackedStyle(mNativePtr); + mFontStyle = new FontStyle( + FontFileUtil.unpackWeight(packedStyle), + FontFileUtil.unpackItalic(packedStyle) + ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT); + } + return mFontStyle; + } } /** @@ -597,7 +609,7 @@ public final class Font { * @return a TTC index value */ public @IntRange(from = 0) int getTtcIndex() { - return mTtcIndex; + return nGetIndex(mNativePtr); } /** @@ -608,7 +620,23 @@ public final class Font { * @return font variation settings */ public @Nullable FontVariationAxis[] getAxes() { - return mAxes == null ? null : mAxes.clone(); + synchronized (mLock) { + if (mAxes == null) { + int axisCount = nGetAxisCount(mNativePtr); + mAxes = new FontVariationAxis[axisCount]; + char[] charBuffer = new char[4]; + for (int i = 0; i < axisCount; ++i) { + long packedAxis = nGetAxisInfo(mNativePtr, i); + float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL)); + charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >>> 56); + charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >>> 48); + charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >>> 40); + charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >>> 32); + mAxes[i] = new FontVariationAxis(new String(charBuffer), value); + } + } + } + return mAxes; } /** @@ -618,7 +646,17 @@ public final class Font { * @return a locale list */ public @NonNull LocaleList getLocaleList() { - return LocaleList.forLanguageTags(mLocaleList); + synchronized (mLock) { + if (mLocaleList == null) { + String langTags = nGetLocaleList(mNativePtr); + if (TextUtils.isEmpty(langTags)) { + mLocaleList = LocaleList.getEmptyLocaleList(); + } else { + mLocaleList = LocaleList.forLanguageTags(langTags); + } + } + return mLocaleList; + } } /** @@ -713,7 +751,20 @@ public final class Font { * @return an unique identifier for the font source data. */ public int getSourceIdentifier() { - return mSourceIdentifier; + synchronized (mLock) { + if (mSourceIdentifier == -1) { + long bufferAddress = nGetBufferAddress(mNativePtr); + synchronized (SOURCE_ID_LOCK) { + long id = FONT_SOURCE_ID_MAP.get(bufferAddress, -1); + if (id == -1) { + id = FONT_SOURCE_ID_MAP.size(); + FONT_SOURCE_ID_MAP.append(bufferAddress, id); + } + mSourceIdentifier = (int) id; + } + } + return mSourceIdentifier; + } } /** @@ -736,13 +787,16 @@ public final class Font { private boolean isSameSource(@NonNull Font other) { Objects.requireNonNull(other); + ByteBuffer myBuffer = getBuffer(); + ByteBuffer otherBuffer = other.getBuffer(); + // Shortcut for the same instance. - if (mBuffer == other.mBuffer) { + if (myBuffer == otherBuffer) { return true; } // Shortcut for different font buffer check by comparing size. - if (mBuffer.capacity() != other.mBuffer.capacity()) { + if (myBuffer.capacity() != otherBuffer.capacity()) { return false; } @@ -750,15 +804,15 @@ public final class Font { // underlying native font object holds buffer address, check if this buffer points exactly // the same address as a shortcut of equality. For being compatible with of API30 or before, // check buffer position even if the buffer points the same address. - if (mSourceIdentifier == other.mSourceIdentifier - && mBuffer.position() == other.mBuffer.position()) { + if (getSourceIdentifier() == other.getSourceIdentifier() + && myBuffer.position() == otherBuffer.position()) { return true; } // Unfortunately, need to compare bytes one-by-one since the buffer may be different font // file but has the same file size, or two font has same content but they are allocated // differently. For being compatible with API30 ore before, compare with ByteBuffer#equals. - return mBuffer.equals(other.mBuffer); + return myBuffer.equals(otherBuffer); } @Override @@ -769,10 +823,20 @@ public final class Font { if (!(o instanceof Font)) { return false; } + Font f = (Font) o; - boolean paramEqual = mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex - && Arrays.equals(f.mAxes, mAxes) && Objects.equals(f.mLocaleList, mLocaleList) - && Objects.equals(mFile, f.mFile); + + // The underlying minikin::Font object is the source of the truth of font information. Thus, + // Pointer equality is the object equality. + if (nGetMinikinFontPtr(mNativePtr) == nGetMinikinFontPtr(f.mNativePtr)) { + return true; + } + + boolean paramEqual = f.getStyle().equals(getStyle()) + && f.getTtcIndex() == getTtcIndex() + && Arrays.equals(f.getAxes(), getAxes()) + && Objects.equals(f.getLocaleList(), getLocaleList()) + && Objects.equals(getFile(), f.getFile()); if (!paramEqual) { return false; @@ -784,64 +848,42 @@ public final class Font { @Override public int hashCode() { return Objects.hash( - mFontStyle, - mTtcIndex, - Arrays.hashCode(mAxes), + getStyle(), + getTtcIndex(), + Arrays.hashCode(getAxes()), // Use Buffer size instead of ByteBuffer#hashCode since ByteBuffer#hashCode traverse // data which is not performant e.g. for HashMap. The hash collision are less likely // happens because it is unlikely happens the different font files has exactly the // same size. - mLocaleList); + getLocaleList()); } @Override public String toString() { return "Font {" - + "path=" + mFile - + ", style=" + mFontStyle - + ", ttcIndex=" + mTtcIndex - + ", axes=" + FontVariationAxis.toFontVariationSettings(mAxes) - + ", localeList=" + mLocaleList - + ", buffer=" + mBuffer + + "path=" + getFile() + + ", style=" + getStyle() + + ", ttcIndex=" + getTtcIndex() + + ", axes=" + FontVariationAxis.toFontVariationSettings(getAxes()) + + ", localeList=" + getLocaleList() + + ", buffer=" + getBuffer() + "}"; } - /** - * Lookup Font object from native pointer or create new one if not found. - * @hide - */ - public static Font findOrCreateFontFromNativePtr(long ptr) { - // First, lookup from known mapps. - synchronized (MAP_LOCK) { - WeakReference<Font> fontRef = FONT_PTR_MAP.get(ptr); - if (fontRef != null) { - Font font = fontRef.get(); - if (font != null) { - return font; - } - } + @CriticalNative + private static native long nGetMinikinFontPtr(long font); - // If not found, create Font object from native object for Java API users. - ByteBuffer buffer = NativeFontBufferHelper.refByteBuffer(ptr); - NativeFont.Font font = NativeFont.readNativeFont(ptr); + @CriticalNative + private static native long nCloneFont(long font); - Font.Builder builder = new Font.Builder(buffer, font.getFile(), "") - .setWeight(font.getStyle().getWeight()) - .setSlant(font.getStyle().getSlant()) - .setTtcIndex(font.getIndex()) - .setFontVariationSettings(font.getAxes()); + @FastNative + private static native ByteBuffer nNewByteBuffer(long font); - Font newFont = null; - try { - newFont = builder.build(); - FONT_PTR_MAP.append(ptr, new WeakReference<>(newFont)); - } catch (IOException e) { - // This must not happen since the buffer was already created once. - Log.e("Font", "Failed to create font object from existing buffer.", e); - } - return newFont; - } - } + @CriticalNative + private static native long nGetBufferAddress(long font); + + @CriticalNative + private static native long nGetReleaseNativeFont(); @FastNative private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect); @@ -849,9 +891,21 @@ public final class Font { @FastNative private static native float nGetFontMetrics(long font, long paint, Paint.FontMetrics metrics); + @FastNative + private static native String nGetFontPath(long fontPtr); + + @FastNative + private static native String nGetLocaleList(long familyPtr); + + @CriticalNative + private static native int nGetPackedStyle(long fontPtr); + + @CriticalNative + private static native int nGetIndex(long fontPtr); + @CriticalNative - private static native long nGetNativeFontPtr(long ptr); + private static native int nGetAxisCount(long fontPtr); @CriticalNative - private static native long nGetFontBufferAddress(long font); + private static native long nGetAxisInfo(long fontPtr, int i); } diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java index c29c194861f1..8c13d3e7e51d 100644 --- a/graphics/java/android/graphics/fonts/FontFamily.java +++ b/graphics/java/android/graphics/fonts/FontFamily.java @@ -20,15 +20,16 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.text.FontConfig; +import android.util.SparseIntArray; import com.android.internal.util.Preconditions; import dalvik.annotation.optimization.CriticalNative; +import dalvik.annotation.optimization.FastNative; import libcore.util.NativeAllocationRegistry; import java.util.ArrayList; -import java.util.HashSet; /** * A font family class can be used for creating Typeface. @@ -68,7 +69,9 @@ public final class FontFamily { nGetReleaseNativeFamily()); private final ArrayList<Font> mFonts = new ArrayList<>(); - private final HashSet<Integer> mStyleHashSet = new HashSet<>(); + // Most FontFamily only has regular, bold, italic, bold-italic. Thus 4 should be good for + // initial capacity. + private final SparseIntArray mStyles = new SparseIntArray(4); /** * Constructs a builder. @@ -77,7 +80,7 @@ public final class FontFamily { */ public Builder(@NonNull Font font) { Preconditions.checkNotNull(font, "font can not be null"); - mStyleHashSet.add(makeStyleIdentifier(font)); + mStyles.append(makeStyleIdentifier(font), 0); mFonts.add(font); } @@ -97,9 +100,11 @@ public final class FontFamily { */ public @NonNull Builder addFont(@NonNull Font font) { Preconditions.checkNotNull(font, "font can not be null"); - if (!mStyleHashSet.add(makeStyleIdentifier(font))) { + int key = makeStyleIdentifier(font); + if (mStyles.indexOfKey(key) >= 0) { throw new IllegalArgumentException(font + " has already been added"); } + mStyles.append(key, 0); mFonts.add(font); return this; } @@ -120,7 +125,7 @@ public final class FontFamily { nAddFont(builderPtr, mFonts.get(i).getNativePtr()); } final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback); - final FontFamily family = new FontFamily(mFonts, langTags, variant, ptr); + final FontFamily family = new FontFamily(mFonts, ptr); sFamilyRegistory.registerNativeAllocation(family, ptr); return family; } @@ -138,16 +143,10 @@ public final class FontFamily { private static native long nGetReleaseNativeFamily(); } - private final ArrayList<Font> mFonts; - private final String mLangTags; - private final int mVariant; private final long mNativePtr; // Use Builder instead. - private FontFamily(@NonNull ArrayList<Font> fonts, String langTags, int variant, long ptr) { - mFonts = fonts; - mLangTags = langTags; - mVariant = variant; + private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) { mNativePtr = ptr; } @@ -157,7 +156,7 @@ public final class FontFamily { * @return a BCP-47 compliant language tag. */ public @Nullable String getLangTags() { - return mLangTags; + return nGetLangTags(mNativePtr); } /** @@ -165,7 +164,7 @@ public final class FontFamily { * @return a family variant */ public int getVariant() { - return mVariant; + return nGetVariant(mNativePtr); } /** @@ -175,7 +174,10 @@ public final class FontFamily { * @return a registered font */ public @NonNull Font getFont(@IntRange(from = 0) int index) { - return mFonts.get(index); + if (index < 0 || getSize() <= index) { + throw new IndexOutOfBoundsException(); + } + return new Font(nGetFont(mNativePtr, index)); } /** @@ -184,11 +186,23 @@ public final class FontFamily { * @return the number of fonts registered in this family. */ public @IntRange(from = 1) int getSize() { - return mFonts.size(); + return nGetFontSize(mNativePtr); } /** @hide */ public long getNativePtr() { return mNativePtr; } + + @CriticalNative + private static native int nGetFontSize(long family); + + @CriticalNative + private static native long nGetFont(long family, int i); + + @FastNative + private static native String nGetLangTags(long family); + + @CriticalNative + private static native int nGetVariant(long family); } diff --git a/graphics/java/android/graphics/fonts/NativeFont.java b/graphics/java/android/graphics/fonts/NativeFont.java deleted file mode 100644 index 9e9d76a89385..000000000000 --- a/graphics/java/android/graphics/fonts/NativeFont.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.graphics.fonts; - -import android.graphics.Typeface; - -import dalvik.annotation.optimization.CriticalNative; -import dalvik.annotation.optimization.FastNative; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -/** - * Read native font objects. - * - * @hide - */ -public class NativeFont { - - /** - * Represents native font object. - */ - public static final class Font { - private final File mFile; - private final int mIndex; - private final FontVariationAxis[] mAxes; - private final FontStyle mStyle; - - public Font(File file, int index, FontVariationAxis[] axes, FontStyle style) { - mFile = file; - mIndex = index; - mAxes = axes; - mStyle = style; - } - - public File getFile() { - return mFile; - } - - public FontVariationAxis[] getAxes() { - return mAxes; - } - - public FontStyle getStyle() { - return mStyle; - } - - public int getIndex() { - return mIndex; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Font font = (Font) o; - return mIndex == font.mIndex && mFile.equals(font.mFile) - && Arrays.equals(mAxes, font.mAxes) && mStyle.equals(font.mStyle); - } - - @Override - public int hashCode() { - int result = Objects.hash(mFile, mIndex, mStyle); - result = 31 * result + Arrays.hashCode(mAxes); - return result; - } - } - - /** - * Represents native font family object. - */ - public static final class Family { - private final List<Font> mFonts; - private final String mLocale; - - public Family(List<Font> fonts, String locale) { - mFonts = fonts; - mLocale = locale; - } - - public List<Font> getFonts() { - return mFonts; - } - - public String getLocale() { - return mLocale; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Family family = (Family) o; - return mFonts.equals(family.mFonts) && mLocale.equals(family.mLocale); - } - - @Override - public int hashCode() { - return Objects.hash(mFonts, mLocale); - } - } - - /** - * Get underlying font families from Typeface - * - * @param typeface a typeface - * @return list of family - */ - public static List<Family> readTypeface(Typeface typeface) { - int familyCount = nGetFamilyCount(typeface.native_instance); - List<Family> result = new ArrayList<>(familyCount); - for (int i = 0; i < familyCount; ++i) { - result.add(readNativeFamily(nGetFamily(typeface.native_instance, i))); - } - return result; - } - - /** - * Read family object from native pointer - * - * @param familyPtr a font family pointer - * @return a family - */ - public static Family readNativeFamily(long familyPtr) { - int fontCount = nGetFontCount(familyPtr); - List<Font> result = new ArrayList<>(fontCount); - for (int i = 0; i < fontCount; ++i) { - result.add(readNativeFont(nGetFont(familyPtr, i))); - } - String localeList = nGetLocaleList(familyPtr); - return new Family(result, localeList); - } - - /** - * Read font object from native pointer. - * - * @param ptr a font pointer - * @return a font - */ - public static Font readNativeFont(long ptr) { - long packed = nGetFontInfo(ptr); - int weight = (int) (packed & 0x0000_0000_0000_FFFFL); - boolean italic = (packed & 0x0000_0000_0001_0000L) != 0; - int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32); - int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48); - FontVariationAxis[] axes = new FontVariationAxis[axisCount]; - char[] charBuffer = new char[4]; - for (int i = 0; i < axisCount; ++i) { - long packedAxis = nGetAxisInfo(ptr, i); - float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL)); - charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56); - charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48); - charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40); - charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32); - axes[i] = new FontVariationAxis(new String(charBuffer), value); - } - String path = nGetFontPath(ptr); - File file = (path == null) ? null : new File(path); - FontStyle style = new FontStyle(weight, - italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT); - - return new Font(file, ttcIndex, axes, style); - } - - @CriticalNative - private static native int nGetFamilyCount(long ptr); - - @CriticalNative - private static native long nGetFamily(long ptr, int index); - - @FastNative - private static native String nGetLocaleList(long familyPtr); - - @CriticalNative - private static native long nGetFont(long familyPtr, int fontIndex); - - @CriticalNative - private static native int nGetFontCount(long familyPtr); - - @CriticalNative - private static native long nGetFontInfo(long fontPtr); - - @CriticalNative - private static native long nGetAxisInfo(long fontPtr, int i); - - @FastNative - private static native String nGetFontPath(long fontPtr); -} diff --git a/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java b/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java deleted file mode 100644 index 5655e7fafc1b..000000000000 --- a/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.graphics.fonts; - -import android.annotation.NonNull; - -import dalvik.annotation.optimization.CriticalNative; -import dalvik.annotation.optimization.FastNative; - -import libcore.util.NativeAllocationRegistry; - -import java.nio.ByteBuffer; - -/** - * This is a helper class for showing native allocated buffer in Java API. - * - * @hide - */ -public class NativeFontBufferHelper { - private NativeFontBufferHelper() {} - - private static final NativeAllocationRegistry REGISTRY = - NativeAllocationRegistry.createMalloced( - ByteBuffer.class.getClassLoader(), nGetReleaseFunc()); - - /** - * Wrap native buffer with ByteBuffer with adding reference to it. - */ - public static @NonNull ByteBuffer refByteBuffer(long fontPtr) { - long refPtr = nRefFontBuffer(fontPtr); - ByteBuffer buffer = nWrapByteBuffer(refPtr); - - // Releasing native object so that decreasing shared pointer ref count when the byte buffer - // is GCed. - REGISTRY.registerNativeAllocation(buffer, refPtr); - - return buffer; - } - - @CriticalNative - private static native long nRefFontBuffer(long fontPtr); - - @FastNative - private static native ByteBuffer nWrapByteBuffer(long refPtr); - - @CriticalNative - private static native long nGetReleaseFunc(); -} diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index c166e12fc6bf..904085feb8cb 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -22,6 +22,7 @@ import android.graphics.FontListParser; import android.graphics.Typeface; import android.text.FontConfig; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -36,8 +37,6 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -76,7 +75,7 @@ public final class SystemFonts { if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { sAvailableFonts = collectAllFonts(); } else { - Set<Font> set = new HashSet<>(); + Set<Font> set = new ArraySet<>(); for (FontFamily[] items : sFamilyMap.values()) { for (FontFamily family : items) { for (int i = 0; i < family.getSize(); ++i) { @@ -96,7 +95,7 @@ public final class SystemFonts { FontConfig fontConfig = getSystemPreinstalledFontConfig(); Map<String, FontFamily[]> map = buildSystemFallback(fontConfig); - Set<Font> res = new HashSet<>(); + Set<Font> res = new ArraySet<>(); for (FontFamily[] families : map.values()) { for (FontFamily family : families) { for (int i = 0; i < family.getSize(); ++i) { @@ -218,7 +217,7 @@ public final class SystemFonts { } private static void appendNamedFamily(@NonNull FontConfig.FontFamily xmlFamily, - @NonNull HashMap<String, ByteBuffer> bufferCache, + @NonNull ArrayMap<String, ByteBuffer> bufferCache, @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) { final String familyName = xmlFamily.getName(); final FontFamily family = createFontFamily( @@ -284,8 +283,8 @@ public final class SystemFonts { */ @VisibleForTesting public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) { - final Map<String, FontFamily[]> fallbackMap = new HashMap<>(); - final HashMap<String, ByteBuffer> bufferCache = new HashMap<>(); + final Map<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>(); final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies(); final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>(); @@ -326,7 +325,7 @@ public final class SystemFonts { public static Map<String, Typeface> buildSystemTypefaces( FontConfig fontConfig, Map<String, FontFamily[]> fallbackMap) { - final HashMap<String, Typeface> result = new HashMap<>(); + final ArrayMap<String, Typeface> result = new ArrayMap<>(); Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result); return result; } diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java index c2de0acebca9..8d20e9cee7d7 100644 --- a/graphics/java/android/graphics/text/PositionedGlyphs.java +++ b/graphics/java/android/graphics/text/PositionedGlyphs.java @@ -184,7 +184,7 @@ public final class PositionedGlyphs { long ptr = nGetFont(layoutPtr, i); if (prevPtr != ptr) { prevPtr = ptr; - prevFont = Font.findOrCreateFontFromNativePtr(ptr); + prevFont = new Font(ptr); } mFonts.add(prevFont); } @@ -224,9 +224,7 @@ public final class PositionedGlyphs { if (getGlyphId(i) != that.getGlyphId(i)) return false; if (getGlyphX(i) != that.getGlyphX(i)) return false; if (getGlyphY(i) != that.getGlyphY(i)) return false; - // Intentionally using reference equality since font equality is heavy due to buffer - // compare. - if (getFont(i) != that.getFont(i)) return false; + if (!getFont(i).equals(that.getFont(i))) return false; } return true; diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index fa4f8b1674d1..3ebca6ad302d 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -20,6 +20,8 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; +import android.annotation.SystemApi; +import android.os.Process; import android.security.KeyStore; import android.security.keymaster.KeymasterDefs; @@ -874,9 +876,18 @@ public abstract class KeyProperties { * which it must be configured in SEPolicy. * @hide */ + @SystemApi public static final int NAMESPACE_APPLICATION = -1; /** + * The namespace identifier for the WIFI Keystore namespace. + * This must be kept in sync with system/sepolicy/private/keystore2_key_contexts + * @hide + */ + @SystemApi + public static final int NAMESPACE_WIFI = 102; + + /** * For legacy support, translate namespaces into known UIDs. * @hide */ @@ -884,6 +895,8 @@ public abstract class KeyProperties { switch (namespace) { case NAMESPACE_APPLICATION: return KeyStore.UID_SELF; + case NAMESPACE_WIFI: + return Process.WIFI_UID; // TODO Translate WIFI and VPN UIDs once the namespaces are defined. // b/171305388 and b/171305607 default: @@ -900,6 +913,8 @@ public abstract class KeyProperties { switch (uid) { case KeyStore.UID_SELF: return NAMESPACE_APPLICATION; + case Process.WIFI_UID: + return NAMESPACE_WIFI; // TODO Translate WIFI and VPN UIDs once the namespaces are defined. // b/171305388 and b/171305607 default: diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index 6a92980de37c..70e30d2de5a1 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -585,6 +585,30 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mSpec.getKeyValidityForConsumptionEnd() )); } + if (mSpec.getCertificateNotAfter() != null) { + params.add(KeyStore2ParameterUtils.makeDate( + KeymasterDefs.KM_TAG_CERTIFICATE_NOT_AFTER, + mSpec.getCertificateNotAfter() + )); + } + if (mSpec.getCertificateNotBefore() != null) { + params.add(KeyStore2ParameterUtils.makeDate( + KeymasterDefs.KM_TAG_CERTIFICATE_NOT_BEFORE, + mSpec.getCertificateNotBefore() + )); + } + if (mSpec.getCertificateSerialNumber() != null) { + params.add(KeyStore2ParameterUtils.makeBignum( + KeymasterDefs.KM_TAG_CERTIFICATE_SERIAL, + mSpec.getCertificateSerialNumber() + )); + } + if (mSpec.getCertificateSubject() != null) { + params.add(KeyStore2ParameterUtils.makeBytes( + KeymasterDefs.KM_TAG_CERTIFICATE_SUBJECT, + mSpec.getCertificateSubject().getEncoded() + )); + } if (mSpec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) { params.add(KeyStore2ParameterUtils.makeInt( diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index 75ac61a22cab..e1011155248e 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -352,14 +352,17 @@ public class AndroidKeyStoreProvider extends Provider { try { response = keyStore.getKeyEntry(descriptor); } catch (android.security.KeyStoreException e) { - if (e.getErrorCode() == ResponseCode.KEY_PERMANENTLY_INVALIDATED) { - throw new KeyPermanentlyInvalidatedException( - "User changed or deleted their auth credentials", - e); - } else { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Failed to obtain information about key") - .initCause(e); + switch (e.getErrorCode()) { + case ResponseCode.KEY_NOT_FOUND: + return null; + case ResponseCode.KEY_PERMANENTLY_INVALIDATED: + throw new KeyPermanentlyInvalidatedException( + "User changed or deleted their auth credentials", + e); + default: + throw (UnrecoverableKeyException) + new UnrecoverableKeyException("Failed to obtain information about key") + .initCause(e); } } diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java index 4c8ab8d6c713..dcdd7defd752 100644 --- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java +++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java @@ -28,6 +28,7 @@ import android.security.keystore.KeyProperties; import android.security.keystore.UserAuthArgs; import android.system.keystore2.Authorization; +import java.math.BigInteger; import java.security.ProviderException; import java.util.ArrayList; import java.util.Date; @@ -154,6 +155,23 @@ public abstract class KeyStore2ParameterUtils { } /** + * This function constructs a {@link KeyParameter} expressing a Bignum. + * @param tag Must be KeyMint tag with the associated type BIGNUM. + * @param b A BitInteger to be stored in the new key parameter. + * @return An instance of {@link KeyParameter}. + * @hide + */ + static @NonNull KeyParameter makeBignum(int tag, @NonNull BigInteger b) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BIGNUM) { + throw new IllegalArgumentException("Not a bignum tag: " + tag); + } + KeyParameter p = new KeyParameter(); + p.tag = tag; + p.value = KeyParameterValue.blob(b.toByteArray()); + return p; + } + + /** * This function constructs a {@link KeyParameter} expressing date. * @param tag Must be KeyMint tag with the associated type DATE. * @param date A date @@ -167,10 +185,6 @@ public abstract class KeyStore2ParameterUtils { KeyParameter p = new KeyParameter(); p.tag = tag; p.value = KeyParameterValue.dateTime(date.getTime()); - if (p.value.getDateTime() < 0) { - throw new IllegalArgumentException("Date tag value out of range: " - + p.value.getDateTime()); - } return p; } /** diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml index 93a6e7b70c54..b581f555c234 100644 --- a/libs/WindowManager/Shell/res/layout/pip_menu.xml +++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml @@ -97,14 +97,4 @@ android:padding="@dimen/pip_resize_handle_padding" android:src="@drawable/pip_resize_handle" android:background="?android:selectableItemBackgroundBorderless" /> - - <!-- invisible layer to trap the focus, b/169372603 --> - <FrameLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@null" - android:defaultFocusHighlightEnabled="false" - android:focusable="true" - android:focusableInTouchMode="true" - android:focusedByDefault="true" /> -t </FrameLayout> +</FrameLayout> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt index 7ea4689be7e2..255e4d2c0d44 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.animation -import android.os.Looper import android.util.ArrayMap import android.util.Log import android.view.View @@ -847,7 +846,7 @@ class PhysicsAnimator<T> private constructor (target: T) { * pass to [spring]. */ data class SpringConfig internal constructor( - internal var stiffness: Float, + var stiffness: Float, internal var dampingRatio: Float, internal var startVelocity: Float = 0f, internal var finalPosition: Float = UNSET @@ -879,8 +878,8 @@ class PhysicsAnimator<T> private constructor (target: T) { */ data class FlingConfig internal constructor( internal var friction: Float, - internal var min: Float, - internal var max: Float, + var min: Float, + var max: Float, internal var startVelocity: Float ) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 39510e55139a..d54be0e62527 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -1478,6 +1478,9 @@ public class BubbleStackView extends FrameLayout * Update bubble order and pointer position. */ public void updateBubbleOrder(List<Bubble> bubbles) { + if (isExpansionAnimating()) { + return; + } final Runnable reorder = () -> { for (int i = 0; i < bubbles.size(); i++) { Bubble bubble = bubbles.get(i); @@ -1662,6 +1665,7 @@ public class BubbleStackView extends FrameLayout } beforeExpandedViewAnimation(); + updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */); mBubbleContainer.setActiveController(mExpandedAnimationController); updateOverflowVisibility(); updatePointerPosition(); @@ -1875,7 +1879,7 @@ public class BubbleStackView extends FrameLayout mExpandedBubble)); } updateOverflowVisibility(); - + updateBadgesAndZOrder(true /* setBadgeForCollapsedStack */); afterExpandedViewAnimation(); if (previouslySelected != null) { previouslySelected.setContentVisibility(false); @@ -2623,7 +2627,6 @@ public class BubbleStackView extends FrameLayout } mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide(); - updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index 616f24a874bc..fb70cbe502b0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -40,9 +40,11 @@ import android.view.IWindowSessionCallback; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.view.SurfaceControlViewHost; import android.view.View; import android.view.ViewGroup; +import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.WindowlessWindowManager; import android.window.ClientWindowFrames; @@ -251,6 +253,8 @@ public class SystemWindows { public class SysUiWindowManager extends WindowlessWindowManager { final int mDisplayId; ContainerWindow mContainerWindow; + final HashMap<IBinder, SurfaceControl> mLeashForWindow = + new HashMap<IBinder, SurfaceControl>(); public SysUiWindowManager(int displayId, Context ctx, SurfaceControl rootSurface, ContainerWindow container) { super(ctx.getResources().getConfiguration(), rootSurface, null /* hostInputToken */); @@ -263,7 +267,33 @@ public class SystemWindows { } SurfaceControl getSurfaceControlForWindow(View rootView) { - return getSurfaceControl(rootView); + synchronized (this) { + return mLeashForWindow.get(getWindowBinder(rootView)); + } + } + + protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { + SurfaceControl leash = new SurfaceControl.Builder(new SurfaceSession()) + .setContainerLayer() + .setName("SystemWindowLeash") + .setHidden(false) + .setParent(mRootSurface) + .setCallsite("SysUiWIndowManager#attachToParentSurface").build(); + synchronized (this) { + mLeashForWindow.put(window.asBinder(), leash); + } + b.setParent(leash); + } + + @Override + public void remove(android.view.IWindow window) throws RemoteException { + super.remove(window); + synchronized(this) { + IBinder token = window.asBinder(); + new SurfaceControl.Transaction().remove(mLeashForWindow.get(token)) + .apply(); + mLeashForWindow.remove(token); + } } void setTouchableRegionForWindow(View rootView, Region region) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 7f9c34f5df7a..728f60d93497 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -87,7 +87,7 @@ public final class SplitWindowManager extends WindowlessWindowManager { } @Override - protected void attachToParentSurface(SurfaceControl.Builder b) { + protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { mParentContainerCallbacks.attachToParentSurface(b); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java index e13a1dbe22d6..a18d106abea4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java @@ -862,14 +862,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, * assigned to it. */ private SurfaceControl getWindowSurfaceControl() { - final ViewRootImpl root = getViewRootImpl(); - if (root == null) { - return null; - } - SurfaceControl out = root.getSurfaceControl(); - if (out != null && out.isValid()) { - return out; - } return mWindowManager.mSystemWindows.getViewSurface(this); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index a57eee83ef59..ae5300502993 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -198,8 +198,7 @@ public class PhonePipMenuController implements PipMenuController { * {@code null}), it will get the leash that the WindowlessWM has assigned to it. */ public SurfaceControl getSurfaceControl() { - SurfaceControl sf = mPipMenuView.getWindowSurfaceControl(); - return sf != null ? sf : mSystemWindows.getViewSurface(mPipMenuView); + return mSystemWindows.getViewSurface(mPipMenuView); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java index 6d12752d9218..3eeba6eb5366 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java @@ -31,7 +31,6 @@ public class PipMenuIconsAlgorithm { private static final String TAG = "PipMenuIconsAlgorithm"; - private boolean mFinishedLayout = false; protected ViewGroup mViewRoot; protected ViewGroup mTopEndContainer; protected View mDragHandle; @@ -51,27 +50,16 @@ public class PipMenuIconsAlgorithm { mDragHandle = dragHandle; mSettingsButton = settingsButton; mDismissButton = dismissButton; + + bindInitialViewState(); } /** * Updates the position of the drag handle based on where the PIP window is on the screen. */ public void onBoundsChanged(Rect bounds) { - if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null - || mSettingsButton == null || mDismissButton == null) { - Log.e(TAG, "One if the required views is null."); - return; - } - - //We only need to calculate the layout once since it does not change. - if (!mFinishedLayout) { - mTopEndContainer.removeView(mSettingsButton); - mViewRoot.addView(mSettingsButton); - - setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP); - setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP); - mFinishedLayout = true; - } + // On phones, the menu icons are always static and will never move based on the PIP window + // position. No need to do anything here. } /** @@ -84,4 +72,22 @@ public class PipMenuIconsAlgorithm { v.setLayoutParams(params); } } + + /** Calculate the initial state of the menu icons. Called when the menu is first created. */ + private void bindInitialViewState() { + if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null + || mSettingsButton == null || mDismissButton == null) { + Log.e(TAG, "One of the required views is null."); + return; + } + // The menu view layout starts out with the settings button aligned at the top|end of the + // view group next to the dismiss button. On phones, the settings button should be aligned + // to the top|start of the view, so move it to parent view group to then align it to the + // top|start of the menu. + mTopEndContainer.removeView(mSettingsButton); + mViewRoot.addView(mSettingsButton); + + setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP); + setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index b91ba34c4464..53571ff70c6f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -107,6 +107,7 @@ public class PipResizeGestureHandler { private boolean mThresholdCrossed0; private boolean mThresholdCrossed1; private boolean mUsingPinchToZoom = false; + private float mAngle = 0; int mFirstIndex = -1; int mSecondIndex = -1; @@ -420,18 +421,25 @@ public class PipResizeGestureHandler { float down1X = mDownSecondaryPoint.x; float down1Y = mDownSecondaryPoint.y; - // Top right + Bottom left pinch to zoom. - if ((down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY) - || (down1X > focusX && down1Y < focusY - && down0X < focusX && down0Y > focusY)) { + if (down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY) { + // Top right + Bottom left pinch to zoom. mAngle = calculateRotationAngle(mLastResizeBounds.centerX(), mLastResizeBounds.centerY(), x0, y0, x1, y1, true); - } else if ((down0X < focusX && down0Y < focusY - && down1X > focusX && down1Y > focusY) - || (down1X < focusX && down1Y < focusY - && down0X > focusX && down0Y > focusY)) { + } else if (down1X > focusX && down1Y < focusY + && down0X < focusX && down0Y > focusY) { + // Top right + Bottom left pinch to zoom. + mAngle = calculateRotationAngle(mLastResizeBounds.centerX(), + mLastResizeBounds.centerY(), x1, y1, x0, y0, true); + } else if (down0X < focusX && down0Y < focusY + && down1X > focusX && down1Y > focusY) { + // Top left + bottom right pinch to zoom. mAngle = calculateRotationAngle(mLastResizeBounds.centerX(), mLastResizeBounds.centerY(), x0, y0, x1, y1, false); + } else if (down1X < focusX && down1Y < focusY + && down0X > focusX && down0Y > focusY) { + // Top left + bottom right pinch to zoom. + mAngle = calculateRotationAngle(mLastResizeBounds.centerX(), + mLastResizeBounds.centerY(), x1, y1, x0, y0, false); } mLastResizeBounds.set(PipPinchResizingAlgorithm.pinchResize(x0, y0, x1, y1, @@ -445,21 +453,26 @@ public class PipResizeGestureHandler { } } - private float mAngle = 0; - - private float calculateRotationAngle(int focusX, int focusY, float x0, float y0, - float x1, float y1, boolean positive) { + private float calculateRotationAngle(int pivotX, int pivotY, float topX, float topY, + float bottomX, float bottomY, boolean positive) { // The base angle is the angle formed by taking the angle between the center horizontal // and one of the corners. - double baseAngle = Math.toDegrees(Math.atan2(Math.abs(mLastResizeBounds.top - focusY), - Math.abs(mLastResizeBounds.right - focusX))); + double baseAngle = Math.toDegrees(Math.atan2(Math.abs(mLastResizeBounds.top - pivotY), + Math.abs(mLastResizeBounds.right - pivotX))); + double angle0 = mThresholdCrossed0 - ? Math.toDegrees(Math.atan2(Math.abs(y0 - focusY), Math.abs(x0 - focusX))) - : baseAngle; - double angle1 = mThresholdCrossed1 - ? Math.toDegrees(Math.atan2(Math.abs(y1 - focusY), Math.abs(x1 - focusX))) - : baseAngle; + ? Math.toDegrees(Math.atan2(pivotY - topY, topX - pivotX)) : baseAngle; + double angle1 = mThresholdCrossed0 + ? Math.toDegrees(Math.atan2(pivotY - bottomY, bottomX - pivotX)) : baseAngle; + + + if (positive) { + angle1 = angle1 < 0 ? 180 + angle1 : angle1 - 180; + } else { + angle0 = angle0 < 0 ? -angle0 - 180 : 180 - angle0; + angle1 = -angle1; + } // Calculate the percentage difference of [0, 90] compare to the base angle. double diff0 = (Math.max(0, Math.min(angle0, 90)) - baseAngle) / 90; diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml index 8258630a9502..f06d57c6c789 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml @@ -25,8 +25,6 @@ </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest"> <option name="package" value="com.android.wm.shell.flicker"/> - <option name="include-annotation" value="androidx.test.filters.RequiresDevice" /> - <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" /> <option name="shell-timeout" value="6600s" /> <option name="test-timeout" value="6000s" /> <option name="hidden-api-checks" value="false" /> diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp index dca27328f192..c0ab20d9249f 100644 --- a/libs/WindowManager/Shell/tests/unittest/Android.bp +++ b/libs/WindowManager/Shell/tests/unittest/Android.bp @@ -15,7 +15,10 @@ android_test { name: "WMShellUnitTests", - srcs: ["**/*.java"], + srcs: [ + "**/*.java", + "**/*.kt", + ], static_libs: [ "WindowManager-Shell", diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt index 4bd9bed26a82..17ed396987af 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt @@ -26,7 +26,7 @@ import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.FloatPropertyCompat import androidx.dynamicanimation.animation.SpringForce import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase +import com.android.wm.shell.ShellTestCase import com.android.wm.shell.animation.PhysicsAnimator.EndListener import com.android.wm.shell.animation.PhysicsAnimator.UpdateListener import com.android.wm.shell.animation.PhysicsAnimatorTestUtils.clearAnimationUpdateFrames @@ -54,8 +54,7 @@ import org.mockito.MockitoAnnotations @TestableLooper.RunWithLooper @RunWith(AndroidTestingRunner::class) @SmallTest -@Ignore("Blocking presubmits - investigating in b/158697054") -class PhysicsAnimatorTest : SysuiTestCase() { +class PhysicsAnimatorTest : ShellTestCase() { private lateinit var viewGroup: ViewGroup private lateinit var testView: View private lateinit var testView2: View diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt index 4fab9a5496ec..dd1a6a5a281e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt @@ -20,12 +20,12 @@ import android.content.pm.LauncherApps import android.os.UserHandle import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import com.android.systemui.util.mockito.eq import com.android.wm.shell.ShellTestCase import junit.framework.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.eq import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt index fe536411d5ed..9f1ee6c92700 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt @@ -21,8 +21,8 @@ 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 com.android.wm.shell.ShellTestCase +import com.android.wm.shell.animation.PhysicsAnimatorTestUtils import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -43,7 +43,7 @@ import org.mockito.Mockito.verifyNoMoreInteractions @TestableLooper.RunWithLooper @RunWith(AndroidTestingRunner::class) @SmallTest -class MagnetizedObjectTest : SysuiTestCase() { +class MagnetizedObjectTest : ShellTestCase() { /** Incrementing value for fake MotionEvent timestamps. */ private var time = 0L diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ce1d96c167d7..f48122858267 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -335,7 +335,6 @@ cc_defaults { "jni/YuvToJpegEncoder.cpp", "jni/fonts/Font.cpp", "jni/fonts/FontFamily.cpp", - "jni/fonts/NativeFont.cpp", "jni/text/LineBreaker.cpp", "jni/text/MeasuredText.cpp", "jni/text/TextShaper.cpp", diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index 0fad2d58cc8a..e1f5abd786bf 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -69,7 +69,6 @@ extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env); extern int register_android_graphics_fonts_Font(JNIEnv* env); extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); -extern int register_android_graphics_fonts_NativeFont(JNIEnv* env); extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env); extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env); extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env); @@ -136,7 +135,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_drawable_VectorDrawable), REG_JNI(register_android_graphics_fonts_Font), REG_JNI(register_android_graphics_fonts_FontFamily), - REG_JNI(register_android_graphics_fonts_NativeFont), REG_JNI(register_android_graphics_pdf_PdfDocument), REG_JNI(register_android_graphics_pdf_PdfEditor), REG_JNI(register_android_graphics_pdf_PdfRenderer), diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp index 97c40d695f97..fa1752cc47d6 100644 --- a/libs/hwui/jni/RenderEffect.cpp +++ b/libs/hwui/jni/RenderEffect.cpp @@ -115,6 +115,18 @@ static jlong createChainEffect( return reinterpret_cast<jlong>(composeFilter.release()); } +static jlong createShaderEffect( + JNIEnv* env, + jobject, + jlong shaderHandle +) { + auto* shader = reinterpret_cast<const SkShader*>(shaderHandle); + sk_sp<SkImageFilter> shaderFilter = SkImageFilters::Shader( + sk_ref_sp(shader), nullptr + ); + return reinterpret_cast<jlong>(shaderFilter.release()); +} + static void RenderEffect_safeUnref(SkImageFilter* filter) { SkSafeUnref(filter); } @@ -130,7 +142,8 @@ static const JNINativeMethod gRenderEffectMethods[] = { {"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect}, {"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect}, {"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect}, - {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect} + {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect}, + {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect} }; int register_android_graphics_RenderEffect(JNIEnv* env) { diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index b769d40238a4..3392dac02493 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -34,6 +34,7 @@ #include <hwui/Typeface.h> #include <minikin/FontFamily.h> #include <minikin/FontFileParser.h> +#include <minikin/LocaleList.h> #include <ui/FatVector.h> #include <memory> @@ -149,12 +150,8 @@ static jlong Font_Builder_clone(JNIEnv* env, jobject clazz, jlong fontPtr, jlong return reinterpret_cast<jlong>(new FontWrapper(std::move(newFont))); } -// Critical Native -static jlong Font_Builder_getReleaseNativeFont(CRITICAL_JNI_PARAMS) { - return reinterpret_cast<jlong>(releaseFont); -} - /////////////////////////////////////////////////////////////////////////////// +// Font JNI functions // Fast Native static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint glyphId, @@ -195,51 +192,92 @@ static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong } // Critical Native -static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { - FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle); - return reinterpret_cast<jlong>(font->font.get()); +static jlong Font_getMinikinFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { + FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); + return reinterpret_cast<jlong>(font->font->typeface().get()); } // Critical Native -static jlong Font_GetBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { - FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle); - const void* bufferPtr = font->font->typeface()->GetFontData(); - return reinterpret_cast<jlong>(bufferPtr); +static jlong Font_cloneFont(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { + FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); + std::shared_ptr<minikin::Font> ref = font->font; + return reinterpret_cast<jlong>(new FontWrapper(std::move(ref))); } -/////////////////////////////////////////////////////////////////////////////// - -struct FontBufferWrapper { - FontBufferWrapper(const std::shared_ptr<minikin::MinikinFont>& font) : minikinFont(font) {} - // MinikinFont holds a shared pointer of SkTypeface which has reference to font data. - std::shared_ptr<minikin::MinikinFont> minikinFont; -}; +// Fast Native +static jobject Font_newByteBuffer(JNIEnv* env, jobject, jlong fontPtr) { + FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + return env->NewDirectByteBuffer(const_cast<void*>(minikinFont->GetFontData()), + minikinFont->GetFontSize()); +} -static void unrefBuffer(jlong nativePtr) { - FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr); - delete wrapper; +// Critical Native +static jlong Font_getBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { + FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); + return reinterpret_cast<jlong>(font->font->typeface()->GetFontData()); } // Critical Native -static jlong FontBufferHelper_refFontBuffer(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { - const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); - return reinterpret_cast<jlong>(new FontBufferWrapper(font->typeface())); +static jlong Font_getReleaseNativeFontFunc() { + return reinterpret_cast<jlong>(releaseFont); } // Fast Native -static jobject FontBufferHelper_wrapByteBuffer(JNIEnv* env, jobject, jlong nativePtr) { - FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr); - return env->NewDirectByteBuffer( - const_cast<void*>(wrapper->minikinFont->GetFontData()), - wrapper->minikinFont->GetFontSize()); +static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) { + FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + const std::string& path = minikinFont->GetFontPath(); + if (path.empty()) { + return nullptr; + } + return env->NewStringUTF(path.c_str()); +} + +// Fast Native +static jstring Font_getLocaleList(JNIEnv* env, jobject, jlong fontPtr) { + FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); + uint32_t localeListId = font->font->getLocaleListId(); + if (localeListId == 0) { + return nullptr; + } + std::string langTags = minikin::getLocaleString(localeListId); + if (langTags.empty()) { + return nullptr; + } + return env->NewStringUTF(langTags.c_str()); } // Critical Native -static jlong FontBufferHelper_getReleaseFunc(CRITICAL_JNI_PARAMS) { - return reinterpret_cast<jlong>(unrefBuffer); +static jint Font_getPackedStyle(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { + FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); + uint32_t weight = font->font->style().weight(); + uint32_t isItalic = font->font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 1 : 0; + return (isItalic << 16) | weight; } -/////////////////////////////////////////////////////////////////////////////// +// Critical Native +static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { + FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + return minikinFont->GetFontIndex(); +} + +// Critical Native +static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) { + FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + return minikinFont->GetAxes().size(); +} + +// Critical Native +static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint index) { + FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr); + const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface(); + minikin::FontVariation var = minikinFont->GetAxes().at(index); + uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value); + return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary); +} // Fast Native static jlong FontFileUtil_getFontRevision(JNIEnv* env, jobject, jobject buffer, jint index) { @@ -314,20 +352,23 @@ static const JNINativeMethod gFontBuilderMethods[] = { {"nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;Ljava/lang/String;IZI)J", (void*)Font_Builder_build}, {"nClone", "(JJIZI)J", (void*)Font_Builder_clone}, - {"nGetReleaseNativeFont", "()J", (void*)Font_Builder_getReleaseNativeFont}, }; static const JNINativeMethod gFontMethods[] = { - { "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds }, - { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics }, - { "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr }, - { "nGetFontBufferAddress", "(J)J", (void*) Font_GetBufferAddress }, -}; - -static const JNINativeMethod gFontBufferHelperMethods[] = { - { "nRefFontBuffer", "(J)J", (void*) FontBufferHelper_refFontBuffer }, - { "nWrapByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) FontBufferHelper_wrapByteBuffer }, - { "nGetReleaseFunc", "()J", (void*) FontBufferHelper_getReleaseFunc }, + {"nGetMinikinFontPtr", "(J)J", (void*)Font_getMinikinFontPtr}, + {"nCloneFont", "(J)J", (void*)Font_cloneFont}, + {"nNewByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*)Font_newByteBuffer}, + {"nGetBufferAddress", "(J)J", (void*)Font_getBufferAddress}, + {"nGetReleaseNativeFont", "()J", (void*)Font_getReleaseNativeFontFunc}, + {"nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*)Font_getGlyphBounds}, + {"nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", + (void*)Font_getFontMetrics}, + {"nGetFontPath", "(J)Ljava/lang/String;", (void*)Font_getFontPath}, + {"nGetLocaleList", "(J)Ljava/lang/String;", (void*)Font_getLocaleList}, + {"nGetPackedStyle", "(J)I", (void*)Font_getPackedStyle}, + {"nGetIndex", "(J)I", (void*)Font_getIndex}, + {"nGetAxisCount", "(J)I", (void*)Font_getAxisCount}, + {"nGetAxisInfo", "(JI)J", (void*)Font_getAxisInfo}, }; static const JNINativeMethod gFontFileUtilMethods[] = { @@ -343,8 +384,6 @@ int register_android_graphics_fonts_Font(JNIEnv* env) { NELEM(gFontBuilderMethods)) + RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods, NELEM(gFontMethods)) + - RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFontBufferHelper", - gFontBufferHelperMethods, NELEM(gFontBufferHelperMethods)) + RegisterMethodsOrDie(env, "android/graphics/fonts/FontFileUtil", gFontFileUtilMethods, NELEM(gFontFileUtilMethods)); } diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp index 37e52766f2ef..80964794efb2 100644 --- a/libs/hwui/jni/fonts/FontFamily.cpp +++ b/libs/hwui/jni/fonts/FontFamily.cpp @@ -83,19 +83,57 @@ static jlong FontFamily_Builder_GetReleaseFunc(CRITICAL_JNI_PARAMS) { return reinterpret_cast<jlong>(releaseFontFamily); } +// FastNative +static jstring FontFamily_getLangTags(JNIEnv* env, jobject, jlong familyPtr) { + FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr); + uint32_t localeListId = family->family->localeListId(); + if (localeListId == 0) { + return nullptr; + } + std::string langTags = minikin::getLocaleString(localeListId); + return env->NewStringUTF(langTags.c_str()); +} + +// CriticalNative +static jint FontFamily_getVariant(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) { + FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr); + return static_cast<jint>(family->family->variant()); +} + +// CriticalNative +static jint FontFamily_getFontSize(jlong familyPtr) { + FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr); + return family->family->getNumFonts(); +} + +// CriticalNative +static jlong FontFamily_getFont(jlong familyPtr, jint index) { + FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr); + std::shared_ptr<minikin::Font> font = family->family->getFontRef(index); + return reinterpret_cast<jlong>(new FontWrapper(std::move(font))); +} + /////////////////////////////////////////////////////////////////////////////// static const JNINativeMethod gFontFamilyBuilderMethods[] = { { "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder }, { "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont }, { "nBuild", "(JLjava/lang/String;IZ)J", (void*) FontFamily_Builder_build }, - { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc }, }; +static const JNINativeMethod gFontFamilyMethods[] = { + {"nGetFontSize", "(J)I", (void*)FontFamily_getFontSize}, + {"nGetFont", "(JI)J", (void*)FontFamily_getFont}, + {"nGetLangTags", "(J)Ljava/lang/String;", (void*)FontFamily_getLangTags}, + {"nGetVariant", "(J)I", (void*)FontFamily_getVariant}, +}; + int register_android_graphics_fonts_FontFamily(JNIEnv* env) { return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder", - gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods)); + gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods)) + + RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily", gFontFamilyMethods, + NELEM(gFontFamilyMethods)); } } diff --git a/libs/hwui/jni/fonts/NativeFont.cpp b/libs/hwui/jni/fonts/NativeFont.cpp deleted file mode 100644 index c5c5d464ccac..000000000000 --- a/libs/hwui/jni/fonts/NativeFont.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "Minikin" - -#include "Font.h" -#include "SkData.h" -#include "SkFont.h" -#include "SkFontMetrics.h" -#include "SkFontMgr.h" -#include "SkRefCnt.h" -#include "SkTypeface.h" -#include "GraphicsJNI.h" -#include <nativehelper/ScopedUtfChars.h> -#include "Utils.h" -#include "FontUtils.h" - -#include <hwui/MinikinSkia.h> -#include <hwui/Paint.h> -#include <hwui/Typeface.h> -#include <minikin/FontFamily.h> -#include <minikin/LocaleList.h> -#include <ui/FatVector.h> - -#include <memory> - -namespace android { - -// Critical Native -static jint NativeFont_getFamilyCount(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle) { - Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle); - return tf->fFontCollection->getFamilies().size(); -} - -// Critical Native -static jlong NativeFont_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle, jint index) { - Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle); - return reinterpret_cast<jlong>(tf->fFontCollection->getFamilies()[index].get()); - -} - -// Fast Native -static jstring NativeFont_getLocaleList(JNIEnv* env, jobject, jlong familyHandle) { - minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle); - uint32_t localeListId = family->localeListId(); - return env->NewStringUTF(minikin::getLocaleString(localeListId).c_str()); -} - -// Critical Native -static jint NativeFont_getFontCount(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle) { - minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle); - return family->getNumFonts(); -} - -// Critical Native -static jlong NativeFont_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle, jint index) { - minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle); - return reinterpret_cast<jlong>(family->getFont(index)); -} - -// Critical Native -static jlong NativeFont_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { - const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); - MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); - - uint64_t result = font->style().weight(); - result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000; - result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32); - result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48); - return result; -} - -// Critical Native -static jlong NativeFont_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) { - const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); - MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); - const minikin::FontVariation& var = minikinSkia->GetAxes().at(index); - uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value); - return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary); -} - -// FastNative -static jstring NativeFont_getFontPath(JNIEnv* env, jobject, jlong fontHandle) { - const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); - MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); - const std::string& filePath = minikinSkia->getFilePath(); - if (filePath.empty()) { - return nullptr; - } - return env->NewStringUTF(filePath.c_str()); -} - -/////////////////////////////////////////////////////////////////////////////// - -static const JNINativeMethod gNativeFontMethods[] = { - { "nGetFamilyCount", "(J)I", (void*) NativeFont_getFamilyCount }, - { "nGetFamily", "(JI)J", (void*) NativeFont_getFamily }, - { "nGetLocaleList", "(J)Ljava/lang/String;", (void*) NativeFont_getLocaleList }, - { "nGetFontCount", "(J)I", (void*) NativeFont_getFontCount }, - { "nGetFont", "(JI)J", (void*) NativeFont_getFont }, - { "nGetFontInfo", "(J)J", (void*) NativeFont_getFontInfo }, - { "nGetAxisInfo", "(JI)J", (void*) NativeFont_getAxisInfo }, - { "nGetFontPath", "(J)Ljava/lang/String;", (void*) NativeFont_getFontPath }, -}; - -int register_android_graphics_fonts_NativeFont(JNIEnv* env) { - return RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFont", gNativeFontMethods, - NELEM(gNativeFontMethods)); -} - -} // namespace android diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp index 9785aa537f65..a6fb95832c03 100644 --- a/libs/hwui/jni/text/TextShaper.cpp +++ b/libs/hwui/jni/text/TextShaper.cpp @@ -23,13 +23,14 @@ #include <set> #include <algorithm> -#include "SkPaint.h" -#include "SkTypeface.h" #include <hwui/MinikinSkia.h> #include <hwui/MinikinUtils.h> #include <hwui/Paint.h> -#include <minikin/MinikinPaint.h> #include <minikin/MinikinFont.h> +#include <minikin/MinikinPaint.h> +#include "FontUtils.h" +#include "SkPaint.h" +#include "SkTypeface.h" namespace android { @@ -149,7 +150,8 @@ static jfloat TextShaper_Result_getY(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i // CriticalNative static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr); - return reinterpret_cast<jlong>(layout->layout.getFont(i)); + std::shared_ptr<minikin::Font> fontRef = layout->layout.getFontRef(i); + return reinterpret_cast<jlong>(new FontWrapper(std::move(fontRef))); } // CriticalNative diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp index 06f158f25fc5..6a9a98d6743b 100644 --- a/libs/hwui/tests/common/TestContext.cpp +++ b/libs/hwui/tests/common/TestContext.cpp @@ -40,9 +40,9 @@ const DisplayInfo& getDisplayInfo() { return info; } -const DisplayConfig& getActiveDisplayConfig() { - static DisplayConfig config = [] { - DisplayConfig config; +const ui::DisplayMode& getActiveDisplayMode() { + static ui::DisplayMode config = [] { + ui::DisplayMode config; #if HWUI_NULL_GPU config.resolution = ui::Size(1080, 1920); config.xDpi = config.yDpi = 320.f; @@ -51,7 +51,7 @@ const DisplayConfig& getActiveDisplayConfig() { const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__); - const status_t status = SurfaceComposerClient::getActiveDisplayConfig(token, &config); + const status_t status = SurfaceComposerClient::getActiveDisplayMode(token, &config); LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get active display config", __FUNCTION__); #endif return config; diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h index a012ecb1a1d3..7d2f6d8ea731 100644 --- a/libs/hwui/tests/common/TestContext.h +++ b/libs/hwui/tests/common/TestContext.h @@ -23,8 +23,8 @@ #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> -#include <ui/DisplayConfig.h> #include <ui/DisplayInfo.h> +#include <ui/DisplayMode.h> #include <utils/Looper.h> #include <atomic> @@ -37,10 +37,10 @@ namespace uirenderer { namespace test { const DisplayInfo& getDisplayInfo(); -const DisplayConfig& getActiveDisplayConfig(); +const ui::DisplayMode& getActiveDisplayMode(); inline const ui::Size& getActiveDisplayResolution() { - return getActiveDisplayConfig().resolution; + return getActiveDisplayMode().resolution; } class TestContext { diff --git a/libs/tracingproxy/Android.bp b/libs/tracingproxy/Android.bp new file mode 100644 index 000000000000..67f407ff7599 --- /dev/null +++ b/libs/tracingproxy/Android.bp @@ -0,0 +1,42 @@ +// 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. + +// Provides C++ wrappers for system services. + +cc_library_shared { + name: "libtracingproxy", + + aidl: { + export_aidl_headers: true, + include_dirs: [ + "frameworks/base/core/java", + ], + }, + + srcs: [ + ":ITracingServiceProxy.aidl", + ], + + shared_libs: [ + "libbinder", + "libutils", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wunused", + "-Wunreachable-code", + ], +} diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 65721cc33aed..adf58da6a072 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -36,6 +36,7 @@ import android.location.LastLocationRequest; import android.location.Location; import android.location.LocationRequest; import android.location.LocationTime; +import android.location.provider.IProviderRequestListener; import android.location.provider.ProviderProperties; import android.os.Bundle; import android.os.ICancellationSignal; @@ -91,6 +92,9 @@ interface ILocationManager void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag); void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener); + void addProviderRequestListener(in IProviderRequestListener listener); + void removeProviderRequestListener(in IProviderRequestListener listener); + int getGnssBatchSize(); void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, @nullable String attributionTag, @nullable String listenerId); void flushGnssBatch(); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index d56948222797..088b789ea690 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -49,7 +49,10 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.location.provider.IProviderRequestListener; import android.location.provider.ProviderProperties; +import android.location.provider.ProviderRequest; +import android.location.provider.ProviderRequest.Listener; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; @@ -436,6 +439,9 @@ public class LocationManager { new GnssNavigationTransportManager(); } + private static final ProviderRequestTransportManager sProviderRequestListeners = + new ProviderRequestTransportManager(); + private final Context mContext; private final ILocationManager mService; @@ -2772,6 +2778,37 @@ public class LocationManager { } /** + * Registers a {@link ProviderRequest.Listener} to all providers. + * + * @param executor the executor that the callback runs on + * @param listener the listener to register + * @return {@code true} always + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) + public boolean registerProviderRequestListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull Listener listener) { + sProviderRequestListeners.addListener(listener, + new ProviderRequestTransport(executor, listener)); + return true; + } + + /** + * Unregisters a {@link ProviderRequest.Listener}. + * + * @param listener the listener to remove. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) + public void unregisterProviderRequestListener( + @NonNull Listener listener) { + sProviderRequestListeners.removeListener(listener); + } + + /** * Returns the batch size (in number of Location objects) that are supported by the batching * interface. * @@ -2960,6 +2997,22 @@ public class LocationManager { } } + private static class ProviderRequestTransportManager extends + ListenerTransportManager<ProviderRequestTransport> { + + @Override + protected void registerTransport(ProviderRequestTransport transport) + throws RemoteException { + getService().addProviderRequestListener(transport); + } + + @Override + protected void unregisterTransport(ProviderRequestTransport transport) + throws RemoteException { + getService().removeProviderRequestListener(transport); + } + } + private static class GetCurrentLocationTransport extends ILocationCallback.Stub implements ListenerExecutor, CancellationSignal.OnCancelListener { @@ -3359,6 +3412,36 @@ public class LocationManager { } } + private static class ProviderRequestTransport extends IProviderRequestListener.Stub + implements ListenerTransport<ProviderRequest.Listener> { + + private final Executor mExecutor; + + private volatile @Nullable ProviderRequest.Listener mListener; + + ProviderRequestTransport(Executor executor, ProviderRequest.Listener listener) { + Preconditions.checkArgument(executor != null, "invalid null executor"); + Preconditions.checkArgument(listener != null, "invalid null callback"); + mExecutor = executor; + mListener = listener; + } + + @Override + public void unregister() { + mListener = null; + } + + @Override + public @Nullable ProviderRequest.Listener getListener() { + return mListener; + } + + @Override + public void onProviderRequestChanged(String provider, ProviderRequest request) { + execute(mExecutor, listener -> listener.onProviderRequestChanged(provider, request)); + } + } + private static class BatchedLocationCallbackWrapper implements LocationListener { private final BatchedLocationCallback mCallback; diff --git a/location/java/android/location/provider/IProviderRequestListener.aidl b/location/java/android/location/provider/IProviderRequestListener.aidl new file mode 100644 index 000000000000..89d454aafe2c --- /dev/null +++ b/location/java/android/location/provider/IProviderRequestListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location.provider; + +import android.location.provider.ProviderRequest; + +/** + * {@hide} + */ +oneway interface IProviderRequestListener { + void onProviderRequestChanged(String provider, in ProviderRequest request); +} diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java index e543b040a2d4..b6ec32309b08 100644 --- a/location/java/android/location/provider/ProviderRequest.java +++ b/location/java/android/location/provider/ProviderRequest.java @@ -53,6 +53,17 @@ public final class ProviderRequest implements Parcelable { private final boolean mLocationSettingsIgnored; private final WorkSource mWorkSource; + /** + * Listener to be invoked when a new request is set to the provider. + */ + public interface Listener { + + /** + * Invoked when a new request is set. + */ + void onProviderRequestChanged(@NonNull String provider, @NonNull ProviderRequest request); + } + private ProviderRequest( long intervalMillis, @Quality int quality, diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 08deb156a6cf..8d090f824e71 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1,5 +1,4 @@ /* -/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java index 812dac97bcd3..27f72687ccbe 100644 --- a/media/java/android/media/AudioPlaybackConfiguration.java +++ b/media/java/android/media/AudioPlaybackConfiguration.java @@ -20,6 +20,7 @@ import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL; import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -195,6 +196,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { private int mDeviceId; + private int mSessionId; + /** * Never use without initializing parameters afterwards */ @@ -207,7 +210,10 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @hide */ public AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid) { - if (DEBUG) { Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer); } + if (DEBUG) { + Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer + + " sessionId=" + pic.mSessionId); + } mPlayerIId = piid; mPlayerType = pic.mPlayerType; mClientUid = uid; @@ -220,6 +226,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { } else { mIPlayerShell = null; } + mSessionId = pic.mSessionId; } /** @@ -259,6 +266,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { anonymCopy.mClientUid = PLAYER_UPID_INVALID; anonymCopy.mClientPid = PLAYER_UPID_INVALID; anonymCopy.mIPlayerShell = null; + anonymCopy.mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE; return anonymCopy; } @@ -303,6 +311,17 @@ public final class AudioPlaybackConfiguration implements Parcelable { /** * @hide + * Return the audio session ID associated with this player. + * See {@link AudioManager#generateAudioSessionId()}. + * @return an audio session ID + */ + @SystemApi + public @IntRange(from = 0) int getSessionId() { + return mSessionId; + } + + /** + * @hide * Return the type of player linked to this configuration. * <br>Note that player types not exposed in the system API will be represented as * {@link #PLAYER_TYPE_UNKNOWN}. @@ -381,6 +400,17 @@ public final class AudioPlaybackConfiguration implements Parcelable { /** * @hide + * Handle a change of audio session Id + * @param sessionId the audio session ID + */ + public boolean handleSessionIdEvent(int sessionId) { + final boolean changed = sessionId != mSessionId; + mSessionId = sessionId; + return changed; + } + + /** + * @hide * Handle a player state change * @param event * @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID} @@ -476,7 +506,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public int hashCode() { - return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid); + return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid, + mSessionId); } @Override @@ -498,6 +529,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { ips = mIPlayerShell; } dest.writeStrongInterface(ips == null ? null : ips.getIPlayer()); + dest.writeInt(mSessionId); } private AudioPlaybackConfiguration(Parcel in) { @@ -510,6 +542,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in); final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder()); mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p); + mSessionId = in.readInt(); } @Override @@ -523,7 +556,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { && (mDeviceId == that.mDeviceId) && (mPlayerType == that.mPlayerType) && (mClientUid == that.mClientUid) - && (mClientPid == that.mClientPid)); + && (mClientPid == that.mClientPid)) + && (mSessionId == that.mSessionId); } @Override @@ -533,7 +567,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { + " type:" + toLogFriendlyPlayerType(mPlayerType) + " u/pid:" + mClientUid + "/" + mClientPid + " state:" + toLogFriendlyPlayerState(mPlayerState) - + " attr:" + mPlayerAttr; + + " attr:" + mPlayerAttr + + " sessionId:" + mSessionId; } //===================================================================== diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 175d36fedb1f..e056d435198a 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -836,7 +836,7 @@ public class AudioTrack extends PlayerBase mState = STATE_INITIALIZED; } - baseRegisterPlayer(); + baseRegisterPlayer(mSessionId); } /** @@ -866,7 +866,7 @@ public class AudioTrack extends PlayerBase // other initialization... if (nativeTrackInJavaObj != 0) { - baseRegisterPlayer(); + baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE); deferred_connect(nativeTrackInJavaObj); } else { mState = STATE_UNINITIALIZED; diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java index 06bf5f70d9ec..9c6b276e7b20 100644 --- a/media/java/android/media/CamcorderProfile.java +++ b/media/java/android/media/CamcorderProfile.java @@ -119,9 +119,14 @@ public class CamcorderProfile */ public static final int QUALITY_2K = 12; + /** + * Quality level corresponding to 8K UHD (7680 x 4320) resolution + */ + public static final int QUALITY_8KUHD = 13; + // Start and end of quality list private static final int QUALITY_LIST_START = QUALITY_LOW; - private static final int QUALITY_LIST_END = QUALITY_2K; + private static final int QUALITY_LIST_END = QUALITY_8KUHD; /** * Time lapse quality level corresponding to the lowest available resolution. @@ -188,10 +193,14 @@ public class CamcorderProfile */ public static final int QUALITY_TIME_LAPSE_2K = 1012; + /** + * Time lapse quality level corresponding to the 8K UHD (7680 x 4320) resolution. + */ + public static final int QUALITY_TIME_LAPSE_8KUHD = 1013; // Start and end of timelapse quality list private static final int QUALITY_TIME_LAPSE_LIST_START = QUALITY_TIME_LAPSE_LOW; - private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_2K; + private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_8KUHD; /** * High speed ( >= 100fps) quality level corresponding to the lowest available resolution. diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java index bbf632a406ec..e339ae8ef706 100644 --- a/media/java/android/media/HwAudioSource.java +++ b/media/java/android/media/HwAudioSource.java @@ -54,7 +54,7 @@ public class HwAudioSource extends PlayerBase { Preconditions.checkArgument(device.isSource(), "Requires a source device"); mAudioDeviceInfo = device; mAudioAttributes = attributes; - baseRegisterPlayer(); + baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE); } /** diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index dd42aab67c65..71ee57e3d471 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -73,6 +73,8 @@ interface IAudioService { oneway void releaseRecorder(in int riid); + oneway void playerSessionId(in int piid, in int sessionId); + // Java-only methods below. oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java index d248f61f03ca..7837d7e39599 100644 --- a/media/java/android/media/ImageUtils.java +++ b/media/java/android/media/ImageUtils.java @@ -45,6 +45,7 @@ class ImageUtils { case ImageFormat.YV12: case ImageFormat.YUV_420_888: case ImageFormat.NV21: + case ImageFormat.YCBCR_P010: return 3; case ImageFormat.NV16: return 2; @@ -225,6 +226,7 @@ class ImageUtils { case ImageFormat.RAW_SENSOR: case ImageFormat.RAW_PRIVATE: // round estimate, real size is unknown case ImageFormat.DEPTH16: + case ImageFormat.YCBCR_P010: estimatedBytePerPixel = 2.0; break; case PixelFormat.RGB_888: @@ -244,6 +246,7 @@ class ImageUtils { private static Size getEffectivePlaneSizeForImage(Image image, int planeIdx) { switch (image.getFormat()) { + case ImageFormat.YCBCR_P010: case ImageFormat.YV12: case ImageFormat.YUV_420_888: case ImageFormat.NV21: diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index dd0bc61c4225..ca0d29f2f47f 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -663,6 +663,10 @@ public class MediaPlayer extends PlayerBase * result in an exception.</p> */ public MediaPlayer() { + this(AudioSystem.AUDIO_SESSION_ALLOCATE); + } + + private MediaPlayer(int sessionId) { super(new AudioAttributes.Builder().build(), AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER); @@ -684,7 +688,7 @@ public class MediaPlayer extends PlayerBase native_setup(new WeakReference<MediaPlayer>(this), getCurrentOpPackageName()); - baseRegisterPlayer(); + baseRegisterPlayer(sessionId); } /* @@ -913,7 +917,7 @@ public class MediaPlayer extends PlayerBase AudioAttributes audioAttributes, int audioSessionId) { try { - MediaPlayer mp = new MediaPlayer(); + MediaPlayer mp = new MediaPlayer(audioSessionId); final AudioAttributes aa = audioAttributes != null ? audioAttributes : new AudioAttributes.Builder().build(); mp.setAudioAttributes(aa); @@ -978,7 +982,7 @@ public class MediaPlayer extends PlayerBase AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid); if (afd == null) return null; - MediaPlayer mp = new MediaPlayer(); + MediaPlayer mp = new MediaPlayer(audioSessionId); final AudioAttributes aa = audioAttributes != null ? audioAttributes : new AudioAttributes.Builder().build(); @@ -2365,7 +2369,13 @@ public class MediaPlayer extends PlayerBase * This method must be called before one of the overloaded <code> setDataSource </code> methods. * @throws IllegalStateException if it is called in an invalid state */ - public native void setAudioSessionId(int sessionId) throws IllegalArgumentException, IllegalStateException; + public void setAudioSessionId(int sessionId) + throws IllegalArgumentException, IllegalStateException { + native_setAudioSessionId(sessionId); + baseUpdateSessionId(sessionId); + } + + private native void native_setAudioSessionId(int sessionId); /** * Returns the audio session ID. diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java index 58ae279d4df1..4407efad6052 100644 --- a/media/java/android/media/PlayerBase.java +++ b/media/java/android/media/PlayerBase.java @@ -97,6 +97,7 @@ public abstract class PlayerBase { * Constructor. Must be given audio attributes, as they are required for AppOps. * @param attr non-null audio attributes * @param class non-null class of the implementation of this abstract class + * @param sessionId the audio session Id */ PlayerBase(@NonNull AudioAttributes attr, int implType) { if (attr == null) { @@ -110,7 +111,7 @@ public abstract class PlayerBase { /** * Call from derived class when instantiation / initialization is successful */ - protected void baseRegisterPlayer() { + protected void baseRegisterPlayer(int sessionId) { if (!USE_AUDIOFLINGER_MUTING_FOR_OP) { IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE); mAppOps = IAppOpsService.Stub.asInterface(b); @@ -128,7 +129,8 @@ public abstract class PlayerBase { } try { mPlayerIId = getService().trackPlayer( - new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this))); + new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this), + sessionId)); } catch (RemoteException e) { Log.e(TAG, "Error talking to audio service, player will not be tracked", e); } @@ -145,7 +147,7 @@ public abstract class PlayerBase { try { getService().playerAttributes(mPlayerIId, attr); } catch (RemoteException e) { - Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e); + Log.e(TAG, "Error talking to audio service, audio attributes will not be updated", e); } synchronized (mLock) { boolean attributesChanged = (mAttributes != attr); @@ -154,6 +156,18 @@ public abstract class PlayerBase { } } + /** + * To be called whenever the session ID of the player changes + * @param sessionId, the new session Id + */ + void baseUpdateSessionId(int sessionId) { + try { + getService().playerSessionId(mPlayerIId, sessionId); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to audio service, the session ID will not be updated", e); + } + } + void baseUpdateDeviceId(@Nullable AudioDeviceInfo deviceInfo) { int deviceId = 0; if (deviceInfo != null) { @@ -566,16 +580,19 @@ public abstract class PlayerBase { public static final int AUDIO_ATTRIBUTES_DEFINED = 1; public final AudioAttributes mAttributes; public final IPlayer mIPlayer; + public final int mSessionId; - PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer) { + PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer, + int sessionId) { mPlayerType = type; mAttributes = attr; mIPlayer = iplayer; + mSessionId = sessionId; } @Override public int hashCode() { - return Objects.hash(mPlayerType); + return Objects.hash(mPlayerType, mSessionId); } @Override @@ -588,6 +605,7 @@ public abstract class PlayerBase { dest.writeInt(mPlayerType); mAttributes.writeToParcel(dest, 0); dest.writeStrongBinder(mIPlayer == null ? null : mIPlayer.asBinder()); + dest.writeInt(mSessionId); } public static final @android.annotation.NonNull Parcelable.Creator<PlayerIdCard> CREATOR @@ -611,6 +629,7 @@ public abstract class PlayerBase { // IPlayer can be null if unmarshalling a Parcel coming from who knows where final IBinder b = in.readStrongBinder(); mIPlayer = (b == null ? null : IPlayer.Stub.asInterface(b)); + mSessionId = in.readInt(); } @Override @@ -621,7 +640,8 @@ public abstract class PlayerBase { PlayerIdCard that = (PlayerIdCard) o; // FIXME change to the binder player interface once supported as a member - return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes)); + return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes) + && (mSessionId == that.mSessionId)); } } diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java index 797caf36203b..32413dc6e841 100644 --- a/media/java/android/media/SoundPool.java +++ b/media/java/android/media/SoundPool.java @@ -155,7 +155,8 @@ public class SoundPool extends PlayerBase { } mAttributes = attributes; - baseRegisterPlayer(); + // FIXME: b/174876164 implement session id for soundpool + baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE); } /** diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl index 31fb8d03c4a0..9bf126b7a875 100644 --- a/media/java/android/media/session/ISession.aidl +++ b/media/java/android/media/session/ISession.aidl @@ -35,7 +35,7 @@ interface ISession { ISessionController getController(); void setFlags(int flags); void setActive(boolean active); - void setMediaButtonReceiver(in PendingIntent mbr); + void setMediaButtonReceiver(in PendingIntent mbr, String sessionPackageName); void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver); void setLaunchPendingIntent(in PendingIntent pi); void destroySession(); diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 24118b086c24..20fa53d3ec32 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -286,7 +286,7 @@ public final class MediaSession { @Deprecated public void setMediaButtonReceiver(@Nullable PendingIntent mbr) { try { - mBinder.setMediaButtonReceiver(mbr); + mBinder.setMediaButtonReceiver(mbr, mContext.getPackageName()); } catch (RemoteException e) { Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e); } diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 8525e9979aef..ee0be010c233 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -65,6 +65,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -245,6 +246,8 @@ public class Tuner implements AutoCloseable { private static final int MSG_ON_FILTER_STATUS = 3; private static final int MSG_ON_LNB_EVENT = 4; + private static final int FILTER_CLEANUP_THRESHOLD = 256; + /** @hide */ @IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK}) @Retention(RetentionPolicy.SOURCE) @@ -1208,6 +1211,15 @@ public class Tuner implements AutoCloseable { synchronized (mFilters) { WeakReference<Filter> weakFilter = new WeakReference<Filter>(filter); mFilters.add(weakFilter); + if (mFilters.size() > FILTER_CLEANUP_THRESHOLD) { + Iterator<WeakReference<Filter>> iterator = mFilters.iterator(); + while (iterator.hasNext()) { + WeakReference<Filter> wFilter = iterator.next(); + if (wFilter.get() == null) { + iterator.remove(); + } + } + } } } return filter; diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 67a2c49e5746..65b64d7e8df3 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -31,8 +31,8 @@ cc_library_shared { ], shared_libs: [ - "audioclient-types-aidl-unstable-cpp", - "av-types-aidl-unstable-cpp", + "audioclient-types-aidl-cpp", + "av-types-aidl-cpp", "libandroid_runtime", "libaudioclient", "libnativehelper", diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index bd8d2e9f77a4..98ac5b983098 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -1409,7 +1409,7 @@ static const JNINativeMethod gMethods[] = { {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_native_setup}, {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, {"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id}, - {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id}, + {"native_setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id}, {"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel}, {"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect}, {"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData}, diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp index b6c47fca52f9..ecd9cc1cd098 100644 --- a/media/jni/android_media_Utils.cpp +++ b/media/jni/android_media_Utils.cpp @@ -69,6 +69,7 @@ bool isPossiblyYUV(PixelFormat format) { case HAL_PIXEL_FORMAT_RAW_OPAQUE: case HAL_PIXEL_FORMAT_BLOB: case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: + case HAL_PIXEL_FORMAT_YCBCR_P010: return false; case HAL_PIXEL_FORMAT_YV12: @@ -261,6 +262,32 @@ status_t getLockedImageInfo(LockedImage* buffer, int idx, pStride = 1; rStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16); break; + case HAL_PIXEL_FORMAT_YCBCR_P010: + if (buffer->height % 2 != 0) { + ALOGE("YCBCR_P010: height (%d) should be a multiple of 2", buffer->height); + return BAD_VALUE; + } + + if (buffer->width <= 0) { + ALOGE("YCBCR_P010: width (%d) should be a > 0", buffer->width); + return BAD_VALUE; + } + + if (buffer->height <= 0) { + ALOGE("YCBCR_P010: height (%d) should be a > 0", buffer->height); + return BAD_VALUE; + } + + ySize = (buffer->stride * 2) * buffer->height; + cSize = ySize / 2; + pStride = (idx == 0) ? 2 : 4; + cb = buffer->data + ySize; + cr = cb + 2; + + pData = (idx == 0) ? buffer->data : (idx == 1) ? cb : cr; + dataSize = (idx == 0) ? ySize : cSize; + rStride = buffer->stride * 2; + break; case HAL_PIXEL_FORMAT_Y8: // Single plane, 8bpp. LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx); diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 9ec84d9d2265..eee9f1e08131 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -3786,7 +3786,7 @@ static jint android_media_tv_Tuner_read_filter_fmq( jniThrowRuntimeException(env, "Failed to GetByteArrayElements"); return -1; } - int realReadSize = filterClient->read(reinterpret_cast<uint8_t*>(dst) + offset, size); + int realReadSize = filterClient->read(reinterpret_cast<int8_t*>(dst) + offset, size); env->ReleaseByteArrayElements(buffer, dst, 0); return (jint) realReadSize; } diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp index 1a2f8c065bd3..748d45808932 100644 --- a/media/jni/tuner/DemuxClient.cpp +++ b/media/jni/tuner/DemuxClient.cpp @@ -88,12 +88,19 @@ sp<FilterClient> DemuxClient::openFilter(DemuxFilterType type, int bufferSize, } sp<TimeFilterClient> DemuxClient::openTimeFilter() { - // TODO: pending aidl interface + if (mTunerDemux != NULL) { + shared_ptr<ITunerTimeFilter> tunerTimeFilter; + Status s = mTunerDemux->openTimeFilter(&tunerTimeFilter); + if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { + return NULL; + } + return new TimeFilterClient(tunerTimeFilter); + } if (mDemux != NULL) { sp<ITimeFilter> hidlTimeFilter = openHidlTimeFilter(); if (hidlTimeFilter != NULL) { - sp<TimeFilterClient> timeFilterClient = new TimeFilterClient(); + sp<TimeFilterClient> timeFilterClient = new TimeFilterClient(NULL); timeFilterClient->setHidlTimeFilter(hidlTimeFilter); return timeFilterClient; } @@ -103,7 +110,14 @@ sp<TimeFilterClient> DemuxClient::openTimeFilter() { } int DemuxClient::getAvSyncHwId(sp<FilterClient> filterClient) { - // pending aidl interface + if (mTunerDemux != NULL) { + int hwId; + Status s = mTunerDemux->getAvSyncHwId(filterClient->getAidlFilter(), &hwId); + if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { + return INVALID_AV_SYNC_HW_ID; + } + return hwId; + } if (mDemux != NULL) { uint32_t avSyncHwId; @@ -119,11 +133,18 @@ int DemuxClient::getAvSyncHwId(sp<FilterClient> filterClient) { } } - return -1; + return INVALID_AV_SYNC_HW_ID; } long DemuxClient::getAvSyncTime(int avSyncHwId) { - // pending aidl interface + if (mTunerDemux != NULL) { + int64_t time; + Status s = mTunerDemux->getAvSyncTime(avSyncHwId, &time); + if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { + return INVALID_AV_SYNC_TIME; + } + return time; + } if (mDemux != NULL) { uint64_t time; @@ -138,7 +159,7 @@ long DemuxClient::getAvSyncTime(int avSyncHwId) { } } - return -1; + return INVALID_AV_SYNC_TIME; } sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb) { @@ -167,7 +188,10 @@ sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClient } Result DemuxClient::connectCiCam(int ciCamId) { - // pending aidl interface + if (mTunerDemux != NULL) { + Status s = mTunerDemux->connectCiCam(ciCamId); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mDemux != NULL) { return mDemux->connectCiCam(static_cast<uint32_t>(ciCamId)); @@ -177,7 +201,10 @@ Result DemuxClient::connectCiCam(int ciCamId) { } Result DemuxClient::disconnectCiCam() { - // pending aidl interface + if (mTunerDemux != NULL) { + Status s = mTunerDemux->disconnectCiCam(); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mDemux != NULL) { return mDemux->disconnectCiCam(); diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h index 463944a7dc00..c38a8fa34690 100644 --- a/media/jni/tuner/DemuxClient.h +++ b/media/jni/tuner/DemuxClient.h @@ -31,7 +31,9 @@ using Status = ::ndk::ScopedAStatus; using ::aidl::android::media::tv::tuner::ITunerDemux; +using ::aidl::android::media::tv::tuner::ITunerTimeFilter; +using ::android::hardware::tv::tuner::V1_0::IDemux; using ::android::hardware::tv::tuner::V1_0::DemuxFilterType; using ::android::hardware::tv::tuner::V1_0::DvrType; using ::android::hardware::tv::tuner::V1_0::IDemux; @@ -39,6 +41,9 @@ using ::android::hardware::tv::tuner::V1_0::ITimeFilter; using namespace std; +const int64_t INVALID_AV_SYNC_TIME = -1; +const int INVALID_AV_SYNC_HW_ID = -1; + namespace android { struct DemuxClient : public RefBase { @@ -95,6 +100,11 @@ public: */ Result close(); + /** + * Get the Aidl demux to set as source. + */ + shared_ptr<ITunerDemux> getAidlDemux() { return mTunerDemux; } + void setId(int id) { mId = id; } int getId() { return mId; } diff --git a/media/jni/tuner/DescramblerClient.cpp b/media/jni/tuner/DescramblerClient.cpp index 979beeac6b3a..c9bacda0fa70 100644 --- a/media/jni/tuner/DescramblerClient.cpp +++ b/media/jni/tuner/DescramblerClient.cpp @@ -27,13 +27,12 @@ namespace android { /////////////// DescramblerClient /////////////////////// -// TODO: pending aidl interface -DescramblerClient::DescramblerClient() { - //mTunerDescrambler = tunerDescrambler; +DescramblerClient::DescramblerClient(shared_ptr<ITunerDescrambler> tunerDescrambler) { + mTunerDescrambler = tunerDescrambler; } DescramblerClient::~DescramblerClient() { - //mTunerDescrambler = NULL; + mTunerDescrambler = NULL; mDescrambler = NULL; } @@ -47,7 +46,10 @@ Result DescramblerClient::setDemuxSource(sp<DemuxClient> demuxClient) { return Result::INVALID_ARGUMENT; } - // TODO: pending aidl interface + if (mTunerDescrambler != NULL) { + Status s = mTunerDescrambler->setDemuxSource(demuxClient->getAidlDemux()); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mDescrambler != NULL) { return mDescrambler->setDemuxSource(demuxClient->getId()); @@ -57,7 +59,10 @@ Result DescramblerClient::setDemuxSource(sp<DemuxClient> demuxClient) { } Result DescramblerClient::setKeyToken(vector<uint8_t> keyToken) { - // TODO: pending aidl interface + if (mTunerDescrambler != NULL) { + Status s = mTunerDescrambler->setKeyToken(keyToken); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mDescrambler != NULL) { return mDescrambler->setKeyToken(keyToken); @@ -67,7 +72,11 @@ Result DescramblerClient::setKeyToken(vector<uint8_t> keyToken) { } Result DescramblerClient::addPid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) { - // TODO: pending aidl interface + if (mTunerDescrambler != NULL) { + Status s = mTunerDescrambler->addPid( + getAidlDemuxPid(pid), optionalSourceFilter->getAidlFilter()); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mDescrambler != NULL) { return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter()); @@ -76,16 +85,24 @@ Result DescramblerClient::addPid(DemuxPid pid, sp<FilterClient> optionalSourceFi return Result::INVALID_STATE;} Result DescramblerClient::removePid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) { - // TODO: pending aidl interface + if (mTunerDescrambler != NULL) { + Status s = mTunerDescrambler->removePid( + getAidlDemuxPid(pid), optionalSourceFilter->getAidlFilter()); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mDescrambler != NULL) { - return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter()); + return mDescrambler->removePid(pid, optionalSourceFilter->getHalFilter()); } - return Result::INVALID_STATE;} + return Result::INVALID_STATE; +} Result DescramblerClient::close() { - // TODO: pending aidl interface + if (mTunerDescrambler != NULL) { + Status s = mTunerDescrambler->close(); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mDescrambler != NULL) { return mDescrambler->close(); @@ -95,4 +112,16 @@ Result DescramblerClient::close() { /////////////// DescramblerClient Helper Methods /////////////////////// +TunerDemuxPid DescramblerClient::getAidlDemuxPid(DemuxPid& pid) { + TunerDemuxPid aidlPid; + switch (pid.getDiscriminator()) { + case DemuxPid::hidl_discriminator::tPid: + aidlPid.set<TunerDemuxPid::tPid>((int)pid.tPid()); + break; + case DemuxPid::hidl_discriminator::mmtpPid: + aidlPid.set<TunerDemuxPid::mmtpPid>((int)pid.mmtpPid()); + break; + } + return aidlPid; +} } // namespace android diff --git a/media/jni/tuner/DescramblerClient.h b/media/jni/tuner/DescramblerClient.h index 8af688314db1..a8fa1e2e0497 100644 --- a/media/jni/tuner/DescramblerClient.h +++ b/media/jni/tuner/DescramblerClient.h @@ -17,14 +17,15 @@ #ifndef _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_ #define _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_ -//#include <aidl/android/media/tv/tuner/ITunerDescrambler.h> +#include <aidl/android/media/tv/tuner/ITunerDescrambler.h> #include <android/hardware/tv/tuner/1.0/IDescrambler.h> #include <android/hardware/tv/tuner/1.1/types.h> #include "DemuxClient.h" #include "FilterClient.h" -//using ::aidl::android::media::tv::tuner::ITunerDescrambler; +using ::aidl::android::media::tv::tuner::ITunerDescrambler; +using ::aidl::android::media::tv::tuner::TunerDemuxPid; using ::android::hardware::tv::tuner::V1_0::IDescrambler; using ::android::hardware::tv::tuner::V1_0::Result; @@ -37,8 +38,7 @@ namespace android { struct DescramblerClient : public RefBase { public: - // TODO: pending hidl interface - DescramblerClient(); + DescramblerClient(shared_ptr<ITunerDescrambler> tunerDescrambler); ~DescramblerClient(); // TODO: remove after migration to Tuner Service is done. @@ -70,12 +70,13 @@ public: Result close(); private: + TunerDemuxPid getAidlDemuxPid(DemuxPid& pid); + /** * An AIDL Tuner Descrambler Singleton assigned at the first time the Tuner Client * opens a descrambler. Default null when descrambler is not opened. */ - // TODO: pending on aidl interface - //shared_ptr<ITunerDescrambler> mTunerDescrambler; + shared_ptr<ITunerDescrambler> mTunerDescrambler; /** * A Descrambler HAL interface that is ready before migrating to the TunerDescrambler. diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp index 6b788170a944..8b4ca371056e 100644 --- a/media/jni/tuner/FilterClient.cpp +++ b/media/jni/tuner/FilterClient.cpp @@ -18,6 +18,7 @@ #include <aidlcommonsupport/NativeHandle.h> #include <android-base/logging.h> +#include <fmq/ConvertMQDescriptors.h> #include <utils/Log.h> #include "FilterClient.h" @@ -34,7 +35,7 @@ using ::aidl::android::media::tv::tuner::TunerFilterSectionTableInfo; using ::aidl::android::media::tv::tuner::TunerFilterSharedHandleInfo; using ::aidl::android::media::tv::tuner::TunerFilterTlvConfiguration; using ::aidl::android::media::tv::tuner::TunerFilterTsConfiguration; - +using ::android::hardware::hidl_vec; using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType; using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits; @@ -68,18 +69,12 @@ void FilterClient::setHidlFilter(sp<IFilter> filter) { mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter); } -int FilterClient::read(uint8_t* buffer, int size) { - // TODO: pending aidl interface - - if (mFilter != NULL) { - Result res = getFilterMq(); - if (res != Result::SUCCESS) { - return -1; - } - return copyData(buffer, size); +int FilterClient::read(int8_t* buffer, int size) { + Result res = getFilterMq(); + if (res != Result::SUCCESS) { + return -1; } - - return -1; + return copyData(buffer, size); } SharedHandleInfo FilterClient::getAvSharedHandleInfo() { @@ -106,7 +101,10 @@ Result FilterClient::configure(DemuxFilterSettings configure) { } Result FilterClient::configureMonitorEvent(int monitorEventType) { - // TODO: pending aidl interface + if (mTunerFilter != NULL) { + Status s = mTunerFilter->configureMonitorEvent(monitorEventType); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mFilter_1_1 != NULL) { return mFilter_1_1->configureMonitorEvent(monitorEventType); @@ -116,7 +114,10 @@ Result FilterClient::configureMonitorEvent(int monitorEventType) { } Result FilterClient::configureIpFilterContextId(int cid) { - // TODO: pending aidl interface + if (mTunerFilter != NULL) { + Status s = mTunerFilter->configureIpFilterContextId(cid); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mFilter_1_1 != NULL) { return mFilter_1_1->configureIpCid(cid); @@ -126,7 +127,19 @@ Result FilterClient::configureIpFilterContextId(int cid) { } Result FilterClient::configureAvStreamType(AvStreamType avStreamType) { - // TODO: pending aidl interface + if (mTunerFilter != NULL) { + int type; + switch (avStreamType.getDiscriminator()) { + case AvStreamType::hidl_discriminator::audio: + type = (int)avStreamType.audio(); + break; + case AvStreamType::hidl_discriminator::video: + type = (int)avStreamType.video(); + break; + } + Status s = mTunerFilter->configureAvStreamType(type); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mFilter_1_1 != NULL) { return mFilter_1_1->configureAvStreamType(avStreamType); @@ -228,7 +241,10 @@ Result FilterClient::releaseAvHandle(native_handle_t* handle, uint64_t avDataId) } Result FilterClient::setDataSource(sp<FilterClient> filterClient){ - // TODO: pending aidl interface + if (mTunerFilter != NULL) { + Status s = mTunerFilter->setDataSource(filterClient->getAidlFilter()); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mFilter != NULL) { sp<IFilter> sourceFilter = filterClient->getHalFilter(); @@ -687,10 +703,10 @@ void TunerFilterCallback::getHidlFilterEvent(const vector<TunerFilterEvent>& fil void TunerFilterCallback::getHidlMediaEvent( const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) { + event.events.resize(filterEvents.size()); for (int i = 0; i < filterEvents.size(); i++) { hidl_handle handle = hidl_handle(makeFromAidl(filterEvents[i] .get<TunerFilterEvent::media>().avMemory)); - event.events.resize(i + 1); event.events[i].media({ .avMemory = handle, .streamId = static_cast<DemuxStreamId>(filterEvents[i] @@ -736,9 +752,9 @@ void TunerFilterCallback::getHidlMediaEvent( void TunerFilterCallback::getHidlSectionEvent( const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) { + event.events.resize(filterEvents.size()); for (int i = 0; i < filterEvents.size(); i++) { auto section = filterEvents[i].get<TunerFilterEvent::section>(); - event.events.resize(i + 1); event.events[i].section({ .tableId = static_cast<uint16_t>(section.tableId), .version = static_cast<uint16_t>(section.version), @@ -750,9 +766,9 @@ void TunerFilterCallback::getHidlSectionEvent( void TunerFilterCallback::getHidlPesEvent( const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) { + event.events.resize(filterEvents.size()); for (int i = 0; i < filterEvents.size(); i++) { auto pes = filterEvents[i].get<TunerFilterEvent::pes>(); - event.events.resize(i + 1); event.events[i].pes({ .streamId = static_cast<DemuxStreamId>(pes.streamId), .dataLength = static_cast<uint16_t>(pes.dataLength), @@ -763,9 +779,10 @@ void TunerFilterCallback::getHidlPesEvent( void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) { + event.events.resize(filterEvents.size()); + eventExt.events.resize(filterEvents.size()); for (int i = 0; i < filterEvents.size(); i++) { auto ts = filterEvents[i].get<TunerFilterEvent::tsRecord>(); - event.events.resize(i + 1); event.events[i].tsRecord({ .tsIndexMask = static_cast<uint32_t>(ts.tsIndexMask), .byteNumber = static_cast<uint64_t>(ts.byteNumber), @@ -787,7 +804,6 @@ void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& f break; } - eventExt.events.resize(i + 1); if (ts.isExtended) { eventExt.events[i].tsRecord({ .pts = static_cast<uint64_t>(ts.pts), @@ -801,15 +817,15 @@ void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& f void TunerFilterCallback::getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) { + event.events.resize(filterEvents.size()); + eventExt.events.resize(filterEvents.size()); for (int i = 0; i < filterEvents.size(); i++) { auto mmtp = filterEvents[i].get<TunerFilterEvent::mmtpRecord>(); - event.events.resize(i + 1); event.events[i].mmtpRecord({ .scHevcIndexMask = static_cast<uint32_t>(mmtp.scHevcIndexMask), .byteNumber = static_cast<uint64_t>(mmtp.byteNumber), }); - eventExt.events.resize(i + 1); if (mmtp.isExtended) { eventExt.events[i].mmtpRecord({ .pts = static_cast<uint64_t>(mmtp.pts), @@ -825,9 +841,9 @@ void TunerFilterCallback::getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& void TunerFilterCallback::getHidlDownloadEvent(const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) { + event.events.resize(filterEvents.size()); for (int i = 0; i < filterEvents.size(); i++) { auto download = filterEvents[i].get<TunerFilterEvent::download>(); - event.events.resize(i + 1); event.events[i].download({ .itemId = static_cast<uint32_t>(download.itemId), .mpuSequenceNumber = static_cast<uint32_t>(download.mpuSequenceNumber), @@ -840,9 +856,9 @@ void TunerFilterCallback::getHidlDownloadEvent(const vector<TunerFilterEvent>& f void TunerFilterCallback::getHidlIpPayloadEvent(const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) { + event.events.resize(filterEvents.size()); for (int i = 0; i < filterEvents.size(); i++) { auto ip = filterEvents[i].get<TunerFilterEvent::ipPayload>(); - event.events.resize(i + 1); event.events[i].ipPayload({ .dataLength = static_cast<uint16_t>(ip.dataLength), }); @@ -851,15 +867,15 @@ void TunerFilterCallback::getHidlIpPayloadEvent(const vector<TunerFilterEvent>& void TunerFilterCallback::getHidlTemiEvent(const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) { + event.events.resize(filterEvents.size()); for (int i = 0; i < filterEvents.size(); i++) { auto temi = filterEvents[i].get<TunerFilterEvent::temi>(); - event.events.resize(i + 1); event.events[i].temi({ .pts = static_cast<uint64_t>(temi.pts), .descrTag = static_cast<uint8_t>(temi.descrTag), }); - vector<uint8_t> descrData(temi.descrData.size()); - copy(temi.descrData.begin(), temi.descrData.end(), descrData.begin()); + hidl_vec<uint8_t> descrData(temi.descrData.begin(), temi.descrData.end()); + event.events[i].temi().descrData = descrData; } } @@ -891,29 +907,43 @@ void TunerFilterCallback::getHidlRestartEvent(const vector<TunerFilterEvent>& fi } Result FilterClient::getFilterMq() { - if (mFilter == NULL) { - return Result::INVALID_STATE; - } - if (mFilterMQ != NULL) { return Result::SUCCESS; } - Result getQueueDescResult = Result::UNKNOWN_ERROR; - MQDescriptorSync<uint8_t> filterMQDesc; - mFilter->getQueueDesc( - [&](Result r, const MQDescriptorSync<uint8_t>& desc) { - filterMQDesc = desc; - getQueueDescResult = r; - }); - if (getQueueDescResult == Result::SUCCESS) { - mFilterMQ = std::make_unique<MQ>(filterMQDesc, true); - EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag); + AidlMQDesc aidlMqDesc; + Result res = Result::UNAVAILABLE; + + if (mTunerFilter != NULL) { + Status s = mTunerFilter->getQueueDesc(&aidlMqDesc); + res = ClientHelper::getServiceSpecificErrorCode(s); + if (res == Result::SUCCESS) { + mFilterMQ = new (nothrow) AidlMQ(aidlMqDesc); + EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag); + } + return res; + } + + if (mFilter != NULL) { + MQDescriptorSync<uint8_t> filterMQDesc; + mFilter->getQueueDesc( + [&](Result r, const MQDescriptorSync<uint8_t>& desc) { + filterMQDesc = desc; + res = r; + }); + if (res == Result::SUCCESS) { + AidlMQDesc aidlMQDesc; + unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>( + filterMQDesc, &aidlMQDesc); + mFilterMQ = new (nothrow) AidlMessageQueue(aidlMQDesc); + EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag); + } } - return getQueueDescResult; + + return res; } -int FilterClient::copyData(uint8_t* buffer, int size) { +int FilterClient::copyData(int8_t* buffer, int size) { if (mFilter == NULL || mFilterMQ == NULL || mFilterMQEventFlag == NULL) { return -1; } diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h index 21919ac10282..bbabc282464a 100644 --- a/media/jni/tuner/FilterClient.h +++ b/media/jni/tuner/FilterClient.h @@ -25,12 +25,14 @@ #include <android/hardware/tv/tuner/1.1/IFilter.h> #include <android/hardware/tv/tuner/1.1/IFilterCallback.h> #include <android/hardware/tv/tuner/1.1/types.h> +#include <fmq/AidlMessageQueue.h> #include <fmq/MessageQueue.h> #include "ClientHelper.h" #include "FilterClientCallback.h" using Status = ::ndk::ScopedAStatus; +using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite; using ::aidl::android::media::tv::tuner::BnTunerFilterCallback; using ::aidl::android::media::tv::tuner::ITunerFilter; using ::aidl::android::media::tv::tuner::TunerDemuxIpAddress; @@ -69,10 +71,13 @@ using ::android::hardware::tv::tuner::V1_1::IFilterCallback; using namespace std; -using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>; - namespace android { +using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>; +using MQDesc = MQDescriptorSync<uint8_t>; +using AidlMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>; +using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>; + struct SharedHandleInfo { native_handle_t* sharedHandle; uint64_t size; @@ -139,7 +144,7 @@ public: * * @return the actual reading size. -1 if failed to read. */ - int read(uint8_t* buffer, int size); + int read(int8_t* buffer, int size); /** * Get the a/v shared memory handle information @@ -234,7 +239,7 @@ private: void getAidlIpAddress(DemuxIpAddress ipAddr, TunerDemuxIpAddress& srcIpAddress, TunerDemuxIpAddress& dstIpAddress); Result getFilterMq(); - int copyData(uint8_t* buffer, int size); + int copyData(int8_t* buffer, int size); void checkIsMediaFilter(DemuxFilterType type); void handleAvShareMemory(); void closeAvSharedMemory(); @@ -259,7 +264,7 @@ private: */ sp<::android::hardware::tv::tuner::V1_1::IFilter> mFilter_1_1; - unique_ptr<MQ> mFilterMQ; + AidlMQ* mFilterMQ; EventFlag* mFilterMQEventFlag; sp<FilterClientCallback> mCallback; diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp index 08573a61c623..3a00133c69e2 100644 --- a/media/jni/tuner/FrontendClient.cpp +++ b/media/jni/tuner/FrontendClient.cpp @@ -27,23 +27,43 @@ using ::aidl::android::media::tv::tuner::TunerFrontendUnionSettings; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType; using ::android::hardware::tv::tuner::V1_0::FrontendAtscModulation; +using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Bandwidth; using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Modulation; +using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3TimeInterleaveMode; using ::android::hardware::tv::tuner::V1_0::FrontendDvbcAnnex; using ::android::hardware::tv::tuner::V1_0::FrontendDvbcModulation; +using ::android::hardware::tv::tuner::V1_0::FrontendDvbcSpectralInversion; using ::android::hardware::tv::tuner::V1_0::FrontendDvbsModulation; using ::android::hardware::tv::tuner::V1_0::FrontendDvbsStandard; +using ::android::hardware::tv::tuner::V1_0::FrontendDvbsRolloff; +using ::android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth; +using ::android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval; using ::android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy; using ::android::hardware::tv::tuner::V1_0::FrontendDvbtStandard; +using ::android::hardware::tv::tuner::V1_0::FrontendInnerFec; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsModulation; +using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsRolloff; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Modulation; +using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Rolloff; +using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtBandwidth; +using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtGuardInterval; +using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtMode; using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtModulation; using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo; -using ::android::hardware::tv::tuner::V1_0::FrontendType; +using ::android::hardware::tv::tuner::V1_0::LnbVoltage; using ::android::hardware::tv::tuner::V1_1::Constant; +using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode; +using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth; +using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval; using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation; +using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode; +using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode; +using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth; using ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation; +using ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode; using ::android::hardware::tv::tuner::V1_1::FrontendModulation; using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion; +using ::android::hardware::tv::tuner::V1_1::FrontendType; namespace android { @@ -71,8 +91,8 @@ Result FrontendClient::setCallback(sp<FrontendClientCallback> frontendClientCall if (mTunerFrontend != NULL) { mAidlCallback = ::ndk::SharedRefBase::make<TunerFrontendCallback>(frontendClientCallback); mAidlCallback->setFrontendType(mType); - mTunerFrontend->setCallback(mAidlCallback); - return Result::SUCCESS; + Status s = mTunerFrontend->setCallback(mAidlCallback); + return ClientHelper::getServiceSpecificErrorCode(s); } mHidlCallback = new HidlFrontendCallback(frontendClientCallback); @@ -160,9 +180,16 @@ vector<FrontendStatus> FrontendClient::getStatus(vector<FrontendStatusType> stat vector<FrontendStatus> status; if (mTunerFrontend != NULL) { - // TODO: handle error message. - /*status = mTunerFrontend->getStatus(statusTypes); - return status;*/ + vector<TunerFrontendStatus> aidlStatus; + vector<int> types; + for (auto t : statusTypes) { + types.push_back((int)t); + } + Status s = mTunerFrontend->getStatus(types, &aidlStatus); + if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { + return status; + } + return getHidlStatus(aidlStatus); } if (mFrontend != NULL && statusTypes.size() > 0) { @@ -180,14 +207,22 @@ vector<FrontendStatus> FrontendClient::getStatus(vector<FrontendStatusType> stat return status; } + vector<FrontendStatusExt1_1> FrontendClient::getStatusExtended_1_1( vector<FrontendStatusTypeExt1_1> statusTypes) { vector<FrontendStatusExt1_1> status; if (mTunerFrontend != NULL) { - // TODO: handle error message. - /*status = mTunerFrontend->getStatusExtended_1_1(statusTypes); - return status;*/ + vector<TunerFrontendStatus> aidlStatus; + vector<int> types; + for (auto t : statusTypes) { + types.push_back((int)t); + } + Status s = mTunerFrontend->getStatusExtended_1_1(types, &aidlStatus); + if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { + return status; + } + return getHidlStatusExt(aidlStatus); } if (mFrontend_1_1 != NULL && statusTypes.size() > 0) { @@ -208,9 +243,8 @@ vector<FrontendStatusExt1_1> FrontendClient::getStatusExtended_1_1( Result FrontendClient::setLnb(sp<LnbClient> lnbClient) { if (mTunerFrontend != NULL) { - // TODO: handle error message. - /*mTunerFrontend->setLnb(lnbClient->getAidlLnb()); - return Result::SUCCESS;*/ + Status s = mTunerFrontend->setLnb(lnbClient->getAidlLnb()); + return ClientHelper::getServiceSpecificErrorCode(s); } if (mFrontend != NULL) { @@ -223,9 +257,8 @@ Result FrontendClient::setLnb(sp<LnbClient> lnbClient) { Result FrontendClient::setLna(bool bEnable) { if (mTunerFrontend != NULL) { - // TODO: handle error message. - /*mTunerFrontend->setLna(bEnable); - return Result::SUCCESS;*/ + Status s = mTunerFrontend->setLna(bEnable); + return ClientHelper::getServiceSpecificErrorCode(s); } if (mFrontend != NULL) { @@ -240,9 +273,11 @@ int FrontendClient::linkCiCamToFrontend(int ciCamId) { int ltsId = (int)Constant::INVALID_LTS_ID; if (mTunerFrontend != NULL) { - // TODO: handle error message. - /*mTunerFrontend->linkCiCamToFrontend(ciCamId, ltsId); - return ltsId;*/ + Status s = mTunerFrontend->linkCiCamToFrontend(ciCamId, <sId); + if (ClientHelper::getServiceSpecificErrorCode(s) == Result::SUCCESS) { + return ltsId; + } + return (int)Constant::INVALID_LTS_ID; } if (mFrontend_1_1 != NULL) { @@ -262,9 +297,8 @@ int FrontendClient::linkCiCamToFrontend(int ciCamId) { Result FrontendClient::unlinkCiCamToFrontend(int ciCamId) { if (mTunerFrontend != NULL) { - // TODO: handle error message. - /*mTunerFrontend->unlinkCiCamToFrontend(ciCamId); - return Result::SUCCESS;*/ + Status s = mTunerFrontend->unlinkCiCamToFrontend(ciCamId); + return ClientHelper::getServiceSpecificErrorCode(s); } if (mFrontend_1_1 != NULL) { @@ -302,6 +336,400 @@ int FrontendClient::getId() { return mId; } +vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>& aidlStatus) { + vector<FrontendStatus> hidlStatus; + for (TunerFrontendStatus s : aidlStatus) { + FrontendStatus status; + switch (s.getTag()) { + case TunerFrontendStatus::isDemodLocked: { + status.isDemodLocked(s.get<TunerFrontendStatus::isDemodLocked>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::snr: { + status.snr(s.get<TunerFrontendStatus::snr>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::ber: { + status.ber((uint32_t)s.get<TunerFrontendStatus::ber>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::per: { + status.per((uint32_t)s.get<TunerFrontendStatus::per>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::preBer: { + status.preBer((uint32_t)s.get<TunerFrontendStatus::preBer>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::signalQuality: { + status.signalQuality((uint32_t)s.get<TunerFrontendStatus::signalQuality>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::signalStrength: { + status.signalStrength(s.get<TunerFrontendStatus::signalStrength>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::symbolRate: { + status.symbolRate((uint32_t)s.get<TunerFrontendStatus::symbolRate>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::innerFec: { + status.innerFec(static_cast<FrontendInnerFec>( + s.get<TunerFrontendStatus::innerFec>())); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::modulation: { + auto aidlMod = s.get<TunerFrontendStatus::modulation>(); + switch (mType) { + case (int)FrontendType::DVBC: + status.modulation().dvbc(static_cast<FrontendDvbcModulation>(aidlMod)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::DVBS: + status.modulation().dvbs(static_cast<FrontendDvbsModulation>(aidlMod)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::ISDBS: + status.modulation().isdbs(static_cast<FrontendIsdbsModulation>(aidlMod)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::ISDBS3: + status.modulation().isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::ISDBT: + status.modulation().isdbt(static_cast<FrontendIsdbtModulation>(aidlMod)); + hidlStatus.push_back(status); + break; + default: + break; + } + break; + } + case TunerFrontendStatus::inversion: { + status.inversion(static_cast<FrontendDvbcSpectralInversion>( + s.get<TunerFrontendStatus::inversion>())); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::lnbVoltage: { + status.lnbVoltage(static_cast<LnbVoltage>( + s.get<TunerFrontendStatus::lnbVoltage>())); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::plpId: { + status.plpId((uint8_t)s.get<TunerFrontendStatus::plpId>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::isEWBS: { + status.isEWBS(s.get<TunerFrontendStatus::isEWBS>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::agc: { + status.agc((uint8_t)s.get<TunerFrontendStatus::agc>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::isLnaOn: { + status.isLnaOn(s.get<TunerFrontendStatus::isLnaOn>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::isLayerError: { + auto aidlE = s.get<TunerFrontendStatus::isLayerError>(); + hidl_vec<bool> e(aidlE.begin(), aidlE.end()); + status.isLayerError(e); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::mer: { + status.mer(s.get<TunerFrontendStatus::mer>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::freqOffset: { + status.freqOffset(s.get<TunerFrontendStatus::freqOffset>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::hierarchy: { + status.hierarchy(static_cast<FrontendDvbtHierarchy>( + s.get<TunerFrontendStatus::freqOffset>())); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::isRfLocked: { + status.isRfLocked(s.get<TunerFrontendStatus::isRfLocked>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::plpInfo: { + int size = s.get<TunerFrontendStatus::plpInfo>().size(); + status.plpInfo().resize(size); + for (int i = 0; i < size; i++) { + auto aidlInfo = s.get<TunerFrontendStatus::plpInfo>()[i]; + status.plpInfo()[i] = { + .plpId = (uint8_t)aidlInfo.plpId, + .isLocked = aidlInfo.isLocked, + .uec = (uint32_t)aidlInfo.uec, + }; + } + hidlStatus.push_back(status); + break; + } + default: + break; + } + } + return hidlStatus; +} + +vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt( + vector<TunerFrontendStatus>& aidlStatus) { + vector<FrontendStatusExt1_1> hidlStatus; + for (TunerFrontendStatus s : aidlStatus) { + FrontendStatusExt1_1 status; + switch (s.getTag()) { + case TunerFrontendStatus::modulations: { + for (auto aidlMod : s.get<TunerFrontendStatus::modulations>()) { + int size = status.modulations().size(); + status.modulations().resize(size + 1); + switch (mType) { + case (int)FrontendType::DVBC: + status.modulations()[size].dvbc( + static_cast<FrontendDvbcModulation>(aidlMod)); + break; + case (int)FrontendType::DVBS: + status.modulations()[size].dvbs( + static_cast<FrontendDvbsModulation>(aidlMod)); + break; + case (int)FrontendType::DVBT: + status.modulations()[size].dvbt( + static_cast<FrontendDvbtConstellation>(aidlMod)); + break; + case (int)FrontendType::ISDBS: + status.modulations()[size].isdbs( + static_cast<FrontendIsdbsModulation>(aidlMod)); + break; + case (int)FrontendType::ISDBS3: + status.modulations()[size].isdbs3( + static_cast<FrontendIsdbs3Modulation>(aidlMod)); + break; + case (int)FrontendType::ISDBT: + status.modulations()[size].isdbt( + static_cast<FrontendIsdbtModulation>(aidlMod)); + break; + case (int)FrontendType::ATSC: + status.modulations()[size].atsc( + static_cast<FrontendAtscModulation>(aidlMod)); + break; + case (int)FrontendType::ATSC3: + status.modulations()[size].atsc3( + static_cast<FrontendAtsc3Modulation>(aidlMod)); + break; + case (int)FrontendType::DTMB: + status.modulations()[size].dtmb( + static_cast<FrontendDtmbModulation>(aidlMod)); + break; + default: + status.modulations().resize(size); + break; + } + } + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::bers: { + auto aidlB = s.get<TunerFrontendStatus::bers>(); + hidl_vec<uint32_t> b(aidlB.begin(), aidlB.end()); + status.bers(b); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::codeRates: { + int size = s.get<TunerFrontendStatus::codeRates>().size(); + status.codeRates().resize(size); + for (int i = 0; i < size; i++) { + auto aidlCodeRate = s.get<TunerFrontendStatus::codeRates>()[i]; + status.codeRates()[i] = + static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate); + } + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::bandwidth: { + auto aidlBand = s.get<TunerFrontendStatus::bandwidth>(); + switch (mType) { + case (int)FrontendType::ATSC3: + status.bandwidth().atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::DVBC: + status.bandwidth().dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::DVBT: + status.bandwidth().dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::ISDBT: + status.bandwidth().isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::DTMB: + status.bandwidth().dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand)); + hidlStatus.push_back(status); + break; + default: + break; + } + break; + } + case TunerFrontendStatus::interval: { + auto aidlInter = s.get<TunerFrontendStatus::interval>(); + switch (mType) { + case (int)FrontendType::DVBT: + status.interval().dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::ISDBT: + status.interval().isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::DTMB: + status.interval().dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter)); + hidlStatus.push_back(status); + break; + default: + break; + } + break; + } + case TunerFrontendStatus::transmissionMode: { + auto aidlTran = s.get<TunerFrontendStatus::transmissionMode>(); + switch (mType) { + case (int)FrontendType::DVBT: + status.transmissionMode().dvbt( + static_cast<FrontendDvbtTransmissionMode>(aidlTran)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::ISDBT: + status.transmissionMode().isdbt(static_cast<FrontendIsdbtMode>(aidlTran)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::DTMB: + status.transmissionMode().dtmb( + static_cast<FrontendDtmbTransmissionMode>(aidlTran)); + hidlStatus.push_back(status); + break; + default: + break; + } + break; + } + case TunerFrontendStatus::uec: { + status.uec((uint32_t)s.get<TunerFrontendStatus::uec>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::systemId: { + status.systemId((uint16_t)s.get<TunerFrontendStatus::systemId>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::interleaving: { + for (auto aidlInter : s.get<TunerFrontendStatus::interleaving>()) { + int size = status.interleaving().size(); + status.interleaving().resize(size + 1); + switch (mType) { + case (int)FrontendType::DVBC: + status.interleaving()[size].dvbc( + static_cast<FrontendCableTimeInterleaveMode>(aidlInter)); + break; + case (int)FrontendType::ATSC3: + status.interleaving()[size].atsc3( + static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter)); + break; + case (int)FrontendType::DTMB: + status.interleaving()[size].dtmb( + static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter)); + break; + default: + status.interleaving().resize(size); + break; + } + } + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::isdbtSegment: { + auto aidlSeg = s.get<TunerFrontendStatus::isdbtSegment>(); + hidl_vec<uint8_t> s(aidlSeg.begin(), aidlSeg.end()); + status.isdbtSegment(s); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::tsDataRate: { + auto aidlTs = s.get<TunerFrontendStatus::tsDataRate>(); + hidl_vec<uint32_t> ts(aidlTs.begin(), aidlTs.end()); + status.tsDataRate(ts); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::rollOff: { + auto aidlRoll = s.get<TunerFrontendStatus::rollOff>(); + switch (mType) { + case (int)FrontendType::DVBS: + status.rollOff().dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::ISDBS: + status.rollOff().isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll)); + hidlStatus.push_back(status); + break; + case (int)FrontendType::ISDBS3: + status.rollOff().isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll)); + hidlStatus.push_back(status); + break; + default: + break; + } + break; + } + case TunerFrontendStatus::isMiso: { + status.isMiso(s.get<TunerFrontendStatus::isMiso>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::isLinear: { + status.isLinear(s.get<TunerFrontendStatus::isLinear>()); + hidlStatus.push_back(status); + break; + } + case TunerFrontendStatus::isShortFrames: { + status.isShortFrames(s.get<TunerFrontendStatus::isShortFrames>()); + hidlStatus.push_back(status); + break; + } + default: + break; + } + } + return hidlStatus; +} + TunerFrontendSettings FrontendClient::getAidlFrontendSettings(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1) { bool isExtended = validateExtendedSettings(settingsExt1_1); @@ -686,14 +1114,15 @@ FrontendScanMessage TunerFrontendCallback::getHalScanMessage( vector<TunerFrontendScanAtsc3PlpInfo> plp = message.get<TunerFrontendScanMessage::atsc3PlpInfos>(); hidl_vec<FrontendScanAtsc3PlpInfo> plpInfo; - for (TunerFrontendScanAtsc3PlpInfo info : plp) { + int size = plp.size(); + plpInfo.resize(size); + for (int i = 0; i < size; i++) { + auto info = message.get<TunerFrontendScanMessage::atsc3PlpInfos>()[i]; FrontendScanAtsc3PlpInfo p{ .plpId = static_cast<uint8_t>(info.plpId), .bLlsFlag = info.llsFlag, }; - int size = plpInfo.size(); - plpInfo.resize(size + 1); - plpInfo[size] = p; + plpInfo[i] = p; } scanMessage.atsc3PlpInfos(plpInfo); break; diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h index b0107ff174d4..298b3974aeb9 100644 --- a/media/jni/tuner/FrontendClient.h +++ b/media/jni/tuner/FrontendClient.h @@ -43,6 +43,7 @@ using ::aidl::android::media::tv::tuner::TunerFrontendIsdbs3Settings; using ::aidl::android::media::tv::tuner::TunerFrontendIsdbtSettings; using ::aidl::android::media::tv::tuner::TunerFrontendScanMessage; using ::aidl::android::media::tv::tuner::TunerFrontendSettings; +using ::aidl::android::media::tv::tuner::TunerFrontendStatus; using ::android::hardware::Return; using ::android::hardware::Void; @@ -182,6 +183,9 @@ public: int getId(); private: + vector<FrontendStatus> getHidlStatus(vector<TunerFrontendStatus>& aidlStatus); + vector<FrontendStatusExt1_1> getHidlStatusExt(vector<TunerFrontendStatus>& aidlStatus); + TunerFrontendSettings getAidlFrontendSettings( const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1); TunerFrontendAnalogSettings getAidlAnalogSettings( diff --git a/media/jni/tuner/LnbClient.h b/media/jni/tuner/LnbClient.h index e7869e890e8f..465dc2331ecf 100644 --- a/media/jni/tuner/LnbClient.h +++ b/media/jni/tuner/LnbClient.h @@ -108,7 +108,7 @@ public: */ Result close(); - //shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; } + shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; } void setId(LnbId id) { mId = id; } LnbId getId() { return mId; } diff --git a/media/jni/tuner/TimeFilterClient.cpp b/media/jni/tuner/TimeFilterClient.cpp index 27ea6e596204..432238d261e5 100644 --- a/media/jni/tuner/TimeFilterClient.cpp +++ b/media/jni/tuner/TimeFilterClient.cpp @@ -19,6 +19,7 @@ #include <android-base/logging.h> #include <utils/Log.h> +#include "ClientHelper.h" #include "TimeFilterClient.h" using ::android::hardware::tv::tuner::V1_0::Result; @@ -28,13 +29,12 @@ namespace android { /////////////// TimeFilterClient /////////////////////// -// TODO: pending aidl interface -TimeFilterClient::TimeFilterClient() { - //mTunerTimeFilter = tunerTimeFilter; +TimeFilterClient::TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter) { + mTunerTimeFilter = tunerTimeFilter; } TimeFilterClient::~TimeFilterClient() { - //mTunerTimeFilter = NULL; + mTunerTimeFilter = NULL; mTimeFilter = NULL; } @@ -44,7 +44,10 @@ void TimeFilterClient::setHidlTimeFilter(sp<ITimeFilter> timeFilter) { } Result TimeFilterClient::setTimeStamp(long timeStamp) { - // TODO: pending aidl interface + if (mTunerTimeFilter != NULL) { + Status s = mTunerTimeFilter->setTimeStamp(timeStamp); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mTimeFilter != NULL) { return mTimeFilter->setTimeStamp(timeStamp); @@ -54,7 +57,10 @@ Result TimeFilterClient::setTimeStamp(long timeStamp) { } Result TimeFilterClient::clearTimeStamp() { - // TODO: pending aidl interface + if (mTunerTimeFilter != NULL) { + Status s = mTunerTimeFilter->clearTimeStamp(); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mTimeFilter != NULL) { return mTimeFilter->clearTimeStamp(); @@ -64,7 +70,14 @@ Result TimeFilterClient::clearTimeStamp() { } long TimeFilterClient::getTimeStamp() { - // TODO: pending aidl interface + if (mTunerTimeFilter != NULL) { + int64_t timeStamp; + Status s = mTunerTimeFilter->getTimeStamp(&timeStamp); + if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { + return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP; + } + return timeStamp; + } if (mTimeFilter != NULL) { Result res; @@ -84,27 +97,37 @@ long TimeFilterClient::getTimeStamp() { } long TimeFilterClient::getSourceTime() { - // TODO: pending aidl interface + if (mTunerTimeFilter != NULL) { + int64_t sourceTime; + Status s = mTunerTimeFilter->getTimeStamp(&sourceTime); + if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { + return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP; + } + return sourceTime; + } if (mTimeFilter != NULL) { Result res; - long timestamp; + long sourceTime; mTimeFilter->getSourceTime( [&](Result r, uint64_t t) { res = r; - timestamp = t; + sourceTime = t; }); if (res != Result::SUCCESS) { return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP; } - return timestamp; + return sourceTime; } return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP; } Result TimeFilterClient::close() { - // TODO: pending aidl interface + if (mTunerTimeFilter != NULL) { + Status s = mTunerTimeFilter->close(); + return ClientHelper::getServiceSpecificErrorCode(s); + } if (mTimeFilter != NULL) { return mTimeFilter->close(); diff --git a/media/jni/tuner/TimeFilterClient.h b/media/jni/tuner/TimeFilterClient.h index 9a9d172665ec..56ddd68d363e 100644 --- a/media/jni/tuner/TimeFilterClient.h +++ b/media/jni/tuner/TimeFilterClient.h @@ -17,12 +17,13 @@ #ifndef _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_ #define _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_ -//#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h> +#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h> #include <android/hardware/tv/tuner/1.0/ITimeFilter.h> #include <android/hardware/tv/tuner/1.1/types.h> -//using ::aidl::android::media::tv::tuner::ITunerTimeFilter; +using ::aidl::android::media::tv::tuner::ITunerTimeFilter; +using Status = ::ndk::ScopedAStatus; using ::android::hardware::Return; using ::android::hardware::Void; using ::android::hardware::hidl_vec; @@ -36,8 +37,7 @@ namespace android { struct TimeFilterClient : public RefBase { public: - // TODO: add TunerTimeFilter as parameter. - TimeFilterClient(); + TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter); ~TimeFilterClient(); // TODO: remove after migration to Tuner Service is done. @@ -73,8 +73,7 @@ private: * An AIDL Tuner TimeFilter Singleton assigned at the first time the Tuner Client * opens an TimeFilter. Default null when time filter is not opened. */ - // TODO: pending on aidl interface - //shared_ptr<ITunerTimeFilter> mTunerTimeFilter; + shared_ptr<ITunerTimeFilter> mTunerTimeFilter; /** * A TimeFilter HAL interface that is ready before migrating to the TunerTimeFilter. diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp index 14393a1081c6..7f954b561567 100644 --- a/media/jni/tuner/TunerClient.cpp +++ b/media/jni/tuner/TunerClient.cpp @@ -22,7 +22,10 @@ #include "TunerClient.h" +using ::aidl::android::media::tv::tuner::TunerFrontendCapabilities; +using ::aidl::android::media::tv::tuner::TunerFrontendDtmbCapabilities; using ::android::hardware::tv::tuner::V1_0::FrontendId; +using ::android::hardware::tv::tuner::V1_0::FrontendStatusType; using ::android::hardware::tv::tuner::V1_0::FrontendType; namespace android { @@ -136,12 +139,11 @@ sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) { shared_ptr<FrontendInfo> TunerClient::getFrontendInfo(int id) { if (mTunerService != NULL) { TunerFrontendInfo aidlFrontendInfo; - // TODO: handle error code Status s = mTunerService->getFrontendInfo(id, &aidlFrontendInfo); if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { return NULL; } - return make_shared<FrontendInfo>(FrontendInfoAidlToHidl(aidlFrontendInfo)); + return make_shared<FrontendInfo>(frontendInfoAidlToHidl(aidlFrontendInfo)); } if (mTuner != NULL) { @@ -157,7 +159,22 @@ shared_ptr<FrontendInfo> TunerClient::getFrontendInfo(int id) { } shared_ptr<FrontendDtmbCapabilities> TunerClient::getFrontendDtmbCapabilities(int id) { - // pending aidl interface + if (mTunerService != NULL) { + TunerFrontendDtmbCapabilities dtmbCaps; + Status s = mTunerService->getFrontendDtmbCapabilities(id, &dtmbCaps); + if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { + return NULL; + } + FrontendDtmbCapabilities hidlCaps{ + .transmissionModeCap = static_cast<uint32_t>(dtmbCaps.transmissionModeCap), + .bandwidthCap = static_cast<uint32_t>(dtmbCaps.bandwidthCap), + .modulationCap = static_cast<uint32_t>(dtmbCaps.modulationCap), + .codeRateCap = static_cast<uint32_t>(dtmbCaps.codeRateCap), + .guardIntervalCap = static_cast<uint32_t>(dtmbCaps.guardIntervalCap), + .interleaveModeCap = static_cast<uint32_t>(dtmbCaps.interleaveModeCap), + }; + return make_shared<FrontendDtmbCapabilities>(hidlCaps); + } if (mTuner_1_1 != NULL) { Result result; @@ -200,7 +217,14 @@ sp<DemuxClient> TunerClient::openDemux(int demuxHandle) { } shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() { - // pending aidl interface + if (mTunerService != NULL) { + TunerDemuxCapabilities aidlCaps; + Status s = mTunerService->getDemuxCaps(&aidlCaps); + if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { + return NULL; + } + return make_shared<DemuxCapabilities>(getHidlDemuxCaps(aidlCaps)); + } if (mTuner != NULL) { Result res; @@ -217,17 +241,18 @@ shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() { return NULL; } -sp<DescramblerClient> TunerClient::openDescrambler(int /*descramblerHandle*/) { +sp<DescramblerClient> TunerClient::openDescrambler(int descramblerHandle) { if (mTunerService != NULL) { - // TODO: handle error code - /*shared_ptr<ITunerDescrambler> tunerDescrambler; - mTunerService->openDescrambler(demuxHandle, &tunerDescrambler); - return new DescramblerClient(tunerDescrambler);*/ + shared_ptr<ITunerDescrambler> tunerDescrambler; + Status s = mTunerService->openDescrambler(descramblerHandle, &tunerDescrambler); + if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) { + return NULL; + } + return new DescramblerClient(tunerDescrambler); } if (mTuner != NULL) { - // TODO: pending aidl interface - sp<DescramblerClient> descramblerClient = new DescramblerClient(); + sp<DescramblerClient> descramblerClient = new DescramblerClient(NULL); sp<IDescrambler> hidlDescrambler = openHidlDescrambler(); if (hidlDescrambler != NULL) { descramblerClient->setHidlDescrambler(hidlDescrambler); @@ -459,7 +484,27 @@ sp<IDescrambler> TunerClient::openHidlDescrambler() { return descrambler; } -FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) { +DemuxCapabilities TunerClient::getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps) { + DemuxCapabilities caps{ + .numDemux = (uint32_t)aidlCaps.numDemux, + .numRecord = (uint32_t)aidlCaps.numRecord, + .numPlayback = (uint32_t)aidlCaps.numPlayback, + .numTsFilter = (uint32_t)aidlCaps.numTsFilter, + .numSectionFilter = (uint32_t)aidlCaps.numSectionFilter, + .numAudioFilter = (uint32_t)aidlCaps.numAudioFilter, + .numVideoFilter = (uint32_t)aidlCaps.numVideoFilter, + .numPesFilter = (uint32_t)aidlCaps.numPesFilter, + .numPcrFilter = (uint32_t)aidlCaps.numPcrFilter, + .numBytesInSectionFilter = (uint32_t)aidlCaps.numBytesInSectionFilter, + .filterCaps = (uint32_t)aidlCaps.filterCaps, + .bTimeFilter = aidlCaps.bTimeFilter, + }; + caps.linkCaps.resize(aidlCaps.linkCaps.size()); + copy(aidlCaps.linkCaps.begin(), aidlCaps.linkCaps.end(), caps.linkCaps.begin()); + return caps; +} + +FrontendInfo TunerClient::frontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) { FrontendInfo hidlFrontendInfo { .type = static_cast<FrontendType>(aidlFrontendInfo.type), .minFrequency = static_cast<uint32_t>(aidlFrontendInfo.minFrequency), @@ -469,8 +514,102 @@ FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendI .acquireRange = static_cast<uint32_t>(aidlFrontendInfo.acquireRange), .exclusiveGroupId = static_cast<uint32_t>(aidlFrontendInfo.exclusiveGroupId), }; - // TODO: handle Frontend caps + int size = aidlFrontendInfo.statusCaps.size(); + hidlFrontendInfo.statusCaps.resize(size); + for (int i = 0; i < size; i++) { + hidlFrontendInfo.statusCaps[i] = + static_cast<FrontendStatusType>(aidlFrontendInfo.statusCaps[i]); + } + + switch (aidlFrontendInfo.caps.getTag()) { + case TunerFrontendCapabilities::analogCaps: { + auto analog = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::analogCaps>(); + hidlFrontendInfo.frontendCaps.analogCaps({ + .typeCap = static_cast<uint32_t>(analog.typeCap), + .sifStandardCap = static_cast<uint32_t>(analog.sifStandardCap), + }); + break; + } + case TunerFrontendCapabilities::atscCaps: { + auto atsc = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::atscCaps>(); + hidlFrontendInfo.frontendCaps.atscCaps({ + .modulationCap = static_cast<uint32_t>(atsc.modulationCap), + }); + break; + } + case TunerFrontendCapabilities::atsc3Caps: { + auto atsc3 = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::atsc3Caps>(); + hidlFrontendInfo.frontendCaps.atsc3Caps({ + .bandwidthCap = static_cast<uint32_t>(atsc3.bandwidthCap), + .modulationCap = static_cast<uint32_t>(atsc3.modulationCap), + .timeInterleaveModeCap = static_cast<uint32_t>(atsc3.timeInterleaveModeCap), + .codeRateCap = static_cast<uint32_t>(atsc3.codeRateCap), + .fecCap = static_cast<uint32_t>(atsc3.fecCap), + .demodOutputFormatCap = static_cast<uint8_t>(atsc3.demodOutputFormatCap), + }); + break; + } + case TunerFrontendCapabilities::cableCaps: { + auto cable = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::cableCaps>(); + hidlFrontendInfo.frontendCaps.dvbcCaps({ + .modulationCap = static_cast<uint32_t>(cable.modulationCap), + .fecCap = static_cast<uint64_t>(cable.codeRateCap), + .annexCap = static_cast<uint8_t>(cable.annexCap), + }); + break; + } + case TunerFrontendCapabilities::dvbsCaps: { + auto dvbs = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::dvbsCaps>(); + hidlFrontendInfo.frontendCaps.dvbsCaps({ + .modulationCap = static_cast<int32_t>(dvbs.modulationCap), + .innerfecCap = static_cast<uint64_t>(dvbs.codeRateCap), + .standard = static_cast<uint8_t>(dvbs.standard), + }); + break; + } + case TunerFrontendCapabilities::dvbtCaps: { + auto dvbt = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::dvbtCaps>(); + hidlFrontendInfo.frontendCaps.dvbtCaps({ + .transmissionModeCap = static_cast<uint32_t>(dvbt.transmissionModeCap), + .bandwidthCap = static_cast<uint32_t>(dvbt.bandwidthCap), + .constellationCap = static_cast<uint32_t>(dvbt.constellationCap), + .coderateCap = static_cast<uint32_t>(dvbt.codeRateCap), + .hierarchyCap = static_cast<uint32_t>(dvbt.hierarchyCap), + .guardIntervalCap = static_cast<uint32_t>(dvbt.guardIntervalCap), + .isT2Supported = dvbt.isT2Supported, + .isMisoSupported = dvbt.isMisoSupported, + }); + break; + } + case TunerFrontendCapabilities::isdbsCaps: { + auto isdbs = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbsCaps>(); + hidlFrontendInfo.frontendCaps.isdbsCaps({ + .modulationCap = static_cast<uint32_t>(isdbs.modulationCap), + .coderateCap = static_cast<uint32_t>(isdbs.codeRateCap), + }); + break; + } + case TunerFrontendCapabilities::isdbs3Caps: { + auto isdbs3 = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbs3Caps>(); + hidlFrontendInfo.frontendCaps.isdbs3Caps({ + .modulationCap = static_cast<uint32_t>(isdbs3.modulationCap), + .coderateCap = static_cast<uint32_t>(isdbs3.codeRateCap), + }); + break; + } + case TunerFrontendCapabilities::isdbtCaps: { + auto isdbt = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbtCaps>(); + hidlFrontendInfo.frontendCaps.isdbtCaps({ + .modeCap = static_cast<uint32_t>(isdbt.modeCap), + .bandwidthCap = static_cast<uint32_t>(isdbt.bandwidthCap), + .modulationCap = static_cast<uint32_t>(isdbt.modulationCap), + .coderateCap = static_cast<uint32_t>(isdbt.codeRateCap), + .guardIntervalCap = static_cast<uint32_t>(isdbt.guardIntervalCap), + }); + break; + } + } return hidlFrontendInfo; } diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h index 6ce666196be8..744bf2058766 100644 --- a/media/jni/tuner/TunerClient.h +++ b/media/jni/tuner/TunerClient.h @@ -32,6 +32,7 @@ using Status = ::ndk::ScopedAStatus; +using ::aidl::android::media::tv::tuner::TunerDemuxCapabilities; using ::aidl::android::media::tv::tuner::ITunerService; using ::aidl::android::media::tv::tuner::TunerFrontendInfo; using ::aidl::android::media::tv::tunerresourcemanager::ITunerResourceManager; @@ -145,7 +146,8 @@ private: sp<ILnb> openHidlLnbByName(string name, LnbId& lnbId); sp<IDescrambler> openHidlDescrambler(); vector<int> getLnbHandles(); - FrontendInfo FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo); + DemuxCapabilities getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps); + FrontendInfo frontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo); void updateTunerResources(); void updateFrontendResources(); void updateLnbResources(); diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp index 45f42f1b5dc6..48d738039696 100644 --- a/native/android/system_fonts.cpp +++ b/native/android/system_fonts.cpp @@ -264,7 +264,7 @@ AFont* _Nonnull AFontMatcher_match( static_cast<minikin::FamilyVariant>(matcher->mFamilyVariant), 1 /* maxRun */); - const minikin::Font* font = runs[0].fakedFont.font; + const std::shared_ptr<minikin::Font>& font = runs[0].fakedFont.font; std::unique_ptr<AFont> result = std::make_unique<AFont>(); const android::MinikinFontSkia* minikinFontSkia = reinterpret_cast<android::MinikinFontSkia*>(font->typeface().get()); diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 2716e092c6c3..a74613970ce7 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -1368,7 +1368,7 @@ public class ConnectivityManager { public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) { try { return mService.getDefaultNetworkCapabilitiesForUser( - userId, mContext.getOpPackageName()); + userId, mContext.getOpPackageName(), getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1450,7 +1450,8 @@ public class ConnectivityManager { @Nullable public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) { try { - return mService.getNetworkCapabilities(network, mContext.getOpPackageName()); + return mService.getNetworkCapabilities( + network, mContext.getOpPackageName(), getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3231,32 +3232,6 @@ public class ConnectivityManager { } } - /** {@hide} - returns the factory serial number */ - @UnsupportedAppUsage - @RequiresPermission(anyOf = { - NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, - android.Manifest.permission.NETWORK_FACTORY}) - public int registerNetworkFactory(Messenger messenger, String name) { - try { - return mService.registerNetworkFactory(messenger, name); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** {@hide} */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - @RequiresPermission(anyOf = { - NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, - android.Manifest.permission.NETWORK_FACTORY}) - public void unregisterNetworkFactory(Messenger messenger) { - try { - mService.unregisterNetworkFactory(messenger); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** * Registers the specified {@link NetworkProvider}. * Each listener must only be registered once. The listener can be unregistered with @@ -3761,7 +3736,8 @@ public class ConnectivityManager { Binder binder = new Binder(); if (reqType == LISTEN) { request = mService.listenForNetwork( - need, messenger, binder, callingPackageName); + need, messenger, binder, callingPackageName, + getAttributionTag()); } else { request = mService.requestNetwork( need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType, @@ -4206,7 +4182,8 @@ public class ConnectivityManager { checkPendingIntentNotNull(operation); try { mService.pendingListenForNetwork( - request.networkCapabilities, operation, mContext.getOpPackageName()); + request.networkCapabilities, operation, mContext.getOpPackageName(), + getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { @@ -4871,9 +4848,13 @@ public class ConnectivityManager { } } - private void setOemNetworkPreference(@NonNull OemNetworkPreferences preference) { - Log.d(TAG, "setOemNetworkPreference called with preference: " - + preference.toString()); + private void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) { + try { + mService.setOemNetworkPreference(preference); + } catch (RemoteException e) { + Log.e(TAG, "setOemNetworkPreference() failed for preference: " + preference.toString()); + throw e.rethrowFromSystemServer(); + } } @NonNull diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index 1b4d2e413943..f909d1362550 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -29,6 +29,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.NetworkState; +import android.net.OemNetworkPreferences; import android.net.ProxyInfo; import android.net.UidRange; import android.net.QosSocketInfo; @@ -65,7 +66,7 @@ interface IConnectivityManager Network getNetworkForType(int networkType); Network[] getAllNetworks(); NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser( - int userId, String callingPackageName); + int userId, String callingPackageName, String callingAttributionTag); boolean isNetworkSupported(int networkType); @@ -74,7 +75,8 @@ interface IConnectivityManager LinkProperties getLinkPropertiesForType(int networkType); LinkProperties getLinkProperties(in Network network); - NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName); + NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName, + String callingAttributionTag); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) NetworkState[] getAllNetworkState(); @@ -156,9 +158,6 @@ interface IConnectivityManager boolean requestBandwidthUpdate(in Network network); - int registerNetworkFactory(in Messenger messenger, in String name); - void unregisterNetworkFactory(in Messenger messenger); - int registerNetworkProvider(in Messenger messenger, in String name); void unregisterNetworkProvider(in Messenger messenger); @@ -178,10 +177,12 @@ interface IConnectivityManager void releasePendingNetworkRequest(in PendingIntent operation); NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities, - in Messenger messenger, in IBinder binder, String callingPackageName); + in Messenger messenger, in IBinder binder, String callingPackageName, + String callingAttributionTag); void pendingListenForNetwork(in NetworkCapabilities networkCapabilities, - in PendingIntent operation, String callingPackageName); + in PendingIntent operation, String callingPackageName, + String callingAttributionTag); void releaseNetworkRequest(in NetworkRequest networkRequest); @@ -243,4 +244,6 @@ interface IConnectivityManager void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback); void unregisterQosCallback(in IQosCallback callback); + + void setOemNetworkPreference(in OemNetworkPreferences preference); } diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index c4d1b09a5c20..6540397d6200 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -16,6 +16,22 @@ package android.net; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -30,6 +46,8 @@ import android.os.Process; import android.text.TextUtils; import android.util.proto.ProtoOutputStream; +import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.Set; @@ -156,8 +174,30 @@ public class NetworkRequest implements Parcelable { * needed in terms of {@link NetworkCapabilities} features */ public static class Builder { + /** + * Capabilities that are currently compatible with VCN networks. + */ + private static final List<Integer> VCN_SUPPORTED_CAPABILITIES = Arrays.asList( + NET_CAPABILITY_CAPTIVE_PORTAL, + NET_CAPABILITY_DUN, + NET_CAPABILITY_FOREGROUND, + NET_CAPABILITY_INTERNET, + NET_CAPABILITY_NOT_CONGESTED, + NET_CAPABILITY_NOT_METERED, + NET_CAPABILITY_NOT_RESTRICTED, + NET_CAPABILITY_NOT_ROAMING, + NET_CAPABILITY_NOT_SUSPENDED, + NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_PARTIAL_CONNECTIVITY, + NET_CAPABILITY_TEMPORARILY_NOT_METERED, + NET_CAPABILITY_TRUSTED, + NET_CAPABILITY_VALIDATED); + private final NetworkCapabilities mNetworkCapabilities; + // A boolean that represents the user modified NOT_VCN_MANAGED capability. + private boolean mModifiedNotVcnManaged = false; + /** * Default constructor for Builder. */ @@ -179,6 +219,7 @@ public class NetworkRequest implements Parcelable { // maybeMarkCapabilitiesRestricted() doesn't add back. final NetworkCapabilities nc = new NetworkCapabilities(mNetworkCapabilities); nc.maybeMarkCapabilitiesRestricted(); + deduceNotVcnManagedCapability(nc); return new NetworkRequest(nc, ConnectivityManager.TYPE_NONE, ConnectivityManager.REQUEST_ID_UNSET, Type.NONE); } @@ -195,6 +236,9 @@ public class NetworkRequest implements Parcelable { */ public Builder addCapability(@NetworkCapabilities.NetCapability int capability) { mNetworkCapabilities.addCapability(capability); + if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) { + mModifiedNotVcnManaged = true; + } return this; } @@ -206,6 +250,9 @@ public class NetworkRequest implements Parcelable { */ public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) { mNetworkCapabilities.removeCapability(capability); + if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) { + mModifiedNotVcnManaged = true; + } return this; } @@ -263,6 +310,9 @@ public class NetworkRequest implements Parcelable { @NonNull public Builder clearCapabilities() { mNetworkCapabilities.clearAll(); + // If the caller explicitly clear all capabilities, the NOT_VCN_MANAGED capabilities + // should not be add back later. + mModifiedNotVcnManaged = true; return this; } @@ -382,6 +432,25 @@ public class NetworkRequest implements Parcelable { mNetworkCapabilities.setSignalStrength(signalStrength); return this; } + + /** + * Deduce the NET_CAPABILITY_NOT_VCN_MANAGED capability from other capabilities + * and user intention, which includes: + * 1. For the requests that don't have anything besides + * {@link #VCN_SUPPORTED_CAPABILITIES}, add the NET_CAPABILITY_NOT_VCN_MANAGED to + * allow the callers automatically utilize VCN networks if available. + * 2. For the requests that explicitly add or remove NET_CAPABILITY_NOT_VCN_MANAGED, + * do not alter them to allow user fire request that suits their need. + * + * @hide + */ + private void deduceNotVcnManagedCapability(final NetworkCapabilities nc) { + if (mModifiedNotVcnManaged) return; + for (final int cap : nc.getCapabilities()) { + if (!VCN_SUPPORTED_CAPABILITIES.contains(cap)) return; + } + nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + } } // implement the Parcelable interface @@ -435,25 +504,7 @@ public class NetworkRequest implements Parcelable { * @hide */ public boolean isRequest() { - return isForegroundRequest() || isBackgroundRequest(); - } - - /** - * Returns true iff. the contained NetworkRequest is one that: - * - * - should be associated with at most one satisfying network - * at a time; - * - * - should cause a network to be kept up and in the foreground if - * it is the best network which can satisfy the NetworkRequest. - * - * For full detail of how isRequest() is used for pairing Networks with - * NetworkRequests read rematchNetworkAndRequests(). - * - * @hide - */ - public boolean isForegroundRequest() { - return type == Type.TRACK_DEFAULT || type == Type.REQUEST; + return type == Type.REQUEST || type == Type.BACKGROUND_REQUEST; } /** diff --git a/packages/Connectivity/framework/src/android/net/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java index 03cfbbb4a22d..77c8a4f4579b 100644 --- a/packages/Connectivity/framework/src/android/net/Proxy.java +++ b/packages/Connectivity/framework/src/android/net/Proxy.java @@ -32,8 +32,6 @@ import java.net.InetSocketAddress; import java.net.ProxySelector; import java.net.URI; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * A convenience class for accessing the user and default proxy @@ -66,40 +64,9 @@ public final class Proxy { @Deprecated public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO"; - /** @hide */ - public static final int PROXY_VALID = 0; - /** @hide */ - public static final int PROXY_HOSTNAME_EMPTY = 1; - /** @hide */ - public static final int PROXY_HOSTNAME_INVALID = 2; - /** @hide */ - public static final int PROXY_PORT_EMPTY = 3; - /** @hide */ - public static final int PROXY_PORT_INVALID = 4; - /** @hide */ - public static final int PROXY_EXCLLIST_INVALID = 5; - private static ConnectivityManager sConnectivityManager = null; - // Hostname / IP REGEX validation - // Matches blank input, ips, and domain names - private static final String NAME_IP_REGEX = - "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*"; - - private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$"; - - private static final Pattern HOSTNAME_PATTERN; - - private static final String EXCL_REGEX = - "[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*(\\.[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*)*"; - - private static final String EXCLLIST_REGEXP = "^$|^" + EXCL_REGEX + "(," + EXCL_REGEX + ")*$"; - - private static final Pattern EXCLLIST_PATTERN; - static { - HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP); - EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP); sDefaultProxySelector = ProxySelector.getDefault(); } @@ -218,33 +185,6 @@ public final class Proxy { return false; } - /** - * Validate syntax of hostname, port and exclusion list entries - * {@hide} - */ - public static int validate(String hostname, String port, String exclList) { - Matcher match = HOSTNAME_PATTERN.matcher(hostname); - Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList); - - if (!match.matches()) return PROXY_HOSTNAME_INVALID; - - if (!listMatch.matches()) return PROXY_EXCLLIST_INVALID; - - if (hostname.length() > 0 && port.length() == 0) return PROXY_PORT_EMPTY; - - if (port.length() > 0) { - if (hostname.length() == 0) return PROXY_HOSTNAME_EMPTY; - int portVal = -1; - try { - portVal = Integer.parseInt(port); - } catch (NumberFormatException ex) { - return PROXY_PORT_INVALID; - } - if (portVal <= 0 || portVal > 0xFFFF) return PROXY_PORT_INVALID; - } - return PROXY_VALID; - } - /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Deprecated diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java index 9c9fed102828..229db0d717cd 100644 --- a/packages/Connectivity/framework/src/android/net/ProxyInfo.java +++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java @@ -23,6 +23,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import com.android.net.module.util.ProxyUtils; + import java.net.InetSocketAddress; import java.net.URLConnection; import java.util.List; @@ -233,7 +235,7 @@ public class ProxyInfo implements Parcelable { */ public boolean isValid() { if (!Uri.EMPTY.equals(mPacFileUrl)) return true; - return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost, + return ProxyUtils.PROXY_VALID == ProxyUtils.validate(mHost == null ? "" : mHost, mPort == 0 ? "" : Integer.toString(mPort), mExclusionList == null ? "" : mExclusionList); } diff --git a/packages/Connectivity/framework/src/android/net/VpnManager.java b/packages/Connectivity/framework/src/android/net/VpnManager.java index c87b8279c4d6..1812509ba6d2 100644 --- a/packages/Connectivity/framework/src/android/net/VpnManager.java +++ b/packages/Connectivity/framework/src/android/net/VpnManager.java @@ -21,6 +21,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.Activity; import android.content.ComponentName; import android.content.Context; @@ -28,6 +29,8 @@ import android.content.Intent; import android.content.res.Resources; import android.os.RemoteException; +import com.android.internal.net.LegacyVpnInfo; +import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; import java.io.IOException; @@ -161,4 +164,104 @@ public class VpnManager { throw e.rethrowFromSystemServer(); } } -} + + /** + * Return the VPN configuration for the given user ID. + * @hide + */ + @Nullable + public VpnConfig getVpnConfig(@UserIdInt int userId) { + try { + return mService.getVpnConfig(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Prepare for a VPN application. + * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId}, + * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required. + * + * @param oldPackage Package name of the application which currently controls VPN, which will + * be replaced. If there is no such application, this should should either be + * {@code null} or {@link VpnConfig.LEGACY_VPN}. + * @param newPackage Package name of the application which should gain control of VPN, or + * {@code null} to disable. + * @param userId User for whom to prepare the new VPN. + * + * @hide + */ + public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage, + int userId) { + try { + return mService.prepareVpn(oldPackage, newPackage, userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set whether the VPN package has the ability to launch VPNs without user intervention. This + * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn} + * class. If the caller is not {@code userId}, {@link + * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required. + * + * @param packageName The package for which authorization state should change. + * @param userId User for whom {@code packageName} is installed. + * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN + * permissions should be granted. When unauthorizing an app, {@link + * VpnManager.TYPE_VPN_NONE} should be used. + * @hide + */ + public void setVpnPackageAuthorization( + String packageName, int userId, @VpnManager.VpnType int vpnType) { + try { + mService.setVpnPackageAuthorization(packageName, userId, vpnType); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Return the legacy VPN information for the specified user ID. + * @hide + */ + public LegacyVpnInfo getLegacyVpnInfo(@UserIdInt int userId) { + try { + return mService.getLegacyVpnInfo(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Starts a legacy VPN. + * @hide + */ + public void startLegacyVpn(VpnProfile profile) { + try { + mService.startLegacyVpn(profile); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Informs the service that legacy lockdown VPN state should be updated (e.g., if its keystore + * entry has been updated). If the LockdownVpn mechanism is enabled, updates the vpn + * with a reload of its profile. + * + * <p>This method can only be called by the system UID + * @return a boolean indicating success + * + * @hide + */ + public boolean updateLockdownVpn() { + try { + return mService.updateLockdownVpn(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 3837743a0ff8..889980a05bfa 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sal nie outomaties koppel nie"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang nie"</string> <string name="saved_network" msgid="7143698034077223645">"Gestoor deur <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Gekoppel aan beperkte netwerk"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Outomaties deur %1$s gekoppel"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Outomaties deur netwerkgraderingverskaffer gekoppel"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Gekoppel via %1$s"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 4ce01d68a5ae..c41e4d5d5869 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"በራስ-ሰር አይገናኝም"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ምንም የበይነመረብ መዳረሻ ያለም"</string> <string name="saved_network" msgid="7143698034077223645">"የተቀመጠው በ<xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ከሚለካ አውታረ መረብ ጋር ተገናኝቷል"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"በ%1$s በኩል በራስ-ሰር ተገናኝቷል"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"በአውታረ መረብ ደረጃ ሰጪ አቅራቢ በኩል በራስ-ሰር ተገናኝቷል"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"በ%1$s በኩል መገናኘት"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 2580d0fc82cc..f8d1d576c759 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"لن يتم الاتصال تلقائيًا"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"لا يتوفّر اتصال بالإنترنت"</string> <string name="saved_network" msgid="7143698034077223645">"تم الحفظ بواسطة <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"تم الاتصال بشبكة تفرض تكلفة استخدام."</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"تم الاتصال تلقائيًا عبر %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"تم الاتصال تلقائيًا عبر مقدم خدمة تقييم الشبكة"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"تم الاتصال عبر %1$s"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index b61ff508cfe7..c6078f898c3e 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"স্বয়ংক্ৰিয়ভাৱে সংযোগ নহ’ব"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ইণ্টাৰনেট সংযোগ নাই"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>এ ছেভ কৰিছে"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"নিৰিখ-নিৰ্দিষ্ট নেটৱৰ্কৰ সৈতে সংযোগ কৰা হৈছে"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s মাধ্যমেদি স্বয়ংক্ৰিয়ভাৱে সংযোগ কৰা হৈছে"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটৱৰ্ক ৰেটিং প্ৰদানকাৰীৰ জৰিয়তে স্বয়ং সংয়োগ কৰা হ’ল"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ৰ মাধ্যমেদি সংযোগ কৰা হৈছে"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index d06377677a50..d3e0a25593e4 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik qoşulmayacaq"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"İnternet girişi yoxdur"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tərəfindən saxlandı"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ölçülən şəbəkəyə qoşulub"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzərindən avtomatik qoşuldu"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Avtomatik olaraq şəbəkə reytinq provayderi ilə qoşuludur"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s vasitəsilə qoşuludur"</string> @@ -313,7 +312,7 @@ <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Adsız Bluetooth cihazları (yalnız MAC ünvanları) göstəriləcək"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Uzaqdan idarə olunan cihazlarda dözülməz yüksək səs həcmi və ya nəzarət çatışmazlığı kimi səs problemləri olduqda Bluetooth mütləq səs həcmi xüsusiyyətini deaktiv edir."</string> <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche funksiyasını aktiv edir."</string> - <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Təkmilləşdirilmiş Bağlantı funksiyasını aktiv edir."</string> + <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Qabaqcıl məlumat mübadiləsini aktiv edir."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Yerli terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Yerli örtük girişini təklif edən terminal tətbiqi aktiv edin"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP yoxlanılır"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 2976bb5893b6..85412224e995 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automatsko povezivanje nije uspelo"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Sačuvao/la je <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezani ste na mrežu sa ograničenjem"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano preko %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano preko dobavljača ocene mreže"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Veza je uspostavljena preko pristupne tačke %1$s"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 7af9e9323e05..aab600f3330e 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не будзе аўтаматычна падключацца"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Няма доступу да інтэрнэту"</string> <string name="saved_network" msgid="7143698034077223645">"Захавана праз: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Падключана да сеткі з падлікам трафіка"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Аўтаматычна падключана праз %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аўтаматычна падключана праз пастаўшчыка паслугі ацэнкі сеткі"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Падключана праз %1$s"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 77b6493931f7..92f293599f97 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Няма да се свърже автоматично"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Няма достъп до интернет"</string> <string name="saved_network" msgid="7143698034077223645">"Запазено от <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Установена е връзка с мрежа с отчитане"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично е установена връзка чрез %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично е установена връзка чрез доставчик на услуги за оценяване на мрежите"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Установена е връзка през „%1$s“"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 819625b67725..b40e24e3a672 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"অটোমেটিক কানেক্ট করবে না"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ইন্টারনেট অ্যাক্সেস নেই"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> দ্বারা সেভ করা"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"মিটার্ড নেটওয়ার্কের সঙ্গে কানেক্ট করা"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"স্বয়ংক্রিয়ভাবে %1$s এর মাধ্যমে কানেক্ট হয়েছে"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটওয়ার্কের রেটিং প্রদানকারীর মাধ্যমে অটোমেটিক কানেক্ট"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s মাধ্যমে কানেক্ট হয়েছে"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 193ac6066e40..cec2e4533878 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se automatski povezati"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Sačuvano: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezani ste s mrežom s naplatom"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano koristeći %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano putem ocjenjivača mreže"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Povezani preko %1$s"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index fef9bfb5c8c2..6134da873a0e 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No es connectarà automàticament"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No hi ha accés a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Desada per <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connectat a una xarxa d\'ús mesurat"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Connectada automàticament a través de: %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connectada automàticament a través d\'un proveïdor de valoració de xarxes"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connectada mitjançant %1$s"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 281a78850e00..f29a3dd54ff8 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Připojení nebude automaticky navázáno"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nebyl zjištěn žádný přístup k internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Uloženo uživatelem <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Připojeno k měřené síti"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky připojeno přes poskytovatele %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky připojeno přes poskytovatele hodnocení sítí"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Připojeno prostřednictvím %1$s"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 69cc8d805091..d92d41d536bc 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Der oprettes ikke automatisk forbindelse"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetadgang"</string> <string name="saved_network" msgid="7143698034077223645">"Gemt af <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Der er oprettet forbindelse til det forbrugsbaserede netværk"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilsluttet via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk forbundet via udbyder af netværksvurdering"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Tilsluttet via %1$s"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 737ea167a8c9..ac05b620539c 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kein automatischer Verbindungsaufbau"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Kein Internetzugriff"</string> <string name="saved_network" msgid="7143698034077223645">"Gespeichert von <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Mit kostenpflichtigem Netzwerk verbunden"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch über %1$s verbunden"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch über Anbieter von Netzwerkbewertungen verbunden"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Über %1$s verbunden"</string> @@ -287,7 +286,7 @@ <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Verringert den Akkuverbrauch und verbessert die Netzwerkleistung"</string> <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Wenn dieser Modus aktiviert ist, kann sich die MAC-Adresse dieses Geräts bei jeder Verbindung mit einem Netzwerk ändern, bei dem die MAC-Adressen randomisiert werden."</string> <string name="wifi_metered_label" msgid="8737187690304098638">"Kostenpflichtig"</string> - <string name="wifi_unmetered_label" msgid="6174142840934095093">"Kostenlos"</string> + <string name="wifi_unmetered_label" msgid="6174142840934095093">"Ohne Datenlimit"</string> <string name="select_logd_size_title" msgid="1604578195914595173">"Logger-Puffergrößen"</string> <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Größe pro Protokollpuffer wählen"</string> <string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"Speicher der dauerhaften Protokollierung löschen?"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 8e1d5e3d1b18..0b11d69132c5 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Δεν θα συνδεθεί αυτόματα"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Δεν υπάρχει πρόσβαση στο διαδίκτυο"</string> <string name="saved_network" msgid="7143698034077223645">"Αποθηκεύτηκε από <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Σύνδεση σε δίκτυο με ογκοχρέωση"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Συνδέθηκε αυτόματα μέσω %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Συνδέθηκε αυτόματα μέσω παρόχου αξιολόγησης δικτύου"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Συνδέθηκε μέσω %1$s"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index f79072fff1e9..1da8e37186c6 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se conectará automáticamente"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No hay acceso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a la red de uso medido"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conexión automática mediante %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente mediante proveedor de calificación de red"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conexión a través de %1$s"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index f99a3f12307b..54226ce0fe5c 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se establecerá conexión automáticamente"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No se ha detectado ningún acceso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a una red de uso medido"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectada automáticamente a través de %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente a través de un proveedor de valoración de redes"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index fa2aa467d0c5..4c747e36ef52 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automaatselt ei ühendata"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Juurdepääs Internetile puudub"</string> <string name="saved_network" msgid="7143698034077223645">"Salvestas: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ühendatud mahupõhise võrguga"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ühendus loodi automaatselt teenusega %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ühendus loodi automaatselt võrgukvaliteedi hinnangute pakkuja kaudu"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Ühendatud üksuse %1$s kaudu"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 2c4a8ee09f5e..6016cd20ff37 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ez da konektatuko automatikoki"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ezin da konektatu Internetera"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioak gorde du"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Sare neurtu batera konektatuta"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s bidez automatikoki konektatuta"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikoki konektatuta sare-balorazioen hornitzailearen bidez"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s bidez konektatuta"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 1c6ca763197a..e85557000a61 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -36,10 +36,9 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"اتصال بهصورت خودکار انجام نمیشود"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"دسترسی به اینترنت ندارد"</string> <string name="saved_network" msgid="7143698034077223645">"ذخیرهشده توسط <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"اتصال به شبکه محدود برقرار شد"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"اتصال خودکار ازطریق %1$s"</string> - <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائهدهنده رتبهبندی شبکه"</string> + <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائهدهنده ردهبندی شبکه"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"متصل از طریق %1$s"</string> <string name="connected_via_app" msgid="3532267661404276584">"متصل شده ازطریق <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="available_via_passpoint" msgid="1716000261192603682">"در دسترس از طریق %1$s"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 04c9130ec31f..724e52a95927 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Yhteyttä ei muodosteta automaattisesti"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ei internetyhteyttä"</string> <string name="saved_network" msgid="7143698034077223645">"Tallentaja: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Yhdistetty maksulliseen verkkoon"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaattinen yhteys muodostettu palvelun %1$s kautta"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Yhdistetty automaattisesti verkon arviointipalvelun kautta"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Yhdistetty seuraavan kautta: %1$s"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index aa0cd2a684c7..74c3005b0228 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Enregistrés par <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Appareil connecté à un réseau mesuré"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiquement connecté par %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement par le fournisseur d\'avis sur le réseau"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté par %1$s"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index dbdc160030fa..c9651ce570c5 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Enregistré lors de : <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connecté au réseau facturé à l\'usage"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Connecté automatiquement via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement via un fournisseur d\'évaluation de l\'état du réseau"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté via %1$s"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 90c130301a8f..f499f587a850 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non se conectará automaticamente"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Sen acceso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Gardada por <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Estableceuse conexión coa rede de pago por consumo"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectouse automaticamente a través de %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de redes"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 4caeda279849..23ee1de46484 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ઑટોમૅટિક રીતે કનેક્ટ કરશે નહીં"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"કોઈ ઇન્ટરનેટ ઍક્સેસ નથી"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા સચવાયું"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"મીટર્ડ (ડેટા નિયંત્રણ) નેટવર્ક સાથે કનેક્ટેડ છે"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s દ્વારા સ્વત: કનેક્ટ થયેલ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"નેટવર્ક રેટિંગ પ્રદાતા દ્વારા ઑટોમૅટિક રીતે કનેક્ટ થયું"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s દ્વારા કનેક્ટ થયેલ"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 468808be918a..a2fc43c250cc 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"अपने आप कनेक्ट नहीं होगा"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट नहीं है"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> के द्वारा सहेजा गया"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"सीमित डेटा वाले नेटवर्क से कनेक्ट किया गया"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s के ज़रिए ऑटोमैटिक रूप से कनेक्ट है"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग कंपनी के ज़रिए अपने आप कनेक्ट है"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s के द्वारा उपलब्ध"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 932c2560f2d9..396051d51a09 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se povezati automatski"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Spremila aplik. <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezano s mrežom s ograničenim prometom"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezan putem %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezan putem ocjenjivača mreže"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Povezano putem %1$s"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index fbaffac6b083..0c9bc54c0861 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nem csatlakozik automatikusan"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nincs internet-hozzáférés"</string> <string name="saved_network" msgid="7143698034077223645">"Mentette: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Forgalomkorlátos hálózathoz csatlakozva"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatikusan csatlakozott a következőn keresztül: %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikusan csatlakozott a hálózatértékelés szolgáltatóján keresztül"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Csatlakozva a következőn keresztül: %1$s"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 224d6418b5d5..56c93b635e20 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Չի միանա ավտոմատ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ինտերնետ կապ չկա"</string> <string name="saved_network" msgid="7143698034077223645">"Ով է պահել՝ <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Միացած է վճարովի թրաֆիկով ցանցի"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ավտոմատ կերպով կապակցվել է %1$s-ի միջոցով"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ավտոմատ միացել է ցանցերի վարկանիշի մատակարարի միջոցով"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Միացված է %1$s-ի միջոցով"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 8cf13cde6a42..0440f4796143 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan tersambung otomatis"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Tidak ada akses internet"</string> <string name="saved_network" msgid="7143698034077223645">"Disimpan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Terhubung ke jaringan berbayar"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Tersambung otomatis melalui %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Otomatis tersambung melalui penyedia rating jaringan"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Terhubung melalui %1$s"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index aa8893e4783a..0bb294f5869b 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Mun ekki tengjast sjálfkrafa"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Enginn netaðgangur"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> vistaði"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Tengdist neti með mældri notkun"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Sjálfkrafa tengt um %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Sjálfkrafa tengt um netgæðaveitu"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Tengt í gegnum %1$s"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 0199f549d099..54049d79a76b 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non verrà eseguita la connessione automatica"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nessun accesso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Salvata da <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connessione a rete a consumo effettuata"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Collegato automaticamente tramite %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Collegato automaticamente tramite fornitore di servizi di valutazione rete"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Collegato tramite %1$s"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index a50a22d2d08d..6cc4347946f4 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"לא יתבצע חיבור באופן אוטומטי"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"אין גישה לאינטרנט"</string> <string name="saved_network" msgid="7143698034077223645">"נשמר על ידי <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"יש חיבור לרשת המבוססת על חיוב לפי שימוש בנתונים"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"מחובר אוטומטית דרך %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"מחובר אוטומטית דרך ספק של דירוג רשת"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"מחובר דרך %1$s"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index a1d1b70d607e..ec674adb75d0 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"自動的に接続されません"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"インターネット接続なし"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>で保存"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"従量制ネットワークに接続しました"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s 経由で自動的に接続しています"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ネットワーク評価プロバイダ経由で自動的に接続しています"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s経由で接続"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 77fd4b156024..30ab3b49afdd 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ავტომატურად დაკავშირება ვერ მოხერხდება"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ინტერნეტ-კავშირი არ არის"</string> <string name="saved_network" msgid="7143698034077223645">"შენახული <xliff:g id="NAME">%1$s</xliff:g>-ის მიერ"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"დაკავშირებულია ფასიან ქსელთან"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"ავტომატურად დაკავშირდა %1$s-ის მეშვეობით"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ავტომატურად დაკავშირდა ქსელის ხარისხის შეფასების პროვაიდერის მეშვეობით"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ით დაკავშირებული"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index eb5cd54e4f19..ef7852710af4 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматты қосылмайды"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Интернетпен байланыс жоқ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> сақтаған"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Трафик саналатын желіге қосылды."</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s арқылы автоматты қосылды"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Желі рейтингі провайдері арқылы автоматты түрде қосылған"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s арқылы қосылған"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 38abb805cb10..ed59c747f8fd 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"មិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string> <string name="saved_network" msgid="7143698034077223645">"បានរក្សាទុកដោយ <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"បានភ្ជាប់ទៅបណ្ដាញដែលផ្អែកតាមទិន្នន័យដែលប្រើ"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"បានភ្ជាប់ដោយស្វ័យប្រវត្តិតាមរយៈ %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"បានភ្ជាប់ដោយស្វ័យប្រវត្តិតាមរយៈក្រុមហ៊ុនផ្តល់ការវាយតម្លៃលើបណ្តាញ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"បានភ្ជាប់តាមរយៈ %1$s"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 560fba15bbad..995dc8632e5c 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವಿಲ್ಲ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ನಿಂದ ಉಳಿಸಲಾಗಿದೆ"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ಮಾಪನ ಮಾಡಲಾದ ನೆಟ್ವರ್ಕ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ನೆಟ್ವರ್ಕ್ ರೇಟಿಂಗ್ ಒದಗಿಸುವವರ ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index f43ce16368cb..7863d481295e 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"자동으로 연결되지 않습니다."</string> <string name="wifi_no_internet" msgid="1774198889176926299">"인터넷에 연결되어 있지 않음"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>(으)로 저장됨"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"데이터 전송량 제한이 있는 네트워크에 연결됨"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s을(를) 통해 자동으로 연결됨"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"네트워크 평가 제공업체를 통해 자동으로 연결됨"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s을(를) 통해 연결됨"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 35b1ecacd505..45d05c6372ce 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматтык түрдө туташпайт"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Интернетке туташпай турат"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> тарабынан сакталды"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Чектелген трафикке туташтырылды"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s аркылуу автоматтык түрдө туташты"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Тармактар рейтингинин булагы аркылуу автоматтык түрдө туташты"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s аркылуу жеткиликтүү"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index f60fe7f8affc..ca6e06c13892 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string> <string name="saved_network" msgid="7143698034077223645">"ບັນທຶກໂດຍ <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍທີ່ມີການວັດແທກແລ້ວ"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"ເຊື່ອມຕໍ່ຜ່ານທາງ %1$s ໂດຍອັດຕະໂນມັດ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ເຊື່ອມຕໍ່ກັບອັດຕະໂນມັດແລ້ວຜ່ານຜູ້ໃຫ້ບໍລິການຄະແນນເຄືອຂ່າຍ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"ເຊື່ອມຕໍ່ຜ່ານ %1$s ແລ້ວ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index e66e3c5a9944..173b57eaba57 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nebus automatiškai prisijungiama"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nėra interneto ryšio"</string> <string name="saved_network" msgid="7143698034077223645">"Išsaugojo <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Prisijungta prie matuojamo tinklo"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiškai prisijungta naudojant „%1$s“"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiškai prisijungta naudojant tinklo įvertinimo paslaugos teikėjo paslaugomis"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Prisijungta naudojant „%1$s“"</string> @@ -208,7 +207,7 @@ <string name="enable_adb" msgid="8072776357237289039">"USB perkrova"</string> <string name="enable_adb_summary" msgid="3711526030096574316">"Derinimo režimas, kai prijungtas USB"</string> <string name="clear_adb_keys" msgid="3010148733140369917">"Panaikinti USB derinimo prieigos teises"</string> - <string name="enable_adb_wireless" msgid="6973226350963971018">"Belaidžio ryšio derinimas"</string> + <string name="enable_adb_wireless" msgid="6973226350963971018">"Belaidžio ryšio derin."</string> <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Derinimo režimas, kai prisijungta prie „Wi‑Fi“"</string> <string name="adb_wireless_error" msgid="721958772149779856">"Klaida"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"Belaidžio ryšio derinimas"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 90bcc04d1212..0ef3f0eee6b5 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не може да се поврзе автоматски"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Нема пристап до интернет"</string> <string name="saved_network" msgid="7143698034077223645">"Зачувано од <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Поврзано на мрежа со ограничен интернет"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматски поврзано преку %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматски поврзано преку оценувач на мрежа"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Поврзано преку %1$s"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index ba1987bc1d88..e6289ae3591e 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"സ്വയമേവ കണക്റ്റുചെയ്യില്ല"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> സംരക്ഷിച്ചത്"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"മീറ്റർ ചെയ്ത നെറ്റ്വർക്കിലേക്ക് കണക്റ്റ് ചെയ്തു"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s വഴി സ്വയമേവ ബന്ധിപ്പിച്ചു"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"നെറ്റ്വർക്ക് റേറ്റിംഗ് ദാതാവുമായി സ്വയം കണക്റ്റുചെയ്തു"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s വഴി ബന്ധിപ്പിച്ചു"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 45a831daee14..ead712c30b89 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматаар холбогдохгүй"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Интернет хандалт алга"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> хадгалсан"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Хязгаартай сүлжээнд холбогдсон"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s-р автоматаар холбогдсон"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Сүлжээний үнэлгээ үзүүлэгчээр автоматаар холбогдох"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-р холбогдсон"</string> @@ -398,8 +397,8 @@ <item msgid="1282170165150762976">"Дижитал агуулгад зориулан тааруулсан өнгө"</item> </string-array> <string name="inactive_apps_title" msgid="5372523625297212320">"Зогсолтын горимын апп"</string> - <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Идэвхгүй байна. Унтраах/асаахын тулд дарна уу."</string> - <string name="inactive_app_active_summary" msgid="8047630990208722344">"Идэвхтэй байна. Унтраах/асаахын тулд дарна уу."</string> + <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Идэвхгүй байна. Асаах/унтраахын тулд дарна уу."</string> + <string name="inactive_app_active_summary" msgid="8047630990208722344">"Идэвхтэй байна. Асаах/унтраахын тулд дарна уу."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Апп зогсолтын горимын төлөв:<xliff:g id="BUCKET"> %s</xliff:g>"</string> <string name="transcode_settings_title" msgid="2581975870429850549">"Медиа хөрвүүлгийн тохиргоо"</string> <string name="transcode_user_control" msgid="6176368544817731314">"Хөрвүүлгийн өгөгдмөлийг дарах"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 1e69b28fa4de..edcc71b17e8a 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वयंचलितपणे कनेक्ट करणार नाही"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट अॅक्सेस नाही"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे सेव्ह केले"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"मर्यादित नेटवर्कशी कनेक्ट केले आहे"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s द्वारे स्वयंचलितपणे कनेक्ट केले"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग प्रदात्याद्वारे स्वयंचलितपणे कनेक्ट केले"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s द्वारे कनेक्ट केले"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 71c9eeafc594..8a14437e3647 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan menyambung secara automatik"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Tiada akses Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Diselamatkan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Disambungkan kepada rangkaian bermeter"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Disambungkan secara automatik melalui %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Disambungkan secara automatik melalui pembekal penilaian rangkaian"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Disambungkan melalui %1$s"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 3b3983e5164e..377c8b241144 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"အလိုအလျောက်ချိတ်ဆက်မည်မဟုတ်ပါ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"အင်တာနက် ချိတ်ဆက်မှု မရှိပါ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> က သိမ်းဆည်းခဲ့သည်"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"အခမဲ့မဟုတ်သော ကွန်ရက်သို့ ချိတ်ဆက်ထားသည်"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ကွန်ရက်အဆင့်သတ်မှတ်ပေးသူ မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index a8c01b3db73c..f0e773b1833d 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kobler ikke til automatisk"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internettilgang"</string> <string name="saved_network" msgid="7143698034077223645">"Lagret av <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Koble til et nettverk med datamåling"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilkoblet via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk tilkoblet via leverandør av nettverksvurdering"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Tilkoblet via %1$s"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index c28563764a2d..0bde70fe209d 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वतः जडान हुने छैन"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"इन्टरनेटमाथिको पहुँच छैन"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा सुरक्षित गरियो"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"प्रयोगसम्बन्धी सीमा तोकिएको नेटवर्कमा कनेक्ट गरियो"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s मार्फत् स्वतः जडान गरिएको"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्कको दर्जा प्रदायक मार्फत स्वत: जडान गरिएको"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s मार्फत जडित"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 9f4ea68f4c64..2bc1e8c0f7f0 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Er wordt niet automatisch verbinding gemaakt"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang"</string> <string name="saved_network" msgid="7143698034077223645">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Verbonden met netwerk met datalimiet"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch verbonden via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via provider van netwerkbeoordelingen"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Verbonden via %1$s"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 31bd7af804e0..787c87182cd7 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ସ୍ୱଚାଳିତ ଭାବେ ସଂଯୁକ୍ତ ହେବନାହିଁ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ଇଣ୍ଟରନେଟ୍ର କୌଣସି ଆକ୍ସେସ୍ ନାହିଁ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ୱାରା ସେଭ କରାଯାଇଛି"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ମିଟରଯୁକ୍ତ ନେଟୱାର୍କ ସହ ସଂଯୋଗ କରାଯାଇଛି"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲୀ ସଂଯୁକ୍ତ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ନେଟୱର୍କ ମୂଲ୍ୟାୟନ ପ୍ରଦାତାଙ୍କ ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲ୍ୟ ସଂଯୁକ୍ତ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ମାଧ୍ୟମରେ ସଂଯୁକ୍ତ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index f21c4cedfb71..9483e060081c 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ਕੋਈ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ਵੱਲੋਂ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ਮੀਟਰਬੱਧ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਹੈ"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ਰਾਹੀਂ ਆਪਣੇ-ਆਪ ਕਨੈਕਟ ਹੋਇਆ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ਨੈੱਟਵਰਕ ਰੇਟਿੰਗ ਪ੍ਰਦਾਨਕ ਰਾਹੀਂ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਹੋਇਆ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index c9c4a6c31f49..c7f1595c86d0 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nie można połączyć automatycznie"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Brak dostępu do internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Zapisane przez: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Połączono z siecią z pomiarem użycia danych"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatycznie połączono przez: %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatycznie połączono przez dostawcę ocen jakości sieci"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Połączono przez %1$s"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 4b710853f329..63d8b8f53f7f 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nu se va conecta automat"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nu există acces la internet"</string> <string name="saved_network" msgid="7143698034077223645">"Salvată de <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"S-a conectat la o rețea contorizată"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectată automat prin %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectată automat prin furnizor de evaluări ale rețelei"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectată prin %1$s"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 4aeb9855ff2b..ac43e493d73b 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Подключение не будет выполняться автоматически"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Без доступа к Интернету"</string> <string name="saved_network" msgid="7143698034077223645">"Кто сохранил: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Подключено к сети с ограниченным трафиком"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматически подключено к %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматически подключено через автора рейтинга сетей"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Подключено к %1$s"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 525d4231eb4c..08fd9a03d7c4 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ස්වයංක්රිය නැවත සම්බන්ධ නොවනු ඇත"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"අන්තර්ජාල ප්රවේශය නැත"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> විසින් සුරකින ලදී"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"මනුගත ජාලයට සම්බන්ධ කර ඇත"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s හරහා ස්වයංක්රියව සම්බන්ධ විය"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ජාල ශ්රේණිගත සපයන්නා හරහා ස්වයංක්රියව සම්බන්ධ විය"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s හරහා සම්බන්ධ විය"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index ee9ae6a47d2f..8f05684e6de9 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nedôjde k automatickému pripojeniu"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Žiadny prístup k internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Uložila aplikácia <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Pripojené k meranej sieti"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky pripojené prostredníctvom %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky pripojené prostredníctvom poskytovateľa hodnotenia siete"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Pripojené prostredníctvom %1$s"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index ff60a322dc1d..3dccf3b53f9c 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Samodejna vnovična vzpostavitev povezave se ne bo izvedla"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ni dostopa do interneta"</string> <string name="saved_network" msgid="7143698034077223645">"Shranil(-a): <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezano z omrežjem z omejenim prenosom podatkov"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Samodejno vzpostavljena povezava prek: %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Samodejno vzpostavljena povezava prek ponudnika ocenjevanja omrežij"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Vzpostavljena povezava prek: %1$s"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 78e6ed60c176..c09ad4c42fa4 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nuk do të lidhet automatikisht"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nuk ka qasje në internet"</string> <string name="saved_network" msgid="7143698034077223645">"E ruajtur nga <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Lidhur me një rrjet me matje"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Lidhur automatikisht përmes %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Lidhur automatikisht nëpërmjet ofruesit të vlerësimit të rrjetit"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"E lidhur përmes %1$s"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 8bb9277ba42b..b5a91bfed1d0 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Аутоматско повезивање није успело"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Нема приступа интернету"</string> <string name="saved_network" msgid="7143698034077223645">"Сачувао/ла је <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Повезани сте на мрежу са ограничењем"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Аутоматски повезано преко %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аутоматски повезано преко добављача оцене мреже"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Веза је успостављена преко приступне тачке %1$s"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 03fb2238989c..16a1be606707 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Det går inte att ansluta automatiskt"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetåtkomst"</string> <string name="saved_network" msgid="7143698034077223645">"Sparades av <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ansluten till nätverk med datapriser"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiskt ansluten via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiskt ansluten via leverantör av nätverksbetyg"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Anslutet via %1$s"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 631a41346282..58896c87baaa 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Haiwezi kuunganisha kiotomatiki"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Hakuna muunganisho wa intaneti"</string> <string name="saved_network" msgid="7143698034077223645">"Ilihifadhiwa na <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Imeunganishwa kwenye mtandao unaopima data"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Imeunganishwa kiotomatiki kupitia %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Imeunganishwa kiotomatiki kupitia mtoa huduma wa ukadiriaji wa mtandao"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Imeunganishwa kupitia %1$s"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 5796603a761a..71cf7d41fcc4 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"தானாக இணைக்கப்படாது"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"இண்டர்நெட் அணுகல் இல்லை"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> சேமித்தது"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"கட்டண நெட்வொர்க்குடன் இணைக்கப்பட்டுள்ளது"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s மூலம் தானாக இணைக்கப்பட்டது"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"நெட்வொர்க் மதிப்பீடு வழங்குநரால் தானாக இணைக்கப்பட்டது"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s வழியாக இணைக்கப்பட்டது"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 9027ca18fc79..c3a65e702a28 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"స్వయంచాలకంగా కనెక్ట్ కాదు"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ఇంటర్నెట్ యాక్సెస్ లేదు"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా సేవ్ చేయబడింది"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"డేటా నియంత్రణ నెట్వర్క్కు కనెక్ట్ చేయబడింది"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"నెట్వర్క్ రేటింగ్ ప్రదాత ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ద్వారా కనెక్ట్ చేయబడింది"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 8358dbbfafa5..903accbdc7ff 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"จะไม่เชื่อมต่อโดยอัตโนมัติ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"เข้าถึงอินเทอร์เน็ตไม่ได้"</string> <string name="saved_network" msgid="7143698034077223645">"บันทึกโดย<xliff:g id="NAME">%1$s</xliff:g> แล้ว"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"เชื่อมต่อกับเครือข่ายแบบจำกัดปริมาณแล้ว"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"เชื่อมต่ออัตโนมัติผ่าน %1$s แล้ว"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"เชื่อมต่ออัตโนมัติผ่านผู้ให้บริการการจัดอันดับเครือข่าย"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"เชื่อมต่อผ่าน %1$s แล้ว"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index d5250a4581db..b259201fe029 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Hindi awtomatikong kokonekta"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Walang access sa internet"</string> <string name="saved_network" msgid="7143698034077223645">"Na-save ng <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Nakakonekta sa nakametrong network"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Awtomatikong nakakonekta sa pamamagitan ng %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Awtomatikong nakakonekta sa pamamagitan ng provider ng rating ng network"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Nakakonekta sa pamamagitan ng %1$s"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 8e0b889f5191..a0bc3626d086 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Otomatik olarak bağlanma"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"İnternet erişimi yok"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tarafından kaydedildi"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Sayaçlı ağa bağlı"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzerinden otomatik olarak bağlı"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ağ derecelendirme sağlayıcı aracılığıyla otomatik olarak bağlandı"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s üzerinden bağlı"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index c2f71c38d83e..509c8a602794 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не під’єднуватиметься автоматично"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Немає доступу до Інтернету"</string> <string name="saved_network" msgid="7143698034077223645">"Збережено додатком <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Установлено з\'єднання з мережею з тарифікацією трафіку"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично під’єднано через %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично під’єднано через постачальника оцінки якості мережі"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Під’єднано через %1$s"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index a0c5f94a2845..e097ab2f5789 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"خودکار طور پر منسلک نہیں ہو گا"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"انٹرنیٹ تک کوئی رسائی نہیں"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> کی جانب سے محفوظ کردہ"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"میٹرڈ نیٹ ورک سے منسلک ہے"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s کے ذریعے از خود منسلک کردہ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"نیٹ ورک درجہ بندی کے فراہم کنندہ کے ذریعے از خود منسلک"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"منسلک بذریعہ %1$s"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 1fb610bad36e..8c2a95d5c316 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik ravishda ulanilmaydi"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Internet aloqasi yo‘q"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tomonidan saqlangan"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Trafik hisoblanadigan tarmoqqa ulandi"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s orqali avtomatik ulandi"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tarmoqlar reytingi muallifi orqali avtomatik ulandi"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s orqali ulangan"</string> @@ -156,7 +155,7 @@ <string name="running_process_item_user_label" msgid="3988506293099805796">"Foydalanuvchi: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> <string name="launch_defaults_some" msgid="3631650616557252926">"Ba’zi birlamchi sozlamalar o‘rnatilgan"</string> <string name="launch_defaults_none" msgid="8049374306261262709">"Birlamchi sozlamalar belgilanmagan"</string> - <string name="tts_settings" msgid="8130616705989351312">"Nutq sintezi sozlamalari"</string> + <string name="tts_settings" msgid="8130616705989351312">"Matnni nutqqa aylantirish sozlamalari"</string> <string name="tts_settings_title" msgid="7602210956640483039">"Nutq sintezi"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"Nutq tezligi"</string> <string name="tts_default_rate_summary" msgid="3781937042151716987">"Matnni o‘qish tezligi"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 1f3ea486bd08..7a4d89bd4f22 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sẽ không tự động kết nối"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Không có quyền truy cập Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Do <xliff:g id="NAME">%1$s</xliff:g> lưu"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Đã kết nối với mạng có đo lượng dữ liệu"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Tự động được kết nối qua %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tự động được kết nối qua nhà cung cấp dịch vụ xếp hạng mạng"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Được kết nối qua %1$s"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 828d25fbe71b..7cd61fc5491c 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"无法自动连接"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"无法访问互联网"</string> <string name="saved_network" msgid="7143698034077223645">"由“<xliff:g id="NAME">%1$s</xliff:g>”保存"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已连接到按流量计费的网络"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"已通过%1$s自动连接"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已自动连接(通过网络评分服务提供方)"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"已通过%1$s连接"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 54e1f41e9ed8..458071cd8cb2 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"不會自動連線"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"無法連接互聯網"</string> <string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已連結至按用量收費的網絡"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網絡評分供應商自動連線"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index b8f1f58b5691..867ed8b61c17 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"無法自動連線"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"沒有可用的網際網路連線"</string> <string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已連線到計量付費網路"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網路評分供應商自動連線"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index d680b66b56fa..e64dbd30dc82 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ngeke ize ixhumeke ngokuzenzakalela"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Akukho ukufinyelela kwe-inthanethi"</string> <string name="saved_network" msgid="7143698034077223645">"Kulondolozwe ngu-<xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Kuxhunywe kunethiwekhi eyenziwe imitha"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ixhumeke ngokuzenzakalela nge-%1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Kuxhunywe ngokuzenzakalelayo ngomhlinzeki wesilinganiso wenethiwekhi"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Kuxhumeke nge-%1$s"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java index b8030f166436..4c7b898a4fb5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java @@ -259,7 +259,12 @@ public class MobileStatusTracker { .append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode).append(',') .append("dataState=").append(dataState).append(',') .append("serviceState=").append(serviceState == null ? "" - : serviceState.toString()).append(',') + : "mVoiceRegState=" + serviceState.getState() + "(" + + ServiceState.rilServiceStateToString(serviceState.getState()) + + ")" + ", mDataRegState=" + serviceState.getDataRegState() + "(" + + ServiceState.rilServiceStateToString( + serviceState.getDataRegState()) + ")") + .append(',') .append("signalStrength=").append(signalStrength == null ? "" : signalStrength.toString()).append(',') .append("telephonyDisplayInfo=").append(telephonyDisplayInfo == null ? "" diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index ad6a5312f156..66165b6d1ff2 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -147,10 +147,5 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.NOTIFICATION_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR); - VALIDATORS.put( - Global.ONE_HANDED_KEYGUARD_SIDE, - new InclusiveIntegerRangeValidator( - /* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT, - /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT)); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 44864a61ade6..a6e2af9b1674 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -42,6 +42,7 @@ import android.provider.settings.validators.GlobalSettingsValidators; import android.provider.settings.validators.SecureSettingsValidators; import android.provider.settings.validators.SystemSettingsValidators; import android.provider.settings.validators.Validator; +import android.telephony.SubscriptionManager; import android.util.ArrayMap; import android.util.ArraySet; import android.util.BackupUtils; @@ -95,10 +96,11 @@ public class SettingsBackupAgent extends BackupAgentHelper { private static final String KEY_NETWORK_POLICIES = "network_policies"; private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config"; private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config"; + private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings"; // Versioning of the state file. Increment this version // number any time the set of state items is altered. - private static final int STATE_VERSION = 8; + private static final int STATE_VERSION = 9; // Versioning of the Network Policies backup payload. private static final int NETWORK_POLICIES_BACKUP_VERSION = 1; @@ -106,19 +108,20 @@ public class SettingsBackupAgent extends BackupAgentHelper { // Slots in the checksum array. Never insert new items in the middle // of this array; new slots must be appended. - private static final int STATE_SYSTEM = 0; - private static final int STATE_SECURE = 1; - private static final int STATE_LOCALE = 2; - private static final int STATE_WIFI_SUPPLICANT = 3; - private static final int STATE_WIFI_CONFIG = 4; - private static final int STATE_GLOBAL = 5; - private static final int STATE_LOCK_SETTINGS = 6; - private static final int STATE_SOFTAP_CONFIG = 7; - private static final int STATE_NETWORK_POLICIES = 8; - private static final int STATE_WIFI_NEW_CONFIG = 9; - private static final int STATE_DEVICE_CONFIG = 10; - - private static final int STATE_SIZE = 11; // The current number of state items + private static final int STATE_SYSTEM = 0; + private static final int STATE_SECURE = 1; + private static final int STATE_LOCALE = 2; + private static final int STATE_WIFI_SUPPLICANT = 3; + private static final int STATE_WIFI_CONFIG = 4; + private static final int STATE_GLOBAL = 5; + private static final int STATE_LOCK_SETTINGS = 6; + private static final int STATE_SOFTAP_CONFIG = 7; + private static final int STATE_NETWORK_POLICIES = 8; + private static final int STATE_WIFI_NEW_CONFIG = 9; + private static final int STATE_DEVICE_CONFIG = 10; + private static final int STATE_SIM_SPECIFIC_SETTINGS = 11; + + private static final int STATE_SIZE = 12; // The current number of state items // Number of entries in the checksum array at various version numbers private static final int STATE_SIZES[] = { @@ -130,7 +133,8 @@ public class SettingsBackupAgent extends BackupAgentHelper { 8, // version 5 added STATE_SOFTAP_CONFIG 9, // version 6 added STATE_NETWORK_POLICIES 10, // version 7 added STATE_WIFI_NEW_CONFIG - STATE_SIZE // version 8 added STATE_DEVICE_CONFIG + 11, // version 8 added STATE_DEVICE_CONFIG + STATE_SIZE // version 9 added STATE_SIM_SPECIFIC_SETTINGS }; private static final int FULL_BACKUP_ADDED_GLOBAL = 2; // added the "global" entry @@ -218,6 +222,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { byte[] netPoliciesData = getNetworkPolicies(); byte[] wifiFullConfigData = getNewWifiConfigData(); byte[] deviceSpecificInformation = getDeviceSpecificConfiguration(); + byte[] simSpecificSettingsData = getSimSpecificSettingsData(); long[] stateChecksums = readOldChecksums(oldState); @@ -246,6 +251,9 @@ public class SettingsBackupAgent extends BackupAgentHelper { stateChecksums[STATE_DEVICE_CONFIG] = writeIfChanged(stateChecksums[STATE_DEVICE_CONFIG], KEY_DEVICE_SPECIFIC_CONFIG, deviceSpecificInformation, data); + stateChecksums[STATE_SIM_SPECIFIC_SETTINGS] = + writeIfChanged(stateChecksums[STATE_SIM_SPECIFIC_SETTINGS], + KEY_SIM_SPECIFIC_SETTINGS, simSpecificSettingsData, data); writeNewChecksums(stateChecksums, newState); } @@ -386,6 +394,12 @@ public class SettingsBackupAgent extends BackupAgentHelper { preservedSettings); break; + case KEY_SIM_SPECIFIC_SETTINGS: + byte[] restoredSimSpecificSettings = new byte[size]; + data.readEntityData(restoredSimSpecificSettings, 0, size); + restoreSimSpecificSettings(restoredSimSpecificSettings); + break; + default : data.skipEntityData(); @@ -1189,6 +1203,20 @@ public class SettingsBackupAgent extends BackupAgentHelper { return true; } + private byte[] getSimSpecificSettingsData() { + SubscriptionManager subManager = SubscriptionManager.from(getBaseContext()); + byte[] simSpecificData = subManager.getAllSimSpecificSettingsForBackup(); + Log.i(TAG, "sim specific data of length + " + simSpecificData.length + + " successfully retrieved"); + + return simSpecificData; + } + + private void restoreSimSpecificSettings(byte[] data) { + SubscriptionManager subManager = SubscriptionManager.from(getBaseContext()); + subManager.restoreAllSimSpecificSettingsFromBackup(data); + } + private void updateWindowManagerIfNeeded(Integer previousDensity) { int newDensity; try { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index c11877a8a963..438cec8ef68f 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -283,7 +283,6 @@ public class SettingsBackupTest { Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS, Settings.Global.EUICC_SWITCH_SLOT_TIMEOUT_MILLIS, Settings.Global.FANCY_IME_ANIMATIONS, - Settings.Global.ONE_HANDED_KEYGUARD_SIDE, Settings.Global.FORCE_ALLOW_ON_EXTERNAL, Settings.Global.FORCED_APP_STANDBY_ENABLED, Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 9021864a5a86..7bfb42b7cfad 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -76,8 +76,8 @@ android_library { "androidx.dynamicanimation_dynamicanimation", "androidx-constraintlayout_constraintlayout", "androidx.exifinterface_exifinterface", - "kotlinx-coroutines-android", - "kotlinx-coroutines-core", + "kotlinx_coroutines_android", + "kotlinx_coroutines", "iconloader_base", "SystemUI-tags", "SystemUI-proto", diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml index b5f55af50bff..79868093fb12 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml @@ -24,7 +24,7 @@ <include style="@style/BouncerSecurityContainer" layout="@layout/keyguard_host_view" - android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> </FrameLayout> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml index c75ee51517d1..04e645bd0a32 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml @@ -41,14 +41,13 @@ android:layout_gravity="center"> <com.android.keyguard.KeyguardSecurityViewFlipper android:id="@+id/view_flipper" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" android:paddingTop="@dimen/keyguard_security_view_top_margin" android:paddingStart="@dimen/keyguard_security_view_lateral_margin" android:paddingEnd="@dimen/keyguard_security_view_lateral_margin" - android:layout_gravity="center" android:gravity="center"> </com.android.keyguard.KeyguardSecurityViewFlipper> </com.android.keyguard.KeyguardSecurityContainer> diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml deleted file mode 100644 index e09bf7e37ed0..000000000000 --- a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> - -<resources> - <bool name="can_use_one_handed_bouncer">true</bool> -</resources> diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml index 6176f7c1dd0a..8d9d6ee68c67 100644 --- a/packages/SystemUI/res-keyguard/values/config.xml +++ b/packages/SystemUI/res-keyguard/values/config.xml @@ -22,5 +22,4 @@ <!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] --> <bool name="config_disableMenuKeyInLockScreen">false</bool> - <bool name="can_use_one_handed_bouncer">false</bool> </resources> diff --git a/packages/SystemUI/res/drawable/controls_dialog_bg.xml b/packages/SystemUI/res/drawable/controls_dialog_bg.xml new file mode 100644 index 000000000000..cb4686dd04a7 --- /dev/null +++ b/packages/SystemUI/res/drawable/controls_dialog_bg.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="?android:attr/colorBackgroundFloating" /> + <corners android:radius="@dimen/notification_corner_radius" /> +</shape> diff --git a/packages/SystemUI/res/drawable/udfps_progress_bar.xml b/packages/SystemUI/res/drawable/udfps_progress_bar.xml new file mode 100644 index 000000000000..e5389f3b99ef --- /dev/null +++ b/packages/SystemUI/res/drawable/udfps_progress_bar.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@android:id/background"> + <shape + android:innerRadiusRatio="2.2" + android:shape="ring" + android:thickness="@dimen/udfps_enroll_progress_thickness" + android:useLevel="false" + android:tint="?android:colorControlNormal"> + <solid android:color="@*android:color/white_disabled_material" /> + </shape> + </item> + <item android:id="@android:id/progress"> + <rotate + android:fromDegrees="270" + android:pivotX="50%" + android:pivotY="50%" + android:toDegrees="270"> + <shape + android:innerRadiusRatio="2.2" + android:shape="ring" + android:thickness="@dimen/udfps_enroll_progress_thickness" + android:tint="?android:attr/colorControlActivated"> + <solid android:color="@android:color/white" /> + </shape> + </rotate> + </item> +</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/controls_in_dialog.xml b/packages/SystemUI/res/layout/controls_in_dialog.xml new file mode 100644 index 000000000000..983999f9a5f8 --- /dev/null +++ b/packages/SystemUI/res/layout/controls_in_dialog.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/control_detail_root" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginVertical="@dimen/controls_activity_view_top_offset" + android:layout_marginHorizontal="@dimen/controls_activity_view_side_offset" + android:padding="8dp" + android:orientation="vertical" + android:background="@drawable/controls_dialog_bg"> + + <com.android.systemui.globalactions.MinHeightScrollView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:orientation="vertical" + android:scrollbars="none"> + + <LinearLayout + android:id="@+id/global_actions_controls" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:orientation="vertical" + android:clipToPadding="false" /> + + </com.android.systemui.globalactions.MinHeightScrollView> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 04de9784812f..862076b650b9 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -81,6 +81,17 @@ android:contentDescription="@string/accessibility_phone_button" android:tint="?attr/wallpaperTextColor" /> + <ImageView + android:id="@+id/alt_left_button" + android:layout_height="@dimen/keyguard_affordance_height" + android:layout_width="@dimen/keyguard_affordance_width" + android:layout_gravity="bottom|start" + android:scaleType="center" + android:tint="?attr/wallpaperTextColor" + android:layout_marginStart="24dp" + android:layout_marginBottom="48dp" + android:visibility="gone" /> + <FrameLayout android:id="@+id/overlay_container" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml index 8f3345f9d85c..e2f3e2a306e3 100644 --- a/packages/SystemUI/res/layout/long_screenshot.xml +++ b/packages/SystemUI/res/layout/long_screenshot.xml @@ -74,7 +74,7 @@ android:id="@+id/preview" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="24dp" + android:layout_marginBottom="42dp" android:layout_marginHorizontal="48dp" android:adjustViewBounds="true" app:layout_constrainedHeight="true" @@ -91,19 +91,33 @@ android:id="@+id/crop_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="24dp" + android:layout_marginBottom="42dp" app:layout_constrainedHeight="true" app:layout_constrainedWidth="true" app:layout_constraintTop_toBottomOf="@id/guideline" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent" - app:handleThickness="3dp" + app:handleThickness="@dimen/screenshot_crop_handle_thickness" app:handleColor="@*android:color/accent_device_default" - app:scrimColor="#9444" + app:scrimColor="@color/screenshot_crop_scrim" tools:background="?android:colorBackground" tools:minHeight="100dp" tools:minWidth="100dp" /> + <com.android.systemui.screenshot.MagnifierView + android:id="@+id/magnifier" + android:visibility="invisible" + android:layout_width="200dp" + android:layout_height="200dp" + app:layout_constraintTop_toBottomOf="@id/guideline" + app:layout_constraintLeft_toLeftOf="parent" + app:handleThickness="@dimen/screenshot_crop_handle_thickness" + app:handleColor="@*android:color/accent_device_default" + app:scrimColor="@color/screenshot_crop_scrim" + app:borderThickness="4dp" + app:borderColor="#fff" + /> + </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index c0788051efed..6ae306e17209 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -20,4 +20,17 @@ android:id="@+id/udfps_view" android:layout_width="match_parent" android:layout_height="match_parent" - systemui:sensorTouchAreaCoefficient="0.5"/> + systemui:sensorTouchAreaCoefficient="0.5"> + + <!-- Enrollment progress bar--> + <com.android.systemui.biometrics.UdfpsProgressBar + android:id="@+id/progress_bar" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:max="100" + android:padding="@dimen/udfps_enroll_progress_thickness" + android:progress="0" + android:layout_gravity="center" + android:visibility="gone"/> + +</com.android.systemui.biometrics.UdfpsView> diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml index 6dff2e3ef6c2..a6321fe14894 100644 --- a/packages/SystemUI/res/values-sw600dp-land/config.xml +++ b/packages/SystemUI/res/values-sw600dp-land/config.xml @@ -19,4 +19,7 @@ <!-- Max number of columns for quick controls area --> <integer name="controls_max_columns">2</integer> + + <!-- Whether to use the split 2-column notification shade --> + <bool name="config_use_split_notification_shade">true</bool> </resources> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 6c55fb62e638..8166e35d5b6a 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -178,6 +178,14 @@ <attr name="scrimColor" format="color" /> </declare-styleable> + <declare-styleable name="MagnifierView"> + <attr name="handleThickness" format="dimension" /> + <attr name="handleColor" format="color" /> + <attr name="scrimColor" format="color" /> + <attr name="borderThickness" format="dimension" /> + <attr name="borderColor" format="color" /> + </declare-styleable> + <declare-styleable name="RoundedCornerProgressDrawable"> <attr name="android:drawable" /> </declare-styleable> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index a7cf3e91dbba..5fb6de7bb588 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -197,6 +197,9 @@ <color name="global_screenshot_dismiss_foreground">@color/GM2_grey_500</color> <color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black --> + <!-- Long screenshot UI --> + <color name="screenshot_crop_scrim">#9444</color> + <!-- GM2 colors --> <color name="GM2_grey_50">#F8F9FA</color> <color name="GM2_grey_100">#F1F3F4</color> @@ -261,6 +264,7 @@ <color name="control_enabled_cool_foreground">@color/GM2_blue_300</color> <color name="control_thumbnail_tint">#33000000</color> <color name="control_thumbnail_shadow_color">@*android:color/black</color> + <color name="controls_lockscreen_scrim">#AA000000</color> <!-- Docked misalignment message --> <color name="misalignment_text_color">#F28B82</color> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 44eeba1146a4..bb04c3b35628 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -565,4 +565,7 @@ <!-- Whether wallet view is shown in landscape / seascape orientations --> <bool name="global_actions_show_landscape_wallet_view">false</bool> + + <!-- Whether to use the split 2-column notification shade --> + <bool name="config_use_split_notification_shade">false</bool> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index d92f4ea65390..afa98b56a13c 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -345,6 +345,7 @@ <dimen name="screenshot_action_chip_padding_end">16dp</dimen> <dimen name="screenshot_action_chip_text_size">14sp</dimen> <dimen name="screenshot_dismissal_height_delta">80dp</dimen> + <dimen name="screenshot_crop_handle_thickness">3dp</dimen> <!-- The width of the view containing navigation buttons --> @@ -1116,6 +1117,9 @@ <!-- Y translation for credential contents when animating in --> <dimen name="biometric_dialog_credential_translation_offset">60dp</dimen> + <!-- UDFPS enrollment progress bar thickness --> + <dimen name="udfps_enroll_progress_thickness">12dp</dimen> + <!-- Wireless Charging Animation values --> <dimen name="wireless_charging_dots_radius_start">0dp</dimen> <dimen name="wireless_charging_dots_radius_end">4dp</dimen> @@ -1259,6 +1263,7 @@ <!-- Home Controls activity view detail panel--> <dimen name="controls_activity_view_top_offset">100dp</dimen> + <dimen name="controls_activity_view_side_offset">12dp</dimen> <dimen name="controls_activity_view_text_size">17sp</dimen> <dimen name="controls_activity_view_corner_radius">@*android:dimen/config_bottomDialogCornerRadius</dimen> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index 01e54ff60582..ded8a2e66aea 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -34,6 +34,9 @@ <bool name="flag_brightness_slider">false</bool> + <!-- The new animations to/from lockscreen and AOD! --> + <bool name="flag_lockscreen_animations">false</bool> + <!-- People Tile flag --> <bool name="flag_conversations">false</bool> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index d93239531038..589a39c16bcb 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -954,11 +954,8 @@ <string name="quick_settings_dark_mode_secondary_label_on_at">On at <xliff:g id="time" example="10 pm">%s</xliff:g></string> <!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until some user-selected time. [CHAR LIMIT=20] --> <string name="quick_settings_dark_mode_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string> - <!-- TODO(b/170970602): remove translatable=false when RBC has official name and strings --> - <!-- QuickSettings: Label for the toggle that controls whether Reduce Bright Colors is enabled. [CHAR LIMIT=NONE] --> - <string name="quick_settings_reduce_bright_colors_label" translatable="false">Reduce Bright Colors</string> - <!-- QuickSettings: Secondary text for intensity level of Reduce Bright Colors. [CHAR LIMIT=NONE] --> - <string name="quick_settings_reduce_bright_colors_secondary_label" translatable="false"> <xliff:g id="intensity" example="50">%d</xliff:g>%% reduction</string> + <!-- QuickSettings: Label for the toggle that controls whether Reduce Brightness is enabled. [CHAR LIMIT=NONE] --> + <string name="quick_settings_reduce_bright_colors_label">Reduce Brightness</string> <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] --> <string name="quick_settings_nfc_label">NFC</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 4b04eebfddf0..7c72548a7252 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -662,6 +662,19 @@ <item name="android:windowExitAnimation">@anim/bottomsheet_out</item> </style> + <style name="Theme.SystemUI.Dialog.Control.LockScreen" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar"> + <item name="android:windowAnimationStyle">@style/Animation.Fade</item> + <item name="android:windowFullscreen">true</item> + <item name="android:windowIsFloating">false</item> + <item name="android:windowBackground">@color/controls_lockscreen_scrim</item> + <item name="android:backgroundDimEnabled">true</item> + </style> + + <style name="Animation.Fade"> + <item name="android:windowEnterAnimation">@android:anim/fade_in</item> + <item name="android:windowExitAnimation">@android:anim/fade_out</item> + </style> + <style name="Control" /> <style name="Control.MenuItem"> @@ -752,4 +765,12 @@ <item name="android:textSize">14sp</item> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> </style> + + <style name="UdfpsProgressBarStyle" + parent="android:style/Widget.Material.ProgressBar.Horizontal"> + <item name="android:indeterminate">false</item> + <item name="android:max">10000</item> + <item name="android:mirrorForRtl">false</item> + <item name="android:progressDrawable">@drawable/udfps_progress_bar</item> + </style> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java index 3584c82bc57d..e2ca349cc5c8 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java @@ -72,9 +72,13 @@ public abstract class ActivityOptionsCompat { return ActivityOptions.makeRemoteTransition(remoteTransition.getTransition()); } + /** + * Returns ActivityOptions for overriding task transition animation. + */ public static ActivityOptions makeCustomAnimation(Context context, int enterResId, int exitResId, final Runnable callback, final Handler callbackHandler) { - return ActivityOptions.makeCustomAnimation(context, enterResId, exitResId, callbackHandler, + return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId, + callbackHandler, new ActivityOptions.OnAnimationStartedListener() { @Override public void onAnimationStarted() { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java index 19e7d7ea4e6e..bec9220028b1 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java @@ -17,7 +17,6 @@ package com.android.systemui.shared.system; import android.annotation.IntDef; -import android.annotation.NonNull; import android.view.View; import com.android.internal.jank.InteractionJankMonitor; @@ -37,6 +36,10 @@ public final class InteractionJankMonitorWrapper { InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP; public static final int CUJ_QUICK_SWITCH = InteractionJankMonitor.CUJ_LAUNCHER_QUICK_SWITCH; + public static final int CUJ_OPEN_ALL_APPS = + InteractionJankMonitor.CUJ_LAUNCHER_OPEN_ALL_APPS; + public static final int CUJ_ALL_APPS_SCROLL = + InteractionJankMonitor.CUJ_LAUNCHER_ALL_APPS_SCROLL; @IntDef({ CUJ_APP_LAUNCH_FROM_RECENTS, diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java index a56c6a1f084e..e6477f158c27 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java @@ -18,9 +18,11 @@ package com.android.systemui.shared.system; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.view.WindowManager.TransitionOldType; import android.os.RemoteException; import android.util.Log; @@ -65,13 +67,17 @@ public class RemoteAnimationAdapterCompat { final RemoteAnimationRunnerCompat remoteAnimationAdapter) { return new IRemoteAnimationRunner.Stub() { @Override - public void onAnimationStart(RemoteAnimationTarget[] apps, + public void onAnimationStart(@TransitionOldType int transit, + RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, final IRemoteAnimationFinishedCallback finishedCallback) { final RemoteAnimationTargetCompat[] appsCompat = RemoteAnimationTargetCompat.wrap(apps); final RemoteAnimationTargetCompat[] wallpapersCompat = RemoteAnimationTargetCompat.wrap(wallpapers); + final RemoteAnimationTargetCompat[] nonAppsCompat = + RemoteAnimationTargetCompat.wrap(nonApps); final Runnable animationFinishedCallback = new Runnable() { @Override public void run() { @@ -83,8 +89,8 @@ public class RemoteAnimationAdapterCompat { } } }; - remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat, - animationFinishedCallback); + remoteAnimationAdapter.onAnimationStart(transit, appsCompat, wallpapersCompat, + nonAppsCompat, animationFinishedCallback); } @Override @@ -104,6 +110,9 @@ public class RemoteAnimationAdapterCompat { RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */); final RemoteAnimationTargetCompat[] wallpapersCompat = RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */); + // TODO(bc-unlock): Build wrapped object for non-apps target. + final RemoteAnimationTargetCompat[] nonAppsCompat = + new RemoteAnimationTargetCompat[0]; final Runnable animationFinishedCallback = new Runnable() { @Override public void run() { @@ -147,7 +156,10 @@ public class RemoteAnimationAdapterCompat { } } t.apply(); - remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat, + // TODO(bc-unlcok): Pass correct transit type. + remoteAnimationAdapter.onAnimationStart( + TRANSIT_OLD_NONE, + appsCompat, wallpapersCompat, nonAppsCompat, animationFinishedCallback); } }; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java index 33372f6bd0b9..007629254c7c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java @@ -16,8 +16,11 @@ package com.android.systemui.shared.system; +import android.view.WindowManager; + public interface RemoteAnimationRunnerCompat { - void onAnimationStart(RemoteAnimationTargetCompat[] apps, - RemoteAnimationTargetCompat[] wallpapers, Runnable finishedCallback); + void onAnimationStart(@WindowManager.TransitionOldType int transit, + RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers, + RemoteAnimationTargetCompat[] nonApps, Runnable finishedCallback); void onAnimationCancelled(); }
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java index 70021b6f3d45..fbabaa489d74 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java @@ -114,10 +114,13 @@ public class SyncRtSurfaceTransactionApplierCompat { for (int i = params.length - 1; i >= 0; i--) { SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = params[i]; - t.deferTransactionUntil(surfaceParams.surface, mBarrierSurfaceControl, frame); surfaceParams.applyTo(t); } - t.apply(); + if (mTargetViewRootImpl != null) { + mTargetViewRootImpl.mergeWithNextTransaction(t, frame); + } else { + t.apply(); + } Trace.traceEnd(Trace.TRACE_TAG_VIEW); Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0) .sendToTarget(); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java index 4a28d56a41e1..89c60f1d3f06 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java @@ -56,4 +56,12 @@ public class ViewRootImplCompat { }); } } + + public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frame) { + if (mViewRoot != null) { + mViewRoot.mergeWithNextTransaction(t, frame); + } else { + t.apply(); + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 85e9ca05d428..276036c400e1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -74,16 +74,7 @@ public class KeyguardDisplayManager { @Override public void onDisplayChanged(int displayId) { - if (displayId == DEFAULT_DISPLAY) return; - 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); - } - } + } @Override @@ -305,15 +296,16 @@ public class KeyguardDisplayManager { } @Override + public void onDisplayChanged() { + updateBounds(); + getWindow().getDecorView().requestLayout(); + } + + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics() - .getBounds(); - mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100; - mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100; - mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200; - mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200; + updateBounds(); setContentView(LayoutInflater.from(mContext) .inflate(R.layout.keyguard_presentation, null)); @@ -338,5 +330,14 @@ public class KeyguardDisplayManager { mKeyguardClockSwitchController.init(); } + + private void updateBounds() { + final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics() + .getBounds(); + mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100; + mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100; + mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200; + mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200; + } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index c182fd178202..5f6fd30ffa1b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -29,17 +29,12 @@ import android.app.AlertDialog; import android.content.Context; import android.graphics.Insets; import android.graphics.Rect; -import android.provider.Settings; import android.util.AttributeSet; import android.util.MathUtils; import android.util.TypedValue; -import android.view.Gravity; import android.view.MotionEvent; -import android.view.OrientationEventListener; import android.view.VelocityTracker; -import android.view.View; import android.view.ViewConfiguration; -import android.view.ViewPropertyAnimator; import android.view.WindowInsets; import android.view.WindowInsetsAnimation; import android.view.WindowInsetsAnimationControlListener; @@ -60,7 +55,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import java.util.List; @@ -105,12 +99,6 @@ public class KeyguardSecurityContainer extends FrameLayout { private boolean mDisappearAnimRunning; private SwipeListener mSwipeListener; - private boolean mIsSecurityViewLeftAligned = true; - private boolean mOneHandedMode = false; - private SecurityMode mSecurityMode = SecurityMode.Invalid; - private ViewPropertyAnimator mRunningOneHandedAnimator; - private final OrientationEventListener mOrientationEventListener; - private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback = new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { @@ -169,20 +157,16 @@ public class KeyguardSecurityContainer extends FrameLayout { // Used to notify the container when something interesting happens. public interface SecurityCallback { boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen); - void userActivity(); - void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput); /** - * @param strongAuth wheher the user has authenticated with strong authentication like - * pattern, password or PIN but not by trust agents or fingerprint + * @param strongAuth wheher the user has authenticated with strong authentication like + * pattern, password or PIN but not by trust agents or fingerprint * @param targetUserId a user that needs to be the foreground user at the finish completion. */ void finish(boolean strongAuth, int targetUserId); - void reset(); - void onCancelClicked(); } @@ -240,136 +224,12 @@ public class KeyguardSecurityContainer extends FrameLayout { super(context, attrs, defStyle); mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y); mViewConfiguration = ViewConfiguration.get(context); - - mOrientationEventListener = new OrientationEventListener(context) { - @Override - public void onOrientationChanged(int orientation) { - updateLayoutForSecurityMode(mSecurityMode); - } - }; } void onResume(SecurityMode securityMode, boolean faceAuthEnabled) { - mSecurityMode = securityMode; mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback); updateBiometricRetry(securityMode, faceAuthEnabled); - updateLayoutForSecurityMode(securityMode); - mOrientationEventListener.enable(); - } - - void updateLayoutForSecurityMode(SecurityMode securityMode) { - mSecurityMode = securityMode; - mOneHandedMode = canUseOneHandedBouncer(); - - if (mOneHandedMode) { - mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext); - } - - updateSecurityViewGravity(); - updateSecurityViewLocation(false); - } - - /** Return whether the one-handed keyguard should be enabled. */ - private boolean canUseOneHandedBouncer() { - // Is it enabled? - if (!getResources().getBoolean( - com.android.internal.R.bool.config_enableOneHandedKeyguard)) { - return false; - } - - if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) { - return false; - } - - return getResources().getBoolean(R.bool.can_use_one_handed_bouncer); - } - - /** Read whether the one-handed keyguard should be on the left/right from settings. */ - private boolean isOneHandedKeyguardLeftAligned(Context context) { - try { - return Settings.Global.getInt(context.getContentResolver(), - Settings.Global.ONE_HANDED_KEYGUARD_SIDE) - == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT; - } catch (Settings.SettingNotFoundException ex) { - return true; - } - } - - private void updateSecurityViewGravity() { - View securityView = findKeyguardSecurityView(); - - if (securityView == null) { - return; - } - - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams(); - - if (mOneHandedMode) { - lp.gravity = Gravity.LEFT | Gravity.BOTTOM; - } else { - lp.gravity = Gravity.CENTER; - } - - securityView.setLayoutParams(lp); - } - - /** - * Moves the inner security view to the correct location (in one handed mode) with animation. - * This is triggered when the user taps on the side of the screen that is not currently occupied - * by the security view . - */ - private void updateSecurityViewLocation(boolean animate) { - View securityView = findKeyguardSecurityView(); - - if (securityView == null) { - return; - } - - if (!mOneHandedMode) { - securityView.setTranslationX(0); - return; - } - - if (mRunningOneHandedAnimator != null) { - mRunningOneHandedAnimator.cancel(); - mRunningOneHandedAnimator = null; - } - - int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f); - - if (animate) { - mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation); - mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mRunningOneHandedAnimator = null; - } - }); - - mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); - mRunningOneHandedAnimator.start(); - } else { - securityView.setTranslationX(targetTranslation); - } - } - - @Nullable - private KeyguardSecurityViewFlipper findKeyguardSecurityView() { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - - if (isKeyguardSecurityView(child)) { - return (KeyguardSecurityViewFlipper) child; - } - } - - return null; - } - - private boolean isKeyguardSecurityView(View view) { - return view instanceof KeyguardSecurityViewFlipper; } public void onPause() { @@ -378,7 +238,6 @@ public class KeyguardSecurityContainer extends FrameLayout { mAlertDialog = null; } mSecurityViewFlipper.setWindowInsetsAnimationCallback(null); - mOrientationEventListener.disable(); } @Override @@ -460,44 +319,19 @@ public class KeyguardSecurityContainer extends FrameLayout { if (mSwipeListener != null) { mSwipeListener.onSwipeUp(); } - } else { - if (!mIsDragging) { - handleTap(event); - } } } return true; } - private void handleTap(MotionEvent event) { - // If we're using a fullscreen security mode, skip - if (!mOneHandedMode) { - return; - } - - // Did the tap hit the "other" side of the bouncer? - if ((mIsSecurityViewLeftAligned && (event.getX() > getWidth() / 2f)) - || (!mIsSecurityViewLeftAligned && (event.getX() < getWidth() / 2f))) { - mIsSecurityViewLeftAligned = !mIsSecurityViewLeftAligned; - - Settings.Global.putInt( - mContext.getContentResolver(), - Settings.Global.ONE_HANDED_KEYGUARD_SIDE, - mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT - : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT); - - updateSecurityViewLocation(true); - } - } - void setSwipeListener(SwipeListener swipeListener) { mSwipeListener = swipeListener; } private void startSpringAnimation(float startVelocity) { mSpringAnimation - .setStartVelocity(startVelocity) - .animateToFinalPosition(0); + .setStartVelocity(startVelocity) + .animateToFinalPosition(0); } public void startDisappearAnimation(SecurityMode securitySelection) { @@ -607,17 +441,18 @@ public class KeyguardSecurityContainer extends FrameLayout { return insets.inset(0, 0, 0, inset); } + private void showDialog(String title, String message) { if (mAlertDialog != null) { mAlertDialog.dismiss(); } mAlertDialog = new AlertDialog.Builder(mContext) - .setTitle(title) - .setMessage(message) - .setCancelable(false) - .setNeutralButton(R.string.ok, null) - .create(); + .setTitle(title) + .setMessage(message) + .setCancelable(false) + .setNeutralButton(R.string.ok, null) + .create(); if (!(mContext instanceof Activity)) { mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); } @@ -655,44 +490,6 @@ public class KeyguardSecurityContainer extends FrameLayout { } } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int maxHeight = 0; - int maxWidth = 0; - int childState = 0; - - int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(widthMeasureSpec) / 2, - MeasureSpec.getMode(widthMeasureSpec)); - - for (int i = 0; i < getChildCount(); i++) { - final View view = getChildAt(i); - if (view.getVisibility() != GONE) { - if (mOneHandedMode && isKeyguardSecurityView(view)) { - measureChildWithMargins(view, halfWidthMeasureSpec, 0, - heightMeasureSpec, 0); - } else { - measureChildWithMargins(view, widthMeasureSpec, 0, - heightMeasureSpec, 0); - } - final LayoutParams lp = (LayoutParams) view.getLayoutParams(); - maxWidth = Math.max(maxWidth, - view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); - maxHeight = Math.max(maxHeight, - view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); - childState = combineMeasuredStates(childState, view.getMeasuredState()); - } - } - - // Check against our minimum height and width - maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); - maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); - - setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), - resolveSizeAndState(maxHeight, heightMeasureSpec, - childState << MEASURED_HEIGHT_STATE_SHIFT)); - } - void showAlmostAtWipeDialog(int attempts, int remaining, int userType) { String message = null; switch (userType) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index fdab8db67431..1a8d420fb394 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -404,7 +404,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard if (newView != null) { newView.onResume(KeyguardSecurityView.VIEW_REVEALED); mSecurityViewFlipperController.show(newView); - mView.updateLayoutForSecurityMode(securityMode); } mSecurityCallback.onSecurityModeChanged( diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java index 631c24844417..c77c86711abf 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java @@ -92,13 +92,4 @@ public class KeyguardSecurityModel { throw new IllegalStateException("Unknown security quality:" + security); } } - - /** - * Returns whether the given security view should be used in a "one handed" way. This can be - * used to change how the security view is drawn (e.g. take up less of the screen, and align to - * one side). - */ - public static boolean isSecurityViewOneHanded(SecurityMode securityMode) { - return securityMode == SecurityMode.Pattern || securityMode == SecurityMode.PIN; - } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 055270ddf0b8..7d06dd6ab085 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -23,7 +23,6 @@ import static android.hardware.biometrics.BiometricManager.Authenticators; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; -import android.app.IActivityTaskManager; import android.app.TaskStackListener; import android.content.BroadcastReceiver; import android.content.Context; @@ -137,7 +136,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mActivityTaskManager.getTasks(1); if (!runningTasks.isEmpty()) { final String topPackage = runningTasks.get(0).topActivity.getPackageName(); - if (!topPackage.contentEquals(clientPackage)) { + if (!topPackage.contentEquals(clientPackage) + && !Utils.isSystem(mContext, clientPackage)) { Log.w(TAG, "Evicting client due to: " + topPackage); mCurrentDialog.dismissWithoutCallback(true /* animate */); mCurrentDialog = null; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java index e07c84034b31..5290986b2a1c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java @@ -38,6 +38,7 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { private static final String TAG = "UdfpsAnimationEnroll"; private static final float SHADOW_RADIUS = 5.f; + private static final float PROGRESS_BAR_RADIUS = 140.f; @Nullable private RectF mSensorRect; @NonNull private final Paint mSensorPaint; @@ -81,12 +82,12 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { @Override public int getPaddingX() { - return (int) Math.ceil(SHADOW_RADIUS); + return (int) Math.ceil(PROGRESS_BAR_RADIUS); } @Override public int getPaddingY() { - return (int) Math.ceil(SHADOW_RADIUS); + return (int) Math.ceil(PROGRESS_BAR_RADIUS); } @Override @@ -104,12 +105,4 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { public int getOpacity() { return 0; } - - public void onEnrollmentProgress(int remaining) { - Log.d(TAG, "Remaining: " + remaining); - } - - public void onEnrollmentHelp() { - Log.d(TAG, "onEnrollmentHelp"); - } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java index 4e3419e1fab3..41ea4d66f575 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java @@ -103,18 +103,6 @@ public class UdfpsAnimationView extends View implements DozeReceiver, postInvalidate(); } - void onEnrollmentProgress(int remaining) { - if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) { - ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentProgress(remaining); - } - } - - void onEnrollmentHelp() { - if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) { - ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp(); - } - } - public int getPaddingX() { if (mUdfpsAnimation == null) { return 0; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index baa597398188..e7b08e72877d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -84,6 +84,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private boolean mIsOverlayRequested; // Reason the overlay has been requested. See IUdfpsOverlayController for definitions. private int mRequestReason; + @Nullable UdfpsEnrollHelper mEnrollHelper; // The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when // to turn off high brightness mode. To get around this limitation, the state of the AOD @@ -95,6 +96,12 @@ public class UdfpsController implements DozeReceiver, HbmCallback { public class UdfpsOverlayController extends IUdfpsOverlayController.Stub { @Override public void showUdfpsOverlay(int sensorId, int reason) { + if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR + || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) { + mEnrollHelper = new UdfpsEnrollHelper(reason); + } else { + mEnrollHelper = null; + } UdfpsController.this.showOverlay(reason); } @@ -260,15 +267,17 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // Transform dimensions if the device is in landscape mode. switch (mContext.getDisplay().getRotation()) { case Surface.ROTATION_90: - mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius; - mCoreLayoutParams.y = - p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius; + mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius + - paddingX; + mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius + - paddingY; break; case Surface.ROTATION_270: - mCoreLayoutParams.x = - p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius; - mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius; + mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius + - paddingX; + mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius + - paddingY; break; default: @@ -294,7 +303,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { try { Log.v(TAG, "showUdfpsOverlay | adding window"); final UdfpsAnimation animation = getUdfpsAnimationForReason(reason); - mView.setUdfpsAnimation(animation); + mView.setExtras(animation, mEnrollHelper); mWindowManager.addView(mView, computeLayoutParams(animation)); mView.setOnTouchListener(mOnTouchListener); mIsOverlayShowing = true; @@ -311,7 +320,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private UdfpsAnimation getUdfpsAnimationForReason(int reason) { Log.d(TAG, "getUdfpsAnimationForReason: " + reason); switch (reason) { - case IUdfpsOverlayController.REASON_ENROLL: + case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR: + case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: return new UdfpsAnimationEnroll(mContext); case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: return new UdfpsAnimationKeyguard(mView, mContext, mStatusBarStateController); @@ -327,7 +337,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { mFgExecutor.execute(() -> { if (mIsOverlayShowing) { Log.v(TAG, "hideUdfpsOverlay | removing window"); - mView.setUdfpsAnimation(null); + mView.setExtras(null, null); mView.setOnTouchListener(null); // Reset the controller back to its starting state. onFingerUp(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java new file mode 100644 index 000000000000..2442633a4a69 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics; + +import android.hardware.fingerprint.IUdfpsOverlayController; + +import androidx.annotation.NonNull; + +/** + * Helps keep track of enrollment state and animates the progress bar accordingly. + */ +public class UdfpsEnrollHelper { + private static final String TAG = "UdfpsEnrollHelper"; + + // IUdfpsOverlayController reason + private final int mEnrollReason; + + private int mTotalSteps = -1; + private int mCurrentProgress = 0; + + public UdfpsEnrollHelper(int reason) { + mEnrollReason = reason; + } + + boolean shouldShowProgressBar() { + return mEnrollReason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING; + } + + void onEnrollmentProgress(int remaining, @NonNull UdfpsProgressBar progressBar) { + if (mTotalSteps == -1) { + mTotalSteps = remaining; + } + + mCurrentProgress = progressBar.getMax() * Math.max(0, mTotalSteps + 1 - remaining) + / (mTotalSteps + 1); + progressBar.setProgress(mCurrentProgress, true /* animate */); + } + + void updateProgress(@NonNull UdfpsProgressBar progressBar) { + progressBar.setProgress(mCurrentProgress); + } + + void onEnrollmentHelp() { + + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java new file mode 100644 index 000000000000..84e2fab7bf6b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ProgressBar; + +import com.android.systemui.R; + +/** + * A (determinate) progress bar in the form of a ring. The progress bar goes clockwise starting + * from the 12 o'clock position. This view maintain equal width and height using a strategy similar + * to "centerInside" for ImageView. + */ +public class UdfpsProgressBar extends ProgressBar { + + public UdfpsProgressBar(Context context) { + this(context, null); + } + + public UdfpsProgressBar(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public UdfpsProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, R.style.UdfpsProgressBarStyle); + } + + public UdfpsProgressBar(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + final int measuredHeight = getMeasuredHeight(); + final int measuredWidth = getMeasuredWidth(); + + final int length = Math.min(measuredHeight, measuredWidth); + setMeasuredDimension(length, length); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index 7e378d3c568e..00cb28b8b8fb 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -56,6 +56,8 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin @NonNull private final RectF mSensorRect; @NonNull private final Paint mDebugTextPaint; + @Nullable private UdfpsProgressBar mProgressBar; + // Used to obtain the sensor location. @NonNull private FingerprintSensorPropertiesInternal mSensorProps; @@ -64,6 +66,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin private boolean mIlluminationRequested; private int mStatusBarState; private boolean mNotificationShadeExpanded; + @Nullable private UdfpsEnrollHelper mEnrollHelper; public UdfpsView(Context context, AttributeSet attrs) { super(context, attrs); @@ -108,8 +111,17 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin mSensorProps = properties; } - void setUdfpsAnimation(@Nullable UdfpsAnimation animation) { + void setExtras(@Nullable UdfpsAnimation animation, @Nullable UdfpsEnrollHelper enrollHelper) { mAnimationView.setAnimation(animation); + mEnrollHelper = enrollHelper; + + if (enrollHelper != null) { + mEnrollHelper.updateProgress(mProgressBar); + mProgressBar.setVisibility(enrollHelper.shouldShowProgressBar() + ? View.VISIBLE : View.GONE); + } else { + mProgressBar.setVisibility(View.GONE); + } } @Override @@ -138,6 +150,11 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin } @Override + protected void onFinishInflate() { + mProgressBar = findViewById(R.id.progress_bar); + } + + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); mSensorRect.set(0 + mAnimationView.getPaddingX(), @@ -233,10 +250,10 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin } void onEnrollmentProgress(int remaining) { - mAnimationView.onEnrollmentProgress(remaining); + mEnrollHelper.onEnrollmentProgress(remaining, mProgressBar); } void onEnrollmentHelp() { - mAnimationView.onEnrollmentHelp(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java index fd5e85a953ad..076c7cbe3937 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java @@ -16,13 +16,16 @@ package com.android.systemui.biometrics; +import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.hardware.biometrics.BiometricManager.Authenticators; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.pm.PackageManager; import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorPropertiesInternal; import android.os.UserManager; @@ -116,4 +119,10 @@ public class Utils { return false; } + + static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) { + final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL) + == PackageManager.PERMISSION_GRANTED; + return hasPermission && "android".equals(clientPackage); + } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt new file mode 100644 index 000000000000..8e878cf76ad9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls.ui + +import android.app.Dialog +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager + +import com.android.systemui.Interpolators +import com.android.systemui.R +import com.android.systemui.broadcast.BroadcastDispatcher + +/** + * Show the controls space inside a dialog, as from the lock screen. + */ +class ControlsDialog( + thisContext: Context, + val broadcastDispatcher: BroadcastDispatcher +) : Dialog(thisContext, R.style.Theme_SystemUI_Dialog_Control_LockScreen) { + + private val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.getAction() + if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { + dismiss() + } + } + } + + init { + window.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) + window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM + or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) + + setContentView(R.layout.controls_in_dialog) + + requireViewById<ViewGroup>(R.id.control_detail_root).apply { + setOnClickListener { dismiss() } + (getParent() as View).setOnClickListener { dismiss() } + } + } + + fun show( + controller: ControlsUiController + ): ControlsDialog { + super.show() + + val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls) + vg.alpha = 0f + controller.show(vg, { /* do nothing */ }) + + vg.animate() + .alpha(1f) + .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) + .setDuration(300) + + val filter = IntentFilter() + filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) + broadcastDispatcher.registerReceiver(receiver, filter) + + return this + } + + override fun dismiss() { + broadcastDispatcher.unregisterReceiver(receiver) + + if (!isShowing()) return + + super.dismiss() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 1b033e91b76b..17f7ccf0d967 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -17,7 +17,13 @@ package com.android.systemui.keyguard; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; +import android.app.ActivityTaskManager; import android.app.Service; import android.content.Intent; import android.os.Binder; @@ -26,8 +32,17 @@ import android.os.Debug; import android.os.IBinder; import android.os.PowerManager; import android.os.Process; +import android.os.RemoteException; +import android.os.SystemProperties; import android.os.Trace; import android.util.Log; +import android.util.Slog; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.IRemoteAnimationRunner; +import android.view.RemoteAnimationAdapter; +import android.view.RemoteAnimationDefinition; +import android.view.RemoteAnimationTarget; +import android.view.WindowManager; import android.view.WindowManagerPolicyConstants; import com.android.internal.policy.IKeyguardDismissCallback; @@ -43,6 +58,21 @@ public class KeyguardService extends Service { static final String TAG = "KeyguardService"; static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD; + /** + * Run Keyguard animation as remote animation in System UI instead of local animation in + * the server process. + * + * Note: Must be consistent with WindowManagerService. + */ + private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY = + "persist.wm.enable_remote_keyguard_animation"; + + /** + * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY + */ + private static boolean sEnableRemoteKeyguardAnimation = + SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false); + private final KeyguardViewMediator mKeyguardViewMediator; private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher; @@ -52,6 +82,21 @@ public class KeyguardService extends Service { super(); mKeyguardViewMediator = keyguardViewMediator; mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher; + + if (sEnableRemoteKeyguardAnimation) { + RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); + final RemoteAnimationAdapter exitAnimationAdapter = + new RemoteAnimationAdapter(mExitAnimationRunner, 0, 0); + definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, exitAnimationAdapter); + definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER, + exitAnimationAdapter); + final RemoteAnimationAdapter occludeAnimationAdapter = + new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0); + definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter); + definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, occludeAnimationAdapter); + ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay( + DEFAULT_DISPLAY, definition); + } } @Override @@ -76,6 +121,48 @@ public class KeyguardService extends Service { } } + private final IRemoteAnimationRunner.Stub mExitAnimationRunner = + new IRemoteAnimationRunner.Stub() { + @Override // Binder interface + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) { + Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation"); + checkPermission(); + mKeyguardViewMediator.startKeyguardExitAnimation(transit, apps, wallpapers, + null /* nonApps */, finishedCallback); + Trace.endSection(); + } + + @Override // Binder interface + public void onAnimationCancelled() { + } + }; + + private final IRemoteAnimationRunner.Stub mOccludeAnimationRunner = + new IRemoteAnimationRunner.Stub() { + @Override // Binder interface + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) { + // TODO(bc-unlock): Calls KeyguardViewMediator#setOccluded to update the state and + // run animation. + try { + finishedCallback.onAnimationFinished(); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException"); + } + } + + @Override // Binder interface + public void onAnimationCancelled() { + } + }; + private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() { @Override // Binder interface @@ -225,6 +312,11 @@ public class KeyguardService extends Service { mKeyguardViewMediator.onBootCompleted(); } + /** + * @deprecated When remote animation is enabled, this won't be called anymore. Use + * {@code IRemoteAnimationRunner#onAnimationStart} instead. + */ + @Deprecated @Override public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation"); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index e7326698e43e..5a918d4808d6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -27,6 +27,9 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE; import static com.android.systemui.DejankUtils.whitelistIpcs; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.AlarmManager; @@ -65,8 +68,13 @@ import android.util.EventLog; import android.util.Log; import android.util.Slog; import android.util.SparseIntArray; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.RemoteAnimationTarget; +import android.view.SyncRtSurfaceTransactionApplier; +import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import android.view.WindowManagerPolicyConstants; import android.view.animation.Animation; import android.view.animation.AnimationUtils; @@ -85,6 +93,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardViewController; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.Dumpable; +import com.android.systemui.Interpolators; import com.android.systemui.SystemUI; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; @@ -1318,6 +1327,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, if (mHiding && isOccluded) { // We're in the process of going away but WindowManager wants to show a // SHOW_WHEN_LOCKED activity instead. + // TODO(bc-unlock): Migrate to remote animation. startKeyguardExitAnimation(0, 0); } @@ -1703,7 +1713,9 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, Trace.beginSection( "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM"); StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj; - handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration); + handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration, + params.mApps, params.mWallpapers, params.mNonApps, + params.mFinishedCallback); mFalsingCollector.onSuccessfulUnlock(); Trace.endSection(); break; @@ -1990,15 +2002,19 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, if (mShowing && !mOccluded) { mKeyguardGoingAwayRunnable.run(); } else { + // TODO(bc-unlock): Fill parameters handleStartKeyguardExitAnimation( SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(), - mHideAnimation.getDuration()); + mHideAnimation.getDuration(), null /* apps */, null /* wallpapers */, + null /* nonApps */, null /* finishedCallback */); } } Trace.endSection(); } - private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) { + private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration, + RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) { Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation"); if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime + " fadeoutDuration=" + fadeoutDuration); @@ -2031,6 +2047,49 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, mWakeAndUnlocking = false; mDismissCallbackRegistry.notifyDismissSucceeded(); mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration); + + // TODO(bc-animation): When remote animation is enabled for keyguard exit animation, + // apps, wallpapers and finishedCallback are set to non-null. nonApps is not yet + // supported, so it's always null. + mContext.getMainExecutor().execute(() -> { + if (finishedCallback == null) { + return; + } + + // TODO(bc-unlock): Sample animation, just to apply alpha animation on the app. + final SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier( + mKeyguardViewControllerLazy.get().getViewRootImpl().getView()); + final RemoteAnimationTarget primary = apps[0]; + ValueAnimator anim = ValueAnimator.ofFloat(0, 1); + anim.setDuration(400 /* duration */); + anim.setInterpolator(Interpolators.LINEAR); + anim.addUpdateListener((ValueAnimator animation) -> { + SurfaceParams params = new SurfaceParams.Builder(primary.leash) + .withAlpha(animation.getAnimatedFraction()) + .build(); + applier.scheduleApply(params); + }); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + try { + finishedCallback.onAnimationFinished(); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException"); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + try { + finishedCallback.onAnimationFinished(); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException"); + } + } + }); + anim.start(); + }); resetKeyguardDonePendingLocked(); mHideAnimationRun = false; adjustStatusBarLocked(); @@ -2223,10 +2282,55 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, return mKeyguardViewControllerLazy.get(); } + /** + * Notifies to System UI that the activity behind has now been drawn and it's safe to remove + * the wallpaper and keyguard flag, and WindowManager has started running keyguard exit + * animation. + * + * @param startTime the start time of the animation in uptime milliseconds. Deprecated. + * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated. + * @deprecated Will be migrate to remote animation soon. + */ + @Deprecated public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { + startKeyguardExitAnimation(0, startTime, fadeoutDuration, null, null, null, null); + } + + /** + * Notifies to System UI that the activity behind has now been drawn and it's safe to remove + * the wallpaper and keyguard flag, and System UI should start running keyguard exit animation. + * + * @param apps The list of apps to animate. + * @param wallpapers The list of wallpapers to animate. + * @param nonApps The list of non-app windows such as Bubbles to animate. + * @param finishedCallback The callback to invoke when the animation is finished. + */ + public void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit, + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) { + startKeyguardExitAnimation(transit, 0, 0, apps, wallpapers, nonApps, finishedCallback); + } + + /** + * Notifies to System UI that the activity behind has now been drawn and it's safe to remove + * the wallpaper and keyguard flag, and start running keyguard exit animation. + * + * @param startTime the start time of the animation in uptime milliseconds. Deprecated. + * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated. + * @param apps The list of apps to animate. + * @param wallpapers The list of wallpapers to animate. + * @param nonApps The list of non-app windows such as Bubbles to animate. + * @param finishedCallback The callback to invoke when the animation is finished. + */ + private void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit, + long startTime, long fadeoutDuration, + RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) { Trace.beginSection("KeyguardViewMediator#startKeyguardExitAnimation"); Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM, - new StartKeyguardExitAnimParams(startTime, fadeoutDuration)); + new StartKeyguardExitAnimParams(transit, startTime, fadeoutDuration, apps, + wallpapers, nonApps, finishedCallback)); mHandler.sendMessage(msg); Trace.endSection(); } @@ -2300,12 +2404,26 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, private static class StartKeyguardExitAnimParams { + @WindowManager.TransitionOldType int mTransit; long startTime; long fadeoutDuration; - - private StartKeyguardExitAnimParams(long startTime, long fadeoutDuration) { + RemoteAnimationTarget[] mApps; + RemoteAnimationTarget[] mWallpapers; + RemoteAnimationTarget[] mNonApps; + IRemoteAnimationFinishedCallback mFinishedCallback; + + private StartKeyguardExitAnimParams(@WindowManager.TransitionOldType int transit, + long startTime, long fadeoutDuration, + RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) { + this.mTransit = transit; this.startTime = startTime; this.fadeoutDuration = fadeoutDuration; + this.mApps = apps; + this.mWallpapers = wallpapers; + this.mNonApps = nonApps; + this.mFinishedCallback = finishedCallback; } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java index 453e85ae25c7..517f8e7f0dac 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java @@ -51,7 +51,7 @@ public class ContextualButton extends ButtonDispatcher { * Reload the drawable from resource id, should reapply the previous dark intensity. */ public void updateIcon(int lightIconColor, int darkIconColor) { - if (getCurrentView() == null || !getCurrentView().isAttachedToWindow() || mIconResId == 0) { + if (mIconResId == 0) { return; } final KeyButtonDrawable currentDrawable = getImageDrawable(); diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index a4e91896be9e..580cbcf8ce47 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -24,10 +24,8 @@ import android.app.INotificationManager; import android.app.people.IPeopleManager; import android.app.people.PeopleSpaceTile; import android.appwidget.AppWidgetManager; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.os.Bundle; @@ -36,12 +34,9 @@ import android.provider.Settings; import android.util.Log; import android.view.ViewGroup; -import androidx.preference.PreferenceManager; - import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.R; -import com.android.systemui.people.widget.PeopleSpaceWidgetProvider; import java.util.List; @@ -128,26 +123,17 @@ public class PeopleSpaceActivity extends Activity { /** Stores the user selected configuration for {@code mAppWidgetId}. */ private void storeWidgetConfiguration(PeopleSpaceTile tile) { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); - SharedPreferences.Editor editor = sp.edit(); if (PeopleSpaceUtils.DEBUG) { Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: " + tile.getId() + " for widget ID: " + mAppWidgetId); } - // Ensure updates to app widget can be retrieved from both appWidget Id and tile ID. - editor.putString(String.valueOf(mAppWidgetId), tile.getId()); - editor.putInt(tile.getId(), mAppWidgetId); - editor.apply(); - AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); - Bundle options = new Bundle(); - options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, tile); - appWidgetManager.updateAppWidgetOptions(mAppWidgetId, options); - int[] widgetIds = appWidgetManager.getAppWidgetIds( - new ComponentName(mContext, PeopleSpaceWidgetProvider.class)); + + PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId); + int[] widgetIds = new int[mAppWidgetId]; // TODO: Populate new widget with existing conversation notification, if there is any. PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager, - mNotificationManager); + mPeopleManager); finishActivity(); } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index 91e7968f7546..994dc6d78507 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -18,6 +18,7 @@ package com.android.systemui.people; import static android.app.Notification.EXTRA_MESSAGES; +import android.annotation.Nullable; import android.app.INotificationManager; import android.app.Notification; import android.app.PendingIntent; @@ -70,11 +71,13 @@ import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -89,6 +92,13 @@ public class PeopleSpaceUtils { private static final int MIN_HOUR = 1; private static final int ONE_DAY = 1; public static final String OPTIONS_PEOPLE_SPACE_TILE = "options_people_space_tile"; + public static final String PACKAGE_NAME = "package_name"; + public static final String USER_ID = "user_id"; + public static final String SHORTCUT_ID = "shortcut_id"; + + public static final String EMPTY_STRING = ""; + public static final int INVALID_WIDGET_ID = -1; + public static final int INVALID_USER_ID = -1; private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+"); private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+"); @@ -171,70 +181,174 @@ public class PeopleSpaceUtils { * notification being posted or removed. */ public static void updateSingleConversationWidgets(Context context, int[] appWidgetIds, - AppWidgetManager appWidgetManager, INotificationManager notificationManager) { - IPeopleManager peopleManager = IPeopleManager.Stub.asInterface( - ServiceManager.getService(Context.PEOPLE_SERVICE)); - LauncherApps launcherApps = context.getSystemService(LauncherApps.class); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + AppWidgetManager appWidgetManager, IPeopleManager peopleManager) { Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>(); - try { - List<PeopleSpaceTile> tiles = - PeopleSpaceUtils.getTiles(context, notificationManager, - peopleManager, launcherApps); - for (int appWidgetId : appWidgetIds) { - String shortcutId = sp.getString(String.valueOf(appWidgetId), null); - if (DEBUG) { - Log.d(TAG, "Widget ID: " + appWidgetId + " Shortcut ID: " + shortcutId); - } + for (int appWidgetId : appWidgetIds) { + PeopleSpaceTile tile = getPeopleSpaceTile(peopleManager, appWidgetManager, context, + appWidgetId); + if (tile == null) { + if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID"); + //TODO: Delete app widget id when crash is fixed (b/172932636) + continue; + } - Optional<PeopleSpaceTile> entry = tiles.stream().filter( - e -> e.getId().equals(shortcutId)).findFirst(); + if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName()); + RemoteViews views = createRemoteViews(context, tile, appWidgetId); - if (!entry.isPresent() || shortcutId == null) { - if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID"); - //TODO: Delete app widget id when crash is fixed (b/175486868) - continue; - } - // Augment current tile based on stored fields. - PeopleSpaceTile tile = augmentTileFromStorage(entry.get(), appWidgetManager, - appWidgetId); + // Tell the AppWidgetManager to perform an update on the current app widget. + appWidgetManager.updateAppWidget(appWidgetId, views); + + widgetIdToTile.put(appWidgetId, tile); + } + getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds); + } - RemoteViews views = createRemoteViews(context, tile, appWidgetId); + @Nullable + private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager, + AppWidgetManager appWidgetManager, + Context context, int appWidgetId) { + try { + // Migrate storage for existing users. + SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId), + Context.MODE_PRIVATE); + String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING); + int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID); + String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING); + if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + shortcutId = sp.getString(String.valueOf(appWidgetId), null); + if (shortcutId == null) { + Log.e(TAG, "Cannot restore widget"); + return null; + } + migrateExistingUsersToNewStorage(context, shortcutId, appWidgetId); + pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING); + userId = widgetSp.getInt(USER_ID, INVALID_USER_ID); + } - // Tell the AppWidgetManager to perform an update on the current app widget. - appWidgetManager.updateAppWidget(appWidgetId, views); + // Check if tile is cached. + Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); + PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + if (tile != null) { + return tile; + } - widgetIdToTile.put(appWidgetId, tile); + // If tile is null, we need to retrieve from persisted storage. + if (DEBUG) Log.d(TAG, "Retrieving from storage after reboots"); + LauncherApps launcherApps = context.getSystemService(LauncherApps.class); + ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId); + if (channel == null) { + Log.d(TAG, "Could not retrieve conversation from storage"); + return null; } + return new PeopleSpaceTile.Builder(channel, launcherApps).build(); } catch (Exception e) { - Log.e(TAG, "Failed to retrieve conversations to set tiles: " + e); + Log.e(TAG, "Failed to retrieve conversation for tile: " + e); + return null; } - getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds); } - /** Augment {@link PeopleSpaceTile} with fields from stored tile. */ - @VisibleForTesting - static PeopleSpaceTile augmentTileFromStorage(PeopleSpaceTile tile, - AppWidgetManager appWidgetManager, int appWidgetId) { - Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); - PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); - if (storedTile == null) { - return tile; + /** Best-effort attempts to migrate existing users to the new storage format. */ + // TODO: Remove after sufficient time. Temporary migration storage for existing users. + private static void migrateExistingUsersToNewStorage(Context context, String shortcutId, + int appWidgetId) { + try { + List<PeopleSpaceTile> tiles = + PeopleSpaceUtils.getTiles(context, INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)), + IPeopleManager.Stub.asInterface( + ServiceManager.getService(Context.PEOPLE_SERVICE)), + context.getSystemService(LauncherApps.class)); + Optional<PeopleSpaceTile> entry = tiles.stream().filter( + e -> e.getId().equals(shortcutId)).findFirst(); + if (entry.isPresent()) { + if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName()); + setStorageForTile(context, entry.get(), appWidgetId); + } else { + Log.e(TAG, "Could not migrate user"); + } + } catch (Exception e) { + Log.e(TAG, "Could not query conversations"); } - return tile.toBuilder() - .setBirthdayText(storedTile.getBirthdayText()) - .setNotificationKey(storedTile.getNotificationKey()) - .setNotificationContent(storedTile.getNotificationContent()) - .setNotificationDataUri(storedTile.getNotificationDataUri()) - .build(); } - /** If incoming notification changed tile, store the changes in the tile options. */ - public static void storeNotificationChange(StatusBarNotification sbn, + /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */ + public static void setStorageForTile(Context context, PeopleSpaceTile tile, int appWidgetId) { + // Write relevant persisted storage. + SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId), + Context.MODE_PRIVATE); + SharedPreferences.Editor widgetEditor = widgetSp.edit(); + widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName()); + widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId()); + int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier(); + widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId); + widgetEditor.apply(); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = sp.edit(); + editor.putString(String.valueOf(appWidgetId), tile.getId()); + String key = PeopleSpaceUtils.getKey(tile.getId(), tile.getPackageName(), userId); + // Don't overwrite existing widgets with the same key. + Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>())); + storedWidgetIds.add(String.valueOf(appWidgetId)); + editor.putStringSet(key, storedWidgetIds); + editor.apply(); + + // Write cached storage. + updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId, + tile); + } + + /** Removes stored data when tile is deleted. */ + public static void removeStorageForTile(Context context, int widgetId) { + SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId), + Context.MODE_PRIVATE); + String packageName = widgetSp.getString(PACKAGE_NAME, null); + String shortcutId = widgetSp.getString(SHORTCUT_ID, null); + int userId = widgetSp.getInt(USER_ID, -1); + + // Delete widgetId mapping to key. + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = sp.edit(); + String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId); + Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>())); + storedWidgetIds.remove(String.valueOf(widgetId)); + editor.putStringSet(key, storedWidgetIds); + editor.remove(String.valueOf(widgetId)); + editor.apply(); + + // Delete all data specifically mapped to widgetId. + SharedPreferences.Editor widgetEditor = widgetSp.edit(); + widgetEditor.remove(PACKAGE_NAME); + widgetEditor.remove(USER_ID); + widgetEditor.remove(SHORTCUT_ID); + widgetEditor.apply(); + } + + /** + * Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the + * requested update data. + */ + public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId, + String packageName, int userId) { + SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId), + Context.MODE_PRIVATE); + String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING); + int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID); + String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING); + return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId) + && storedUserId == userId; + } + + /** + * If incoming notification changed tile, store the changes in the tile options. + */ + public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager, + Context context, + StatusBarNotification sbn, NotificationAction notificationAction, AppWidgetManager appWidgetManager, int appWidgetId) { - Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); - PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context, + appWidgetId); if (storedTile == null) { if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to"); return; @@ -263,7 +377,7 @@ public class PeopleSpaceUtils { .setNotificationDataUri(null) .build(); } - updateAppWidgetOptions(appWidgetManager, appWidgetId, storedTile); + updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile); } private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId, @@ -677,4 +791,23 @@ public class PeopleSpaceUtils { } return lookupKeysWithBirthdaysToday; } + + /** + * Returns the uniquely identifying key for the conversation. + * + * <p>{@code userId} will always be a number, so we put user ID as the + * delimiter between the app-provided strings of shortcut ID and package name. + * + * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring + * a {@code packageName} to always start with a letter. This restriction means we are + * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first + * case is impossible given the package name restrictions: + * <ul> + * <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li> + * <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li> + * </ul> + */ + public static String getKey(String shortcutId, String packageName, int userId) { + return shortcutId + "/" + userId + "/" + packageName; + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index ad6046ba489d..bee54e4dca0a 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -16,8 +16,8 @@ package com.android.systemui.people.widget; -import android.app.INotificationManager; import android.app.NotificationChannel; +import android.app.people.IPeopleManager; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.Context; @@ -29,6 +29,7 @@ import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.Log; +import android.widget.RemoteViews; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.appwidget.IAppWidgetService; @@ -37,7 +38,8 @@ import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; -import java.util.Objects; +import java.util.HashSet; +import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; @@ -51,7 +53,7 @@ public class PeopleSpaceWidgetManager { private final Context mContext; private IAppWidgetService mAppWidgetService; private AppWidgetManager mAppWidgetManager; - private INotificationManager mNotificationManager; + private IPeopleManager mPeopleManager; @Inject public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) { @@ -59,11 +61,13 @@ public class PeopleSpaceWidgetManager { mContext = context; mAppWidgetService = appWidgetService; mAppWidgetManager = AppWidgetManager.getInstance(context); - mNotificationManager = INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + mPeopleManager = IPeopleManager.Stub.asInterface( + ServiceManager.getService(Context.PEOPLE_SERVICE)); } - /** Constructor used for testing. */ + /** + * Constructor used for testing. + */ @VisibleForTesting protected PeopleSpaceWidgetManager(Context context) { if (DEBUG) Log.d(TAG, "constructor"); @@ -72,16 +76,20 @@ public class PeopleSpaceWidgetManager { ServiceManager.getService(Context.APPWIDGET_SERVICE)); } - /** AppWidgetManager setter used for testing. */ + /** + * AppWidgetManager setter used for testing. + */ @VisibleForTesting protected void setAppWidgetManager(IAppWidgetService appWidgetService, - AppWidgetManager appWidgetManager, INotificationManager notificationManager) { + AppWidgetManager appWidgetManager, IPeopleManager peopleManager) { mAppWidgetService = appWidgetService; mAppWidgetManager = appWidgetManager; - mNotificationManager = notificationManager; + mPeopleManager = peopleManager; } - /** Updates People Space widgets. */ + /** + * Updates People Space widgets. + */ public void updateWidgets() { try { if (DEBUG) Log.d(TAG, "updateWidgets called"); @@ -99,7 +107,7 @@ public class PeopleSpaceWidgetManager { if (showSingleConversation) { PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, - mAppWidgetManager, mNotificationManager); + mAppWidgetManager, mPeopleManager); } else { mAppWidgetService .notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds, @@ -114,30 +122,38 @@ public class PeopleSpaceWidgetManager { * Check if any existing People tiles match the incoming notification change, and store the * change in the tile if so. */ - public void storeNotificationChange(StatusBarNotification sbn, + public void updateWidgetWithNotificationChanged(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction) { - if (DEBUG) Log.d(TAG, "storeNotificationChange called"); + RemoteViews views = new RemoteViews( + mContext.getPackageName(), R.layout.people_space_small_avatar_tile); + if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called"); boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; if (!showSingleConversation) { return; } try { + String sbnShortcutId = sbn.getShortcutId(); + if (sbnShortcutId == null) { + return; + } int[] widgetIds = mAppWidgetService.getAppWidgetIds( new ComponentName(mContext, PeopleSpaceWidgetProvider.class) ); if (widgetIds.length == 0) { return; } - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); - for (int widgetId : widgetIds) { - String shortcutId = sp.getString(String.valueOf(widgetId), null); - if (!Objects.equals(sbn.getShortcutId(), shortcutId)) { - continue; - } + int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier(); + String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId); + Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>())); + if (storedWidgetIds.isEmpty()) { + return; + } + for (String widgetIdString : storedWidgetIds) { + int widgetId = Integer.parseInt(widgetIdString); if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey()); - PeopleSpaceUtils.storeNotificationChange( + PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext, sbn, notificationAction, mAppWidgetManager, widgetId); } } catch (Exception e) { @@ -159,8 +175,7 @@ public class PeopleSpaceWidgetManager { public void onNotificationPosted( StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) { if (DEBUG) Log.d(TAG, "onNotificationPosted"); - storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.POSTED); - updateWidgets(); + updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED); } @Override @@ -169,8 +184,7 @@ public class PeopleSpaceWidgetManager { NotificationListenerService.RankingMap rankingMap ) { if (DEBUG) Log.d(TAG, "onNotificationRemoved"); - storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); - updateWidgets(); + updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); } @Override @@ -179,8 +193,7 @@ public class PeopleSpaceWidgetManager { NotificationListenerService.RankingMap rankingMap, int reason) { if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason); - storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); - updateWidgets(); + updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); } @Override @@ -207,4 +220,4 @@ public class PeopleSpaceWidgetManager { } }; -} +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java index 7f204cc68c54..f5577d30c75c 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java @@ -16,8 +16,8 @@ package com.android.systemui.people.widget; -import android.app.INotificationManager; import android.app.PendingIntent; +import android.app.people.IPeopleManager; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; @@ -53,8 +53,8 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; if (showSingleConversation) { PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds, - appWidgetManager, INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE))); + appWidgetManager, IPeopleManager.Stub.asInterface( + ServiceManager.getService(Context.PEOPLE_SERVICE))); return; } // Perform this loop procedure for each App Widget that belongs to this provider @@ -91,6 +91,7 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { for (int widgetId : appWidgetIds) { if (DEBUG) Log.d(TAG, "Widget removed"); mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED); + PeopleSpaceUtils.removeStorageForTile(context, widgetId); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java index 9ab2d7370ed8..35a8257bd5a7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java @@ -16,6 +16,9 @@ package com.android.systemui.qs.dagger; +import android.content.Context; +import android.hardware.display.ColorDisplayManager; + import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.FeatureFlags; @@ -27,6 +30,7 @@ import dagger.Provides; @Module public interface QSFlagsModule { String QS_LABELS_FLAG = "qs_labels_flag"; + String RBC_AVAILABLE = "rbc_available"; @Provides @SysUISingleton @@ -34,4 +38,12 @@ public interface QSFlagsModule { static boolean provideQSFlag(FeatureFlags featureFlags) { return featureFlags.isQSLabelsEnabled(); } + + /** */ + @Provides + @SysUISingleton + @Named(RBC_AVAILABLE) + static boolean isReduceBrightColorsAvailable(Context context) { + return ColorDisplayManager.isReduceBrightColorsAvailable(context); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 6e28cd89d43a..56d06eb1b474 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -315,7 +315,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener } try { if (DEBUG) Log.d(TAG, "Adding token"); - mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY); + mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY, + null /* options */); mIsTokenGranted = true; } catch (RemoteException e) { } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java index 84c7611478cd..f94cabcee297 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java @@ -16,12 +16,13 @@ package com.android.systemui.qs.tiles; +import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE; + import android.content.Intent; import android.os.Handler; import android.os.Looper; import android.provider.Settings; import android.service.quicksettings.Tile; -import android.text.TextUtils; import android.widget.Switch; import com.android.internal.logging.MetricsLogger; @@ -39,6 +40,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.SecureSettings; import javax.inject.Inject; +import javax.inject.Named; /** Quick settings tile: Reduce Bright Colors **/ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> { @@ -46,9 +48,11 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> { //TODO(b/170973645): get icon drawable private final Icon mIcon = null; private final SecureSetting mActivatedSetting; + private final boolean mIsAvailable; @Inject public ReduceBrightColorsTile( + @Named(RBC_AVAILABLE) boolean isAvailable, QSHost host, @Background Looper backgroundLooper, @Main Handler mainHandler, @@ -69,11 +73,12 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> { refreshState(); } }; + mIsAvailable = isAvailable; + } @Override public boolean isAvailable() { - // TODO(b/170970675): Call into ColorDisplayService to get availability/config status - return true; + return mIsAvailable; } @Override @@ -121,15 +126,6 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> { state.label = mContext.getString(R.string.quick_settings_reduce_bright_colors_label); state.expandedAccessibilityClassName = Switch.class.getName(); state.contentDescription = state.label; - - final int intensity = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mActivatedSetting.getCurrentUser()); - state.secondaryLabel = state.value ? mContext.getString( - R.string.quick_settings_reduce_bright_colors_secondary_label, intensity) : ""; - - state.contentDescription = TextUtils.isEmpty(state.secondaryLabel) - ? state.label - : TextUtils.concat(state.label, ", ", state.secondaryLabel); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index 9037192e2ddc..543874325254 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -187,7 +187,7 @@ public class ScreenMediaRecorder { * @param refreshRate Desired refresh rate * @return array with supported width, height, and refresh rate */ - private int[] getSupportedSize(int screenWidth, int screenHeight, int refreshRate) { + private int[] getSupportedSize(final int screenWidth, final int screenHeight, int refreshRate) { double maxScale = 0; MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); @@ -207,25 +207,33 @@ public class ScreenMediaRecorder { int width = vc.getSupportedWidths().getUpper(); int height = vc.getSupportedHeights().getUpper(); - if (width >= screenWidth && height >= screenHeight - && vc.isSizeSupported(screenWidth, screenHeight)) { + int screenWidthAligned = screenWidth; + if (screenWidthAligned % vc.getWidthAlignment() != 0) { + screenWidthAligned -= (screenWidthAligned % vc.getWidthAlignment()); + } + int screenHeightAligned = screenHeight; + if (screenHeightAligned % vc.getHeightAlignment() != 0) { + screenHeightAligned -= (screenHeightAligned % vc.getHeightAlignment()); + } + if (width >= screenWidthAligned && height >= screenHeightAligned + && vc.isSizeSupported(screenWidthAligned, screenHeightAligned)) { // Desired size is supported, now get the rate - int maxRate = vc.getSupportedFrameRatesFor(screenWidth, screenHeight) - .getUpper().intValue(); + int maxRate = vc.getSupportedFrameRatesFor(screenWidthAligned, + screenHeightAligned).getUpper().intValue(); if (maxRate < refreshRate) { refreshRate = maxRate; } Log.d(TAG, "Screen size supported at rate " + refreshRate); - return new int[]{screenWidth, screenHeight, refreshRate}; + return new int[]{screenWidthAligned, screenHeightAligned, refreshRate}; } // Otherwise, continue searching double scale = Math.min(((double) width / screenWidth), ((double) height / screenHeight)); if (scale > maxScale) { - maxScale = scale; + maxScale = Math.min(1, scale); maxInfo = vc; } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java index 8e182b415488..c8afd0b6cfe9 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java @@ -35,7 +35,7 @@ import com.android.systemui.R; * cropped out. */ public class CropView extends View { - private enum CropBoundary { + public enum CropBoundary { NONE, TOP, BOTTOM } @@ -48,8 +48,14 @@ public class CropView extends View { private float mTopCrop = 0f; private float mBottomCrop = 1f; + // When the user is dragging a handle, these variables store the distance between the top/bottom + // crop values and + private float mTopDelta = 0f; + private float mBottomDelta = 0f; + private CropBoundary mCurrentDraggingBoundary = CropBoundary.NONE; - private float mLastY; + private float mStartingY; // y coordinate of ACTION_DOWN + private CropInteractionListener mCropInteractionListener; public CropView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); @@ -73,54 +79,84 @@ public class CropView extends View { @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); - drawShade(canvas, 0, mTopCrop); - drawShade(canvas, mBottomCrop, 1f); - drawHandle(canvas, mTopCrop); - drawHandle(canvas, mBottomCrop); + float top = mTopCrop + mTopDelta; + float bottom = mBottomCrop + mBottomDelta; + drawShade(canvas, 0, top); + drawShade(canvas, bottom, 1f); + drawHandle(canvas, top); + drawHandle(canvas, bottom); } @Override public boolean onTouchEvent(MotionEvent event) { int topPx = fractionToPixels(mTopCrop); int bottomPx = fractionToPixels(mBottomCrop); - if (event.getAction() == MotionEvent.ACTION_DOWN) { - mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx); - if (mCurrentDraggingBoundary != CropBoundary.NONE) { - mLastY = event.getY(); - } - return true; - } - if (event.getAction() == MotionEvent.ACTION_MOVE - && mCurrentDraggingBoundary != CropBoundary.NONE) { - float delta = event.getY() - mLastY; - if (mCurrentDraggingBoundary == CropBoundary.TOP) { - mTopCrop = pixelsToFraction((int) MathUtils.constrain(topPx + delta, 0, - bottomPx - 2 * mCropTouchMargin)); - } else { // Bottom - mBottomCrop = pixelsToFraction((int) MathUtils.constrain(bottomPx + delta, - topPx + 2 * mCropTouchMargin, getMeasuredHeight())); - } - mLastY = event.getY(); - invalidate(); - return true; + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx); + if (mCurrentDraggingBoundary != CropBoundary.NONE) { + mStartingY = event.getY(); + updateListener(event); + } + return true; + case MotionEvent.ACTION_MOVE: + if (mCurrentDraggingBoundary != CropBoundary.NONE) { + float delta = event.getY() - mStartingY; + if (mCurrentDraggingBoundary == CropBoundary.TOP) { + mTopDelta = pixelsToFraction((int) MathUtils.constrain(delta, -topPx, + bottomPx - 2 * mCropTouchMargin - topPx)); + } else { // Bottom + mBottomDelta = pixelsToFraction((int) MathUtils.constrain(delta, + topPx + 2 * mCropTouchMargin - bottomPx, + getMeasuredHeight() - bottomPx)); + } + updateListener(event); + invalidate(); + return true; + } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + if (mCurrentDraggingBoundary != CropBoundary.NONE) { + // Commit the delta to the stored crop values. + mTopCrop += mTopDelta; + mBottomCrop += mBottomDelta; + mTopDelta = 0; + mBottomDelta = 0; + updateListener(event); + } } return super.onTouchEvent(event); } /** - * @return value [0,1] representing the position of the top crop boundary. + * @return value [0,1] representing the position of the top crop boundary. Does not reflect + * changes from any in-progress touch input. */ public float getTopBoundary() { return mTopCrop; } /** - * @return value [0,1] representing the position of the bottom crop boundary. + * @return value [0,1] representing the position of the bottom crop boundary. Does not reflect + * changes from any in-progress touch input. */ public float getBottomBoundary() { return mBottomCrop; } + public void setCropInteractionListener(CropInteractionListener listener) { + mCropInteractionListener = listener; + } + + private void updateListener(MotionEvent event) { + if (mCropInteractionListener != null) { + float boundaryPosition = (mCurrentDraggingBoundary == CropBoundary.TOP) + ? mTopCrop + mTopDelta : mBottomCrop + mBottomDelta; + mCropInteractionListener.onCropMotionEvent(event, mCurrentDraggingBoundary, + boundaryPosition, fractionToPixels(boundaryPosition)); + } + } + private void drawShade(Canvas canvas, float fracStart, float fracEnd) { canvas.drawRect(0, fractionToPixels(fracStart), getMeasuredWidth(), fractionToPixels(fracEnd), mShadePaint); @@ -148,4 +184,17 @@ public class CropView extends View { } return CropBoundary.NONE; } + + /** + * Listen for crop motion events and state. + */ + public interface CropInteractionListener { + /** + * Called whenever CropView has a MotionEvent that can impact the position of the crop + * boundaries. + */ + void onCropMotionEvent(MotionEvent event, CropBoundary boundary, float boundaryPosition, + int boundaryPositionPx); + + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java new file mode 100644 index 000000000000..f88715164bc7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.screenshot; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import androidx.annotation.Nullable; + +import com.android.systemui.R; + +/** + * MagnifierView shows a full-res cropped circular display of a given ImageTileSet, contents and + * positioning dereived from events from a CropView to which it listens. + * + * Not meant to be a general-purpose magnifier! + */ +public class MagnifierView extends View implements CropView.CropInteractionListener { + private Drawable mDrawable; + + private final Paint mShadePaint; + private final Paint mHandlePaint; + + private Path mOuterCircle; + private Path mInnerCircle; + + private Path mCheckerboard; + private Paint mCheckerboardPaint; + private final float mBorderPx; + private final int mBorderColor; + private float mCheckerboardBoxSize = 40; + + private float mLastCropPosition; + private CropView.CropBoundary mCropBoundary; + + public MagnifierView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public MagnifierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + TypedArray t = context.getTheme().obtainStyledAttributes( + attrs, R.styleable.MagnifierView, 0, 0); + mShadePaint = new Paint(); + mShadePaint.setColor(t.getColor(R.styleable.MagnifierView_scrimColor, Color.TRANSPARENT)); + mHandlePaint = new Paint(); + mHandlePaint.setColor(t.getColor(R.styleable.MagnifierView_handleColor, Color.BLACK)); + mHandlePaint.setStrokeWidth( + t.getDimensionPixelSize(R.styleable.MagnifierView_handleThickness, 20)); + mBorderPx = t.getDimensionPixelSize(R.styleable.MagnifierView_borderThickness, 0); + mBorderColor = t.getColor(R.styleable.MagnifierView_borderColor, Color.WHITE); + t.recycle(); + mCheckerboardPaint = new Paint(); + mCheckerboardPaint.setColor(Color.GRAY); + } + + public void setImageTileset(ImageTileSet tiles) { + if (tiles != null) { + mDrawable = tiles.getDrawable(); + mDrawable.setBounds(0, 0, tiles.getWidth(), tiles.getHeight()); + } else { + mDrawable = null; + } + invalidate(); + } + + @Override + public void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + int radius = getWidth() / 2; + mOuterCircle = new Path(); + mOuterCircle.addCircle(radius, radius, radius, Path.Direction.CW); + mInnerCircle = new Path(); + mInnerCircle.addCircle(radius, radius, radius - mBorderPx, Path.Direction.CW); + mCheckerboard = generateCheckerboard(); + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + + // TODO: just draw a circle at the end instead of clipping like this? + canvas.clipPath(mOuterCircle); + canvas.drawColor(mBorderColor); + canvas.clipPath(mInnerCircle); + + // Draw a checkerboard pattern for out of bounds. + canvas.drawPath(mCheckerboard, mCheckerboardPaint); + + if (mDrawable != null) { + canvas.save(); + // Translate such that the center of this view represents the center of the crop + // boundary. + canvas.translate(-mDrawable.getBounds().width() / 2 + getWidth() / 2, + -mDrawable.getBounds().height() * mLastCropPosition + getHeight() / 2); + mDrawable.draw(canvas); + canvas.restore(); + } + + Rect scrimRect = new Rect(0, 0, getWidth(), getHeight() / 2); + if (mCropBoundary == CropView.CropBoundary.BOTTOM) { + scrimRect.offset(0, getHeight() / 2); + } + canvas.drawRect(scrimRect, mShadePaint); + + canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, mHandlePaint); + } + + @Override + public void onCropMotionEvent(MotionEvent event, CropView.CropBoundary boundary, + float cropPosition, int cropPositionPx) { + mCropBoundary = boundary; + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mLastCropPosition = cropPosition; + setTranslationY(cropPositionPx - getHeight() / 2); + setPivotX(getWidth() / 2); + setPivotY(getHeight() / 2); + setScaleX(0.2f); + setScaleY(0.2f); + setAlpha(0f); + setTranslationX((getParentWidth() - getWidth()) / 2); + setVisibility(View.VISIBLE); + animate().alpha(1f).translationX(0).scaleX(1f).scaleY(1f).start(); + break; + case MotionEvent.ACTION_MOVE: + mLastCropPosition = cropPosition; + setTranslationY(cropPositionPx - getHeight() / 2); + invalidate(); + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + animate().alpha(0).translationX((getParentWidth() - getWidth()) / 2).scaleX(0.2f) + .scaleY(0.2f).withEndAction(() -> setVisibility(View.INVISIBLE)).start(); + break; + } + } + + private Path generateCheckerboard() { + Path path = new Path(); + int checkerWidth = (int) Math.ceil(getWidth() / mCheckerboardBoxSize); + int checkerHeight = (int) Math.ceil(getHeight() / mCheckerboardBoxSize); + + for (int row = 0; row < checkerHeight; row++) { + // Alternate starting on the first and second column; + int colStart = (row % 2 == 0) ? 0 : 1; + for (int col = colStart; col < checkerWidth; col += 2) { + path.addRect(col * mCheckerboardBoxSize, + row * mCheckerboardBoxSize, + (col + 1) * mCheckerboardBoxSize, + (row + 1) * mCheckerboardBoxSize, + Path.Direction.CW); + } + } + return path; + } + + private int getParentWidth() { + return ((View) getParent()).getWidth(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 18c379a4650f..25438a6f57ba 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -81,6 +81,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener private View mEdit; private View mShare; private CropView mCropView; + private MagnifierView mMagnifierView; public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor, Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) { @@ -120,13 +121,14 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener mEdit = findViewById(R.id.edit); mShare = findViewById(R.id.share); mCropView = findViewById(R.id.crop_view); + mMagnifierView = findViewById(R.id.magnifier); + mCropView.setCropInteractionListener(mMagnifierView); mSave.setOnClickListener(this::onClicked); mCancel.setOnClickListener(this::onClicked); mEdit.setOnClickListener(this::onClicked); mShare.setOnClickListener(this::onClicked); - //mPreview.setImageDrawable(mImageTileSet.getDrawable()); mConnection.start(this::startCapture); } @@ -164,6 +166,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener private void doFinish() { mPreview.setImageDrawable(null); + mMagnifierView.setImageTileset(null); mImageTileSet.clear(); mCallback.onFinish(); mWindow.getDecorView().getViewTreeObserver() @@ -273,6 +276,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener session.end(mCallback::onFinish); } else { mPreview.setImageDrawable(mImageTileSet.getDrawable()); + mMagnifierView.setImageTileset(mImageTileSet); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index e7b60c3a0d67..2dd85e9bb98d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -67,6 +67,10 @@ public class FeatureFlags { return mFlagReader.isEnabled(R.bool.flag_brightness_slider); } + public boolean useNewLockscreenAnimations() { + return mFlagReader.isEnabled(R.bool.flag_lockscreen_animations); + } + public boolean isPeopleTileEnabled() { return mFlagReader.isEnabled(R.bool.flag_conversations); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index 2f0f90d318eb..c1feacaba440 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -11,14 +11,10 @@ import android.graphics.PorterDuffColorFilter import android.graphics.PorterDuffXfermode import android.graphics.RadialGradient import android.graphics.Shader -import android.os.SystemProperties import android.util.AttributeSet import android.view.View import com.android.systemui.Interpolators -val enableLightReveal = - SystemProperties.getBoolean("persist.sysui.show_new_screen_on_transitions", false) - /** * Provides methods to modify the various properties of a [LightRevealScrim] to reveal between 0% to * 100% of the view(s) underneath the scrim. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index a816eccebe33..cfadcd7f01ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -388,7 +388,28 @@ public class NotificationRemoteInputManager implements Dumpable { */ public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input, PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo) { + return activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo, + null /* userMessageContent */, null /* authBypassCheck */); + } + /** + * Activates a given {@link RemoteInput} + * + * @param view The view of the action button or suggestion chip that was tapped. + * @param inputs The remote inputs that need to be sent to the app. + * @param input The remote input that needs to be activated. + * @param pendingIntent The pending intent to be sent to the app. + * @param editedSuggestionInfo The smart reply that should be inserted in the remote input, or + * {@code null} if the user is not editing a smart reply. + * @param userMessageContent User-entered text with which to initialize the remote input view. + * @param authBypassCheck Optional auth bypass check associated with this remote input + * activation. If {@code null}, we never bypass. + * @return Whether the {@link RemoteInput} was activated. + */ + public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input, + PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo, + @Nullable String userMessageContent, + @Nullable AuthBypassPredicate authBypassCheck) { ViewParent p = view.getParent(); RemoteInputView riv = null; ExpandableNotificationRow row = null; @@ -410,40 +431,9 @@ public class NotificationRemoteInputManager implements Dumpable { row.setUserExpanded(true); - if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) { - final int userId = pendingIntent.getCreatorUserHandle().getIdentifier(); - - final boolean isLockedManagedProfile = - mUserManager.getUserInfo(userId).isManagedProfile() - && mKeyguardManager.isDeviceLocked(userId); - - final boolean isParentUserLocked; - if (isLockedManagedProfile) { - final UserInfo profileParent = mUserManager.getProfileParent(userId); - isParentUserLocked = (profileParent != null) - && mKeyguardManager.isDeviceLocked(profileParent.id); - } else { - isParentUserLocked = false; - } - - if (mLockscreenUserManager.isLockscreenPublicMode(userId) - || mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { - // If the parent user is no longer locked, and the user to which the remote input - // is destined is a locked, managed profile, then onLockedWorkRemoteInput should be - // called to unlock it. - if (isLockedManagedProfile && !isParentUserLocked) { - mCallback.onLockedWorkRemoteInput(userId, row, view); - } else { - // Even if we don't have security we should go through this flow, otherwise - // we won't go to the shade. - mCallback.onLockedRemoteInput(row, view); - } - return true; - } - if (isLockedManagedProfile) { - mCallback.onLockedWorkRemoteInput(userId, row, view); - return true; - } + final boolean deferBouncer = authBypassCheck != null; + if (!deferBouncer && showBouncerForRemoteInput(view, pendingIntent, row)) { + return true; } if (riv != null && !riv.isAttachedToWindow()) { @@ -461,7 +451,10 @@ public class NotificationRemoteInputManager implements Dumpable { && !row.getPrivateLayout().getExpandedChild().isShown()) { // The expanded layout is selected, but it's not shown yet, let's wait on it to // show before we do the animation. - mCallback.onMakeExpandedVisibleForRemoteInput(row, view); + mCallback.onMakeExpandedVisibleForRemoteInput(row, view, deferBouncer, () -> { + activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo, + userMessageContent, authBypassCheck); + }); return true; } @@ -491,10 +484,62 @@ public class NotificationRemoteInputManager implements Dumpable { riv.setPendingIntent(pendingIntent); riv.setRemoteInput(inputs, input, editedSuggestionInfo); riv.focusAnimated(); + if (userMessageContent != null) { + riv.setEditTextContent(userMessageContent); + } + if (deferBouncer) { + final ExpandableNotificationRow finalRow = row; + riv.setBouncerChecker(() -> !authBypassCheck.canSendRemoteInputWithoutBouncer() + && showBouncerForRemoteInput(view, pendingIntent, finalRow)); + } return true; } + private boolean showBouncerForRemoteInput(View view, PendingIntent pendingIntent, + ExpandableNotificationRow row) { + if (mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) { + return false; + } + + final int userId = pendingIntent.getCreatorUserHandle().getIdentifier(); + + final boolean isLockedManagedProfile = + mUserManager.getUserInfo(userId).isManagedProfile() + && mKeyguardManager.isDeviceLocked(userId); + + final boolean isParentUserLocked; + if (isLockedManagedProfile) { + final UserInfo profileParent = mUserManager.getProfileParent(userId); + isParentUserLocked = (profileParent != null) + && mKeyguardManager.isDeviceLocked(profileParent.id); + } else { + isParentUserLocked = false; + } + + if ((mLockscreenUserManager.isLockscreenPublicMode(userId) + || mStatusBarStateController.getState() == StatusBarState.KEYGUARD)) { + // If the parent user is no longer locked, and the user to which the remote + // input + // is destined is a locked, managed profile, then onLockedWorkRemoteInput + // should be + // called to unlock it. + if (isLockedManagedProfile && !isParentUserLocked) { + mCallback.onLockedWorkRemoteInput(userId, row, view); + } else { + // Even if we don't have security we should go through this flow, otherwise + // we won't go to the shade. + mCallback.onLockedRemoteInput(row, view); + } + return true; + } + if (isLockedManagedProfile) { + mCallback.onLockedWorkRemoteInput(userId, row, view); + return true; + } + return false; + } + private RemoteInputView findRemoteInputView(View v) { if (v == null) { return null; @@ -807,8 +852,11 @@ public class NotificationRemoteInputManager implements Dumpable { * * @param row * @param clickedView + * @param deferBouncer + * @param runnable */ - void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView); + void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView, + boolean deferBouncer, Runnable runnable); /** * Return whether or not remote input should be handled for this view. @@ -845,4 +893,28 @@ public class NotificationRemoteInputManager implements Dumpable { */ boolean handleClick(); } + + /** + * Predicate that is associated with a specific {@link #activateRemoteInput(View, RemoteInput[], + * RemoteInput, PendingIntent, EditedSuggestionInfo, String, AuthBypassPredicate)} + * invocation that determines whether or not the bouncer can be bypassed when sending the + * RemoteInput. + */ + public interface AuthBypassPredicate { + /** + * Determines if the RemoteInput can be sent without the bouncer. Should be checked the + * same frame that the RemoteInput is to be sent. + */ + boolean canSendRemoteInputWithoutBouncer(); + } + + /** Shows the bouncer if necessary */ + public interface BouncerChecker { + /** + * Shows the bouncer if necessary in order to send a RemoteInput. + * + * @return {@code true} if the bouncer was shown, {@code false} otherwise + */ + boolean showBouncerIfNecessary(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java index 88b9c6cbddfd..d08f9736adf6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -33,6 +33,7 @@ import android.view.RemoteAnimationTarget; import android.view.SyncRtSurfaceTransactionApplier; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.View; +import android.view.WindowManager; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.policy.ScreenDecorationsUtils; @@ -159,8 +160,10 @@ public class ActivityLaunchAnimator { } @Override - public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets, + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + RemoteAnimationTarget[] remoteAnimationTargets, RemoteAnimationTarget[] remoteAnimationWallpaperTargets, + RemoteAnimationTarget[] remoteAnimationNonAppTargets, IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback) throws RemoteException { mMainExecutor.execute(() -> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java index 79648457c521..301c3726793a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java @@ -29,6 +29,30 @@ public class NotificationDecoratedCustomViewWrapper extends NotificationTemplate private View mWrappedView = null; + /** + * Determines if the standard template contains a custom view, injected by Notification.Builder + */ + public static boolean hasCustomView(View v) { + return getWrappedCustomView(v) != null; + } + + private static View getWrappedCustomView(View view) { + if (view == null) { + return null; + } + ViewGroup container = view.findViewById( + com.android.internal.R.id.notification_main_column); + if (container == null) { + return null; + } + Integer childIndex = (Integer) container.getTag( + com.android.internal.R.id.notification_custom_view_index_tag); + if (childIndex == null || childIndex == -1) { + return null; + } + return container.getChildAt(childIndex); + } + protected NotificationDecoratedCustomViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { super(ctx, view, row); @@ -36,13 +60,7 @@ public class NotificationDecoratedCustomViewWrapper extends NotificationTemplate @Override public void onContentUpdated(ExpandableNotificationRow row) { - ViewGroup container = mView.findViewById( - com.android.internal.R.id.notification_main_column); - Integer childIndex = (Integer) container.getTag( - com.android.internal.R.id.notification_custom_view_index_tag); - if (childIndex != null && childIndex != -1) { - mWrappedView = container.getChildAt(childIndex); - } + mWrappedView = getWrappedCustomView(mView); // Custom views will most likely use just white or black as their text color. // We need to scan through and replace these colors by Material NEXT colors. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 7b5c5f6dcf8f..b3d1a94beaa9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -80,6 +80,9 @@ public abstract class NotificationViewWrapper implements TransformableView { if (Notification.DecoratedCustomViewStyle.class.equals(style)) { return new NotificationDecoratedCustomViewWrapper(ctx, v, row); } + if (NotificationDecoratedCustomViewWrapper.hasCustomView(v)) { + return new NotificationDecoratedCustomViewWrapper(ctx, v, row); + } return new NotificationTemplateViewWrapper(ctx, v, row); } else if (v instanceof NotificationHeaderView) { return new NotificationHeaderViewWrapper(ctx, v, row); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index e6efba7ca28b..5d2203b57991 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -286,8 +286,7 @@ public class StackScrollAlgorithm { float currentYPosition = -algorithmState.scrollY; int childCount = algorithmState.visibleChildren.size(); for (int i = 0; i < childCount; i++) { - currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition, - false /* reverse */); + currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition); } } @@ -301,10 +300,6 @@ public class StackScrollAlgorithm { * @param currentYPosition The Y position of the current pass of the algorithm. For a forward * pass, this should be the top of the child; for a reverse pass, the * bottom of the child. - * @param reverse Whether we're laying out children in the reverse direction (Y - * positions - * decreasing) instead of the forward direction (Y positions - * increasing). * @return The Y position after laying out the child. This will be the {@code currentYPosition} * for the next call to this method, after adjusting for any gaps between children. */ @@ -312,8 +307,7 @@ public class StackScrollAlgorithm { int i, StackScrollAlgorithmState algorithmState, AmbientState ambientState, - float currentYPosition, - boolean reverse) { + float currentYPosition) { ExpandableView child = algorithmState.visibleChildren.get(i); ExpandableView previousChild = i > 0 ? algorithmState.visibleChildren.get(i - 1) : null; final boolean applyGapHeight = @@ -323,20 +317,12 @@ public class StackScrollAlgorithm { ExpandableViewState childViewState = child.getViewState(); childViewState.location = ExpandableViewState.LOCATION_UNKNOWN; - if (applyGapHeight && !reverse) { + if (applyGapHeight) { currentYPosition += mGapHeight; } - int childHeight = getMaxAllowedChildHeight(child); - if (reverse) { - childViewState.yTranslation = currentYPosition - - (childHeight + mPaddingBetweenElements); - if (currentYPosition <= 0) { - childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP; - } - } else { - childViewState.yTranslation = currentYPosition; - } + childViewState.yTranslation = currentYPosition; + boolean isFooterView = child instanceof FooterView; boolean isEmptyShadeView = child instanceof EmptyShadeView; @@ -362,16 +348,9 @@ public class StackScrollAlgorithm { clampPositionToShelf(child, childViewState, ambientState); } - if (reverse) { - currentYPosition = childViewState.yTranslation; - if (applyGapHeight) { - currentYPosition -= mGapHeight; - } - } else { - currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements; - if (currentYPosition <= 0) { - childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP; - } + currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements; + if (currentYPosition <= 0) { + childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP; } if (childViewState.location == ExpandableViewState.LOCATION_UNKNOWN) { Log.wtf(LOG_TAG, "Failed to assign location for child " + i); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 8c2fa3349e4a..85d8df8e6057 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -29,6 +29,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.doze.DozeScreenState; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.tuner.TunerService; @@ -54,6 +55,7 @@ public class DozeParameters implements TunerService.Tunable, private final AlwaysOnDisplayPolicy mAlwaysOnPolicy; private final Resources mResources; private final BatteryController mBatteryController; + private final FeatureFlags mFeatureFlags; private boolean mDozeAlwaysOn; private boolean mControlScreenOffAnimation; @@ -65,7 +67,8 @@ public class DozeParameters implements TunerService.Tunable, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy, PowerManager powerManager, BatteryController batteryController, - TunerService tunerService) { + TunerService tunerService, + FeatureFlags featureFlags) { mResources = resources; mAmbientDisplayConfiguration = ambientDisplayConfiguration; mAlwaysOnPolicy = alwaysOnDisplayPolicy; @@ -74,6 +77,7 @@ public class DozeParameters implements TunerService.Tunable, mControlScreenOffAnimation = !getDisplayNeedsBlanking(); mPowerManager = powerManager; mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation); + mFeatureFlags = featureFlags; tunerService.addTunable( this, @@ -200,8 +204,7 @@ public class DozeParameters implements TunerService.Tunable, * then abruptly showing AOD. */ public boolean shouldControlUnlockedScreenOff() { - return getAlwaysOn() && SystemProperties.getBoolean( - "persist.sysui.show_new_screen_on_transitions", false); + return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations(); } private boolean getBoolean(String propName, int resId) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 0a366c9bb380..dd1419f4ff42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -48,6 +48,7 @@ import android.os.Messenger; import android.os.RemoteException; import android.os.UserHandle; import android.provider.MediaStore; +import android.provider.Settings; import android.service.media.CameraPrewarmService; import android.telecom.TelecomManager; import android.text.TextUtils; @@ -60,6 +61,7 @@ import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; @@ -71,6 +73,10 @@ import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.assist.AssistManager; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.controls.dagger.ControlsComponent; +import com.android.systemui.controls.ui.ControlsDialog; +import com.android.systemui.controls.ui.ControlsUiController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.IntentButtonProvider; import com.android.systemui.plugins.IntentButtonProvider.IntentButton; @@ -117,11 +123,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private static final int DOZE_ANIMATION_STAGGER_DELAY = 48; private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250; + // TODO(b/179494051): May no longer be needed private final boolean mShowLeftAffordance; private final boolean mShowCameraAffordance; private KeyguardAffordanceView mRightAffordanceView; private KeyguardAffordanceView mLeftAffordanceView; + private ImageView mAltLeftButton; private ViewGroup mIndicationArea; private TextView mEnterpriseDisclosure; private TextView mIndicationText; @@ -171,6 +179,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private int mBurnInYOffset; private ActivityIntentHelper mActivityIntentHelper; + private ControlsDialog mControlsDialog; + private ControlsComponent mControlsComponent; + private int mLockScreenMode; + private BroadcastDispatcher mBroadcastDispatcher; + public KeyguardBottomAreaView(Context context) { this(context, null); } @@ -236,6 +249,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mOverlayContainer = findViewById(R.id.overlay_container); mRightAffordanceView = findViewById(R.id.camera_button); mLeftAffordanceView = findViewById(R.id.left_button); + mAltLeftButton = findViewById(R.id.alt_left_button); mIndicationArea = findViewById(R.id.keyguard_indication_area); mEnterpriseDisclosure = findViewById( R.id.keyguard_indication_enterprise_disclosure); @@ -334,6 +348,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height); mLeftAffordanceView.setLayoutParams(lp); updateLeftAffordanceIcon(); + + lp = mAltLeftButton.getLayoutParams(); + lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width); + lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height); + mAltLeftButton.setLayoutParams(lp); } private void updateRightAffordanceIcon() { @@ -392,10 +411,17 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private void updateLeftAffordanceIcon() { + if (mDozing) { + mAltLeftButton.setVisibility(GONE); + } else if (mAltLeftButton.getDrawable() != null) { + mAltLeftButton.setVisibility(VISIBLE); + } + if (!mShowLeftAffordance || mDozing) { mLeftAffordanceView.setVisibility(GONE); return; } + IconState state = mLeftButton.getIcon(); mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE); if (state.drawable != mLeftAffordanceView.getDrawable() @@ -669,6 +695,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL public void startFinishDozeAnimation() { long delay = 0; + if (mAltLeftButton.getVisibility() == View.VISIBLE) { + startFinishDozeAnimationElement(mAltLeftButton, delay); + } if (mLeftAffordanceView.getVisibility() == View.VISIBLE) { startFinishDozeAnimationElement(mLeftAffordanceView, delay); delay += DOZE_ANIMATION_STAGGER_DELAY; @@ -744,6 +773,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL if (dozing) { mOverlayContainer.setVisibility(INVISIBLE); + if (mControlsDialog != null) { + mControlsDialog.dismiss(); + mControlsDialog = null; + } } else { mOverlayContainer.setVisibility(VISIBLE); if (animate) { @@ -773,6 +806,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mLeftAffordanceView.setAlpha(alpha); mRightAffordanceView.setAlpha(alpha); mIndicationArea.setAlpha(alpha); + mAltLeftButton.setAlpha(alpha); } private class DefaultLeftButton implements IntentButton { @@ -844,4 +878,54 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } return insets; } + + /** + * Show or hide controls, depending on the lock screen mode and controls + * availability. + */ + public void setupControls(ControlsComponent component, BroadcastDispatcher dispatcher) { + mControlsComponent = component; + mBroadcastDispatcher = dispatcher; + setupControls(); + } + + private void setupControls() { + if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) { + mAltLeftButton.setVisibility(View.GONE); + mAltLeftButton.setOnClickListener(null); + return; + } + + if (Settings.Global.getInt(mContext.getContentResolver(), "controls_lockscreen", 0) == 0) { + return; + } + + if (mControlsComponent.getControlsListingController().isPresent()) { + mControlsComponent.getControlsListingController().get() + .addCallback(list -> { + if (!list.isEmpty()) { + mAltLeftButton.setImageDrawable(list.get(0).loadIcon()); + mAltLeftButton.setVisibility(View.VISIBLE); + mAltLeftButton.setOnClickListener((v) -> { + ControlsUiController ui = mControlsComponent + .getControlsUiController().get(); + mControlsDialog = new ControlsDialog(mContext, mBroadcastDispatcher) + .show(ui); + }); + + } else { + mAltLeftButton.setVisibility(View.GONE); + mAltLeftButton.setOnClickListener(null); + } + }); + } + } + + /** + * Optionally add controls when in the new lockscreen mode + */ + public void onLockScreenModeChanged(int mode) { + mLockScreenMode = mode; + setupControls(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index a30f193c50a3..e0ef3b6483a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -80,8 +80,10 @@ import com.android.systemui.DejankUtils; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.controls.dagger.ControlsComponent; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeLog; @@ -95,6 +97,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.qs.QSDetailDisplayer; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -228,6 +231,7 @@ public class NotificationPanelViewController extends PanelViewController { public void onLockScreenModeChanged(int mode) { mLockScreenMode = mode; mClockPositionAlgorithm.onLockScreenModeChanged(mode); + mKeyguardBottomArea.onLockScreenModeChanged(mode); } @Override @@ -292,7 +296,10 @@ public class NotificationPanelViewController extends PanelViewController { private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; private final QSDetailDisplayer mQSDetailDisplayer; + private final FeatureFlags mFeatureFlags; private final ScrimController mScrimController; + private final ControlsComponent mControlsComponent; + // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card. // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications private final int mMaxKeyguardNotifications; @@ -501,6 +508,7 @@ public class NotificationPanelViewController extends PanelViewController { private NotificationShelfController mNotificationShelfController; private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; + private BroadcastDispatcher mBroadcastDispatcher; private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { @Override @@ -554,7 +562,10 @@ public class NotificationPanelViewController extends PanelViewController { QSDetailDisplayer qsDetailDisplayer, ScrimController scrimController, MediaDataManager mediaDataManager, - AmbientState ambientState) { + AmbientState ambientState, + FeatureFlags featureFlags, + ControlsComponent controlsComponent, + BroadcastDispatcher broadcastDispatcher) { super(view, falsingManager, dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager, @@ -571,6 +582,7 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationIconAreaController = notificationIconAreaController; mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; mQSDetailDisplayer = qsDetailDisplayer; + mFeatureFlags = featureFlags; mView.setWillNotDraw(!DEBUG); mLayoutInflater = layoutInflater; mFalsingManager = falsingManager; @@ -587,6 +599,7 @@ public class NotificationPanelViewController extends PanelViewController { mBiometricUnlockController = biometricUnlockController; mScrimController = scrimController; mMediaDataManager = mediaDataManager; + mControlsComponent = controlsComponent; pulseExpansionHandler.setPulseExpandAbortListener(() -> { if (mQs != null) { mQs.animateHeaderSlidingOut(); @@ -625,6 +638,7 @@ public class NotificationPanelViewController extends PanelViewController { mEntryManager = notificationEntryManager; mConversationNotificationManager = conversationNotificationManager; mAuthController = authController; + mBroadcastDispatcher = broadcastDispatcher; mView.setBackgroundColor(Color.TRANSPARENT); OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener(); @@ -768,6 +782,26 @@ public class NotificationPanelViewController extends PanelViewController { lp.width = panelWidth; mNotificationStackScrollLayoutController.setLayoutParams(lp); } + + if (shouldUseSplitNotificationShade()) { + // In order to change the constraints at runtime, all children of the Constraint Layout + // must have ids. + ensureAllViewsHaveIds(mNotificationContainerParent); + } + } + + private boolean shouldUseSplitNotificationShade() { + return mFeatureFlags.isTwoColumnNotificationShadeEnabled() + && mResources.getBoolean(R.bool.config_use_split_notification_shade); + } + + private static void ensureAllViewsHaveIds(ViewGroup parentView) { + for (int i = 0; i < parentView.getChildCount(); i++) { + View childView = parentView.getChildAt(i); + if (childView.getId() == View.NO_ID) { + childView.setId(View.generateViewId()); + } + } } private void reInflateViews() { @@ -813,6 +847,7 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper); mKeyguardBottomArea.setStatusBar(mStatusBar); mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete); + mKeyguardBottomArea.setupControls(mControlsComponent, mBroadcastDispatcher); } private void updateMaxDisplayedNotifications(boolean recompute) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 7095afd45959..061f2a8cc1a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -35,7 +35,6 @@ import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTE import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING; -import static com.android.systemui.statusbar.LightRevealScrimKt.getEnableLightReveal; import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; @@ -181,6 +180,7 @@ import com.android.systemui.statusbar.AutoHideUiElement; import com.android.systemui.statusbar.BackDropView; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CrossFadeHelper; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.KeyboardShortcuts; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -442,6 +442,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final KeyguardViewMediator mKeyguardViewMediator; protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private final BrightnessSlider.Factory mBrightnessSliderFactory; + private final FeatureFlags mFeatureFlags; private final List<ExpansionChangedListener> mExpansionChangedListeners; @@ -765,7 +766,8 @@ public class StatusBar extends SystemUI implements DemoMode, Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy, StatusBarTouchableRegionManager statusBarTouchableRegionManager, NotificationIconAreaController notificationIconAreaController, - BrightnessSlider.Factory brightnessSliderFactory) { + BrightnessSlider.Factory brightnessSliderFactory, + FeatureFlags featureFlags) { super(context); mNotificationsController = notificationsController; mLightBarController = lightBarController; @@ -843,6 +845,7 @@ public class StatusBar extends SystemUI implements DemoMode, mDemoModeController = demoModeController; mNotificationIconAreaController = notificationIconAreaController; mBrightnessSliderFactory = brightnessSliderFactory; + mFeatureFlags = featureFlags; mExpansionChangedListeners = new ArrayList<>(); @@ -1186,9 +1189,11 @@ public class StatusBar extends SystemUI implements DemoMode, mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim); - if (getEnableLightReveal()) { + if (mFeatureFlags.useNewLockscreenAnimations() && mDozeParameters.getAlwaysOn()) { mLightRevealScrim.setVisibility(View.VISIBLE); mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE); + } else { + mLightRevealScrim.setVisibility(View.GONE); } mNotificationPanelViewController.initDependencies( @@ -3651,7 +3656,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onDozeAmountChanged(float linear, float eased) { - if (getEnableLightReveal()) { + if (mFeatureFlags.useNewLockscreenAnimations()) { mLightRevealScrim.setRevealAmount(1f - linear); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 36519ac0d808..983b296e006b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -180,8 +180,8 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, @Override public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, - View clickedView) { - if (mKeyguardStateController.isShowing()) { + View clickedView, boolean deferBouncer, Runnable runnable) { + if (!deferBouncer && mKeyguardStateController.isShowing()) { onLockedRemoteInput(row, clickedView); } else { if (row.isChildInGroup() && !row.areChildrenExpanded()) { @@ -189,7 +189,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, mGroupExpansionManager.toggleGroupExpansion(row.getEntry()); } row.setUserExpanded(true); - row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick); + row.getPrivateLayout().setOnExpandedVisibleListener(runnable); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index 9e9533d0e199..b572c57590ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -47,6 +47,7 @@ import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.settings.brightness.BrightnessSlider; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; @@ -200,7 +201,8 @@ public interface StatusBarPhoneModule { DismissCallbackRegistry dismissCallbackRegistry, StatusBarTouchableRegionManager statusBarTouchableRegionManager, NotificationIconAreaController notificationIconAreaController, - BrightnessSlider.Factory brightnessSliderFactory) { + BrightnessSlider.Factory brightnessSliderFactory, + FeatureFlags featureFlags) { return new StatusBar( context, notificationsController, @@ -279,6 +281,7 @@ public interface StatusBarPhoneModule { notificationShadeDepthController, statusBarTouchableRegionManager, notificationIconAreaController, - brightnessSliderFactory); + brightnessSliderFactory, + featureFlags); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java index 08a4492fc15d..ccaa1f480683 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java @@ -25,6 +25,8 @@ import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; @@ -42,11 +44,18 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa private static final int MSG_MOBILE_DATA_ENABLED_CHANGED = 5; private static final int MSG_ADD_REMOVE_EMERGENCY = 6; private static final int MSG_ADD_REMOVE_SIGNAL = 7; + private static final int HISTORY_SIZE = 64; + private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); // All the callbacks. private final ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<>(); private final ArrayList<SignalCallback> mSignalCallbacks = new ArrayList<>(); + // Save the previous HISTORY_SIZE states for logging. + private final String[] mHistory = new String[HISTORY_SIZE]; + // Where to copy the next state into. + private int mHistoryIndex; + public CallbackHandler() { super(Looper.getMainLooper()); } @@ -111,12 +120,27 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa public void setWifiIndicators(final boolean enabled, final IconState statusIcon, final IconState qsIcon, final boolean activityIn, final boolean activityOut, final String description, boolean isTransient, String secondaryLabel) { + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("setWifiIndicators: ") + .append("enabled=").append(enabled).append(",") + .append("statusIcon=").append(statusIcon).append(",") + .append("qsIcon=").append(qsIcon).append(",") + .append("activityIn=").append(activityIn).append(",") + .append("activityOut=").append(activityOut).append(",") + .append("description=").append(description).append(",") + .append("isTransient=").append(isTransient).append(",") + .append("secondaryLabel=").append(secondaryLabel) + .toString(); + recordLastCallback(log); post(() -> { for (SignalCallback callback : mSignalCallbacks) { callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut, description, isTransient, secondaryLabel); } }); + + } @Override @@ -125,6 +149,25 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa final boolean activityOut, final CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, final CharSequence description, final boolean isWide, final int subId, boolean roaming, boolean showTriangle) { + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("setMobileDataIndicators: ") + .append("statusIcon=").append(statusIcon).append(",") + .append("qsIcon=").append(qsIcon).append(",") + .append("statusType=").append(statusType).append(",") + .append("qsType=").append(qsType).append(",") + .append("activityIn=").append(activityIn).append(",") + .append("activityOut=").append(activityOut).append(",") + .append("typeContentDescription=").append(typeContentDescription).append(",") + .append("typeContentDescriptionHtml=").append(typeContentDescriptionHtml) + .append(",") + .append("description=").append(description).append(",") + .append("isWide=").append(isWide).append(",") + .append("subId=").append(subId).append(",") + .append("roaming=").append(roaming).append(",") + .append("showTriangle=").append(showTriangle) + .toString(); + recordLastCallback(log); post(() -> { for (SignalCallback signalCluster : mSignalCallbacks) { signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType, @@ -138,6 +181,14 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa @Override public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork, boolean noNetworksAvailable) { + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("setConnectivityStatus: ") + .append("noDefaultNetwork=").append(noDefaultNetwork).append(",") + .append("noValidatedNetwork=").append(noValidatedNetwork).append(",") + .append("noNetworksAvailable=").append(noNetworksAvailable) + .toString(); + recordLastCallback(log); post(() -> { for (SignalCallback signalCluster : mSignalCallbacks) { signalCluster.setConnectivityStatus( @@ -148,6 +199,13 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa @Override public void setNoCallingStatus(boolean noCalling, int subId) { + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("setNoCallingStatus: ") + .append("noCalling=").append(noCalling).append(",") + .append("subId=").append(subId) + .toString(); + recordLastCallback(log); post(() -> { for (SignalCallback signalCluster : mSignalCallbacks) { signalCluster.setNoCallingStatus(noCalling, subId); @@ -157,16 +215,35 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa @Override public void setSubs(List<SubscriptionInfo> subs) { + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("setSubs: ") + .append("subs=").append(subs == null ? "" : subs.toString()) + .toString(); + recordLastCallback(log); obtainMessage(MSG_SUBS_CHANGED, subs).sendToTarget(); } @Override public void setNoSims(boolean show, boolean simDetected) { + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("setNoSims: ") + .append("show=").append(show).append(",") + .append("simDetected=").append(simDetected) + .toString(); + recordLastCallback(log); obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, simDetected ? 1 : 0).sendToTarget(); } @Override public void setMobileDataEnabled(boolean enabled) { + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("setMobileDataEnabled: ") + .append("enabled=").append(enabled) + .toString(); + recordLastCallback(log); obtainMessage(MSG_MOBILE_DATA_ENABLED_CHANGED, enabled ? 1 : 0, 0).sendToTarget(); } @@ -177,11 +254,23 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa @Override public void setEthernetIndicators(IconState icon) { + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("setEthernetIndicators: ") + .append("icon=").append(icon) + .toString(); + recordLastCallback(log); obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget();; } @Override public void setIsAirplaneMode(IconState icon) { + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("setIsAirplaneMode: ") + .append("icon=").append(icon) + .toString(); + recordLastCallback(log); obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();; } @@ -193,4 +282,25 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa obtainMessage(MSG_ADD_REMOVE_SIGNAL, listening ? 1 : 0, 0, listener).sendToTarget(); } + protected void recordLastCallback(String callback) { + mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)] = callback; + } + + /** + * Dump the Callback logs + */ + public void dump(PrintWriter pw) { + pw.println(" - CallbackHandler -----"); + int size = 0; + for (int i = 0; i < HISTORY_SIZE; i++) { + if (mHistory[i] != null) size++; + } + // Print out the previous states in ordered number. + for (int i = mHistoryIndex + HISTORY_SIZE - 1; + i >= mHistoryIndex + HISTORY_SIZE - size; i--) { + pw.println(" Previous Callback(" + (mHistoryIndex + HISTORY_SIZE - i) + "): " + + mHistory[i & (HISTORY_SIZE - 1)]); + } + } + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 39472ded4a3f..0fe338ea118d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -46,6 +46,7 @@ import com.android.settingslib.Utils; import com.android.settingslib.graph.SignalDrawable; import com.android.settingslib.mobile.MobileMappings.Config; import com.android.settingslib.mobile.MobileStatusTracker; +import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus; import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults; import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.SignalStrengthUtil; @@ -54,6 +55,7 @@ import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import java.io.PrintWriter; +import java.text.SimpleDateFormat; import java.util.BitSet; import java.util.List; import java.util.Map; @@ -62,6 +64,8 @@ import java.util.Map; * Monitors the mobile signal changes and update the SysUI icons. */ public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> { + private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); + private final TelephonyManager mPhone; private final SubscriptionDefaults mDefaults; private final String mNetworkNameDefault; @@ -90,6 +94,11 @@ public class MobileSignalController extends SignalController<MobileState, Mobile @VisibleForTesting MobileStatusTracker mMobileStatusTracker; + // Save the previous HISTORY_SIZE states for logging. + private final String[] mMobileStatusHistory = new String[HISTORY_SIZE]; + // Where to copy the next state into. + private int mMobileStatusHistoryIndex; + // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't // need listener lists anymore. public MobileSignalController(Context context, Config config, boolean hasMobileData, @@ -126,12 +135,17 @@ public class MobileSignalController extends SignalController<MobileState, Mobile mCallback = new MobileStatusTracker.Callback() { @Override public void onMobileStatusChanged(boolean updateTelephony, - MobileStatusTracker.MobileStatus mobileStatus) { + MobileStatus mobileStatus) { if (Log.isLoggable(mTag, Log.DEBUG)) { Log.d(mTag, "onMobileStatusChanged=" + " updateTelephony=" + updateTelephony + " mobileStatus=" + mobileStatus.toString()); } + String status = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append(mobileStatus.toString()) + .toString(); + recordLastMobileStatus(status); updateMobileStatus(mobileStatus); if (updateTelephony) { updateTelephony(); @@ -343,16 +357,8 @@ public class MobileSignalController extends SignalController<MobileState, Mobile return Utils.isInService(mServiceState); } - String getNonDefaultCarrierName() { - if (!mCurrentState.networkNameData.equals(mNetworkNameDefault)) { - return mCurrentState.networkNameData; - } else if (mSubscriptionInfo.getCarrierName() != null) { - return mSubscriptionInfo.getCarrierName().toString(); - } else if (mSubscriptionInfo.getDisplayName() != null) { - return mSubscriptionInfo.getDisplayName().toString(); - } else { - return ""; - } + String getNetworkNameForCarrierWiFi() { + return mPhone.getSimOperatorName(); } private boolean isRoaming() { @@ -455,7 +461,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile return CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; } - private void updateMobileStatus(MobileStatusTracker.MobileStatus mobileStatus) { + private void updateMobileStatus(MobileStatus mobileStatus) { mCurrentState.activityIn = mobileStatus.activityIn; mCurrentState.activityOut = mobileStatus.activityOut; mCurrentState.dataSim = mobileStatus.dataSim; @@ -570,6 +576,10 @@ public class MobileSignalController extends SignalController<MobileState, Mobile notifyListenersIfNecessary(); } + private void recordLastMobileStatus(String mobileStatus) { + mMobileStatusHistory[mMobileStatusHistoryIndex++ & (HISTORY_SIZE - 1)] = mobileStatus; + } + @Override public void dump(PrintWriter pw) { super.dump(pw); @@ -580,5 +590,17 @@ public class MobileSignalController extends SignalController<MobileState, Mobile pw.println(" mDataState=" + mDataState + ","); pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ","); pw.println(" isDataDisabled=" + isDataDisabled() + ","); + pw.println(" MobileStatusHistory"); + int size = 0; + for (int i = 0; i < HISTORY_SIZE; i++) { + if (mMobileStatusHistory[i] != null) size++; + } + // Print out the previous states in ordered number. + for (int i = mMobileStatusHistoryIndex + HISTORY_SIZE - 1; + i >= mMobileStatusHistoryIndex + HISTORY_SIZE - size; i--) { + pw.println(" Previous MobileStatus(" + + (mMobileStatusHistoryIndex + HISTORY_SIZE - i) + "): " + + mMobileStatusHistory[i & (HISTORY_SIZE - 1)]); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index e13e30b74c3c..80c78115f7bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -76,6 +76,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceP import java.io.FileDescriptor; import java.io.PrintWriter; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; @@ -100,6 +101,8 @@ public class NetworkControllerImpl extends BroadcastReceiver private static final int EMERGENCY_VOICE_CONTROLLER = 200; private static final int EMERGENCY_NO_SUB = 300; private static final int EMERGENCY_ASSUMED_VOICE_CONTROLLER = 400; + private static final int HISTORY_SIZE = 16; + private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); private final Context mContext; private final TelephonyManager mPhone; @@ -150,6 +153,11 @@ public class NetworkControllerImpl extends BroadcastReceiver // This list holds our ordering. private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>(); + // Save the previous HISTORY_SIZE states for logging. + private final String[] mHistory = new String[HISTORY_SIZE]; + // Where to copy the next state into. + private int mHistoryIndex; + @VisibleForTesting boolean mListening; @@ -307,6 +315,12 @@ public class NetworkControllerImpl extends BroadcastReceiver public void onLost(Network network) { mLastNetwork = null; mLastNetworkCapabilities = null; + String callback = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("onLost: ") + .append("network=").append(network) + .toString(); + recordLastNetworkCallback(callback); updateConnectivity(); } @@ -327,6 +341,13 @@ public class NetworkControllerImpl extends BroadcastReceiver } mLastNetwork = network; mLastNetworkCapabilities = networkCapabilities; + String callback = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("onCapabilitiesChanged: ") + .append("network=").append(network).append(",") + .append("networkCapabilities=").append(networkCapabilities) + .toString(); + recordLastNetworkCallback(callback); updateConnectivity(); } }; @@ -528,9 +549,9 @@ public class NetworkControllerImpl extends BroadcastReceiver return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); } - String getNonDefaultMobileDataNetworkName(int subId) { + String getNetworkNameForCarrierWiFi(int subId) { MobileSignalController controller = getControllerWithSubId(subId); - return controller != null ? controller.getNonDefaultCarrierName() : ""; + return controller != null ? controller.getNetworkNameForCarrierWiFi() : ""; } private void notifyControllersMobileDataChanged() { @@ -996,6 +1017,19 @@ public class NetworkControllerImpl extends BroadcastReceiver pw.print(" mEmergencySource="); pw.println(emergencyToString(mEmergencySource)); + pw.println(" - DefaultNetworkCallback -----"); + int size = 0; + for (int i = 0; i < HISTORY_SIZE; i++) { + if (mHistory[i] != null) { + size++; + } + } + for (int i = mHistoryIndex + HISTORY_SIZE - 1; + i >= mHistoryIndex + HISTORY_SIZE - size; i--) { + pw.println(" Previous NetworkCallback(" + (mHistoryIndex + HISTORY_SIZE - i) + "): " + + mHistory[i & (HISTORY_SIZE - 1)]); + } + pw.println(" - config ------"); for (int i = 0; i < mMobileSignalControllers.size(); i++) { MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); @@ -1006,6 +1040,8 @@ public class NetworkControllerImpl extends BroadcastReceiver mEthernetSignalController.dump(pw); mAccessPoints.dump(pw); + + mCallbackHandler.dump(pw); } private static final String emergencyToString(int emergencySource) { @@ -1235,6 +1271,10 @@ public class NetworkControllerImpl extends BroadcastReceiver return s; } + private void recordLastNetworkCallback(String callback) { + mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)] = callback; + } + private SubscriptionInfo addSignalController(int id, int simSlotIndex) { SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0, null, null, null, "", false, null, null); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 9380d9110c35..8e833c05b447 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -73,6 +73,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo; @@ -80,6 +81,7 @@ import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewW import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LightBarController; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -99,6 +101,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private final SendButtonTextWatcher mTextWatcher; private final TextView.OnEditorActionListener mEditorActionHandler; + private final NotificationRemoteInputManager mRemoteInputManager; + private final List<OnFocusChangeListener> mEditTextFocusChangeListeners = new ArrayList<>(); private RemoteEditText mEditText; private ImageButton mSendButton; private ProgressBar mProgressBar; @@ -121,12 +125,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private boolean mResetting; private NotificationViewWrapper mWrapper; private Consumer<Boolean> mOnVisibilityChangedListener; + private NotificationRemoteInputManager.BouncerChecker mBouncerChecker; public RemoteInputView(Context context, AttributeSet attrs) { super(context, attrs); mTextWatcher = new SendButtonTextWatcher(); mEditorActionHandler = new EditorActionHandler(); mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class); + mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); mStatusBarManagerService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); } @@ -200,6 +206,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } private void sendRemoteInput(Intent intent) { + if (mBouncerChecker != null && mBouncerChecker.showBouncerIfNecessary()) { + mEditText.hideIme(); + return; + } + mEditText.setEnabled(false); mSendButton.setVisibility(INVISIBLE); mProgressBar.setVisibility(VISIBLE); @@ -351,6 +362,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } + /** Populates the text field of the remote input with the given content. */ + public void setEditTextContent(@Nullable CharSequence editTextContent) { + mEditText.setText(editTextContent); + } + public void focusAnimated() { if (getVisibility() != VISIBLE) { Animator animator = ViewAnimationUtils.createCircularReveal( @@ -552,6 +568,37 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene return getVisibility() == VISIBLE && mController.isSpinning(mEntry.getKey(), mToken); } + /** + * Sets a {@link com.android.systemui.statusbar.NotificationRemoteInputManager.BouncerChecker} + * that will be used to determine if the device needs to be unlocked before sending the + * RemoteInput. + */ + public void setBouncerChecker( + @Nullable NotificationRemoteInputManager.BouncerChecker bouncerChecker) { + mBouncerChecker = bouncerChecker; + } + + /** Registers a listener for focus-change events on the EditText */ + public void addOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) { + mEditTextFocusChangeListeners.add(listener); + } + + /** Removes a previously-added listener for focus-change events on the EditText */ + public void removeOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) { + mEditTextFocusChangeListeners.remove(listener); + } + + /** Determines if the EditText has focus. */ + public boolean editTextHasFocus() { + return mEditText != null && mEditText.hasFocus(); + } + + private void onEditTextFocusChanged(RemoteEditText remoteEditText, boolean focused) { + for (View.OnFocusChangeListener listener : mEditTextFocusChangeListeners) { + listener.onFocusChange(remoteEditText, focused); + } + } + /** Handler for button click on send action in IME. */ private class EditorActionHandler implements TextView.OnEditorActionListener { @@ -603,6 +650,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private RemoteInputView mRemoteInputView; boolean mShowImeOnInputConnection; private LightBarController mLightBarController; + private InputMethodManager mInputMethodManager; UserHandle mUser; public RemoteEditText(Context context, AttributeSet attrs) { @@ -621,6 +669,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene setOnReceiveContentListener(types, listener); } + private void hideIme() { + if (mInputMethodManager != null) { + mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); + } + } + private void defocusIfNeeded(boolean animate) { if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) { @@ -654,6 +708,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(focused, direction, previouslyFocusedRect); + if (mRemoteInputView != null) { + mRemoteInputView.onEditTextFocusChanged(this, focused); + } if (!focused) { defocusIfNeeded(true /* animate */); } @@ -724,17 +781,16 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene if (mShowImeOnInputConnection && ic != null) { Context targetContext = userContext != null ? userContext : getContext(); - final InputMethodManager imm = - targetContext.getSystemService(InputMethodManager.class); - if (imm != null) { + mInputMethodManager = targetContext.getSystemService(InputMethodManager.class); + if (mInputMethodManager != null) { // onCreateInputConnection is called by InputMethodManager in the middle of // setting up the connection to the IME; wait with requesting the IME until that // work has completed. post(new Runnable() { @Override public void run() { - imm.viewClicked(RemoteEditText.this); - imm.showSoftInput(RemoteEditText.this, 0); + mInputMethodManager.viewClicked(RemoteEditText.this); + mInputMethodManager.showSoftInput(RemoteEditText.this, 0); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index b2120d47ab5d..1fd2ccbf8500 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -147,7 +147,7 @@ public class WifiSignalController extends IconState qsIcon = new IconState( mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(), contentDescription); CharSequence description = - mNetworkController.getNonDefaultMobileDataNetworkName(mCurrentState.subId); + mNetworkController.getNetworkNameForCarrierWiFi(mCurrentState.subId); callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription, dataContentDescriptionHtml, description, icons.isWide, diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java index b7d1bc6c4c63..6db21f9159ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -362,34 +362,6 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { } @Test - public void testAugmentTileFromStorageWithNotification() { - PeopleSpaceTile tile = - new PeopleSpaceTile - .Builder("id", "userName", ICON, new Intent()) - .build(); - PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITH_SHORTCUT); - - assertThat(actual.getNotificationKey()).isEqualTo(NOTIFICATION_KEY); - assertThat(actual.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT); - assertThat(actual.getNotificationDataUri()).isEqualTo(URI); - } - - @Test - public void testAugmentTileFromStorageWithoutNotification() { - PeopleSpaceTile tile = - new PeopleSpaceTile - .Builder("id", "userName", ICON, new Intent()) - .build(); - PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITHOUT_SHORTCUT); - - assertThat(actual.getNotificationKey()).isEqualTo(null); - assertThat(actual.getNotificationKey()).isEqualTo(null); - assertThat(actual.getNotificationDataUri()).isEqualTo(null); - } - - @Test public void testDoNotUpdateSingleConversationAppWidgetWhenNotBirthday() { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT}; when(mMockCursor.moveToNext()).thenReturn(true, false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index 8c0afb89b361..ef314ad16556 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -21,6 +21,8 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH; import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -31,26 +33,24 @@ import static org.mockito.Mockito.when; import static java.util.Objects.requireNonNull; -import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.Person; +import android.app.people.ConversationChannel; +import android.app.people.IPeopleManager; import android.app.people.PeopleSpaceTile; import android.appwidget.AppWidgetManager; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutInfo; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; -import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; -import android.service.notification.ConversationChannelWrapper; import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; -import android.widget.RemoteViews; import androidx.preference.PreferenceManager; import androidx.test.filters.SmallTest; @@ -58,6 +58,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.appwidget.IAppWidgetService; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; import com.android.systemui.statusbar.SbnBuilder; @@ -74,26 +75,27 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; @SmallTest @RunWith(AndroidTestingRunner.class) public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private static final long MIN_LINGER_DURATION = 5; - private static final String TEST_PACKAGE_A = "com.test.package_a"; + private static final String TEST_PACKAGE_A = "com.android.systemui.tests"; private static final String TEST_PACKAGE_B = "com.test.package_b"; private static final String TEST_CHANNEL_ID = "channel_id"; private static final String TEST_CHANNEL_NAME = "channel_name"; private static final String TEST_PARENT_CHANNEL_ID = "parent_channel_id"; private static final String TEST_CONVERSATION_ID = "conversation_id"; private static final int WIDGET_ID_WITH_SHORTCUT = 1; + private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3; private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2; private static final String SHORTCUT_ID = "101"; private static final String OTHER_SHORTCUT_ID = "102"; - private static final String NOTIFICATION_KEY = "notification_key"; - private static final String NOTIFICATION_CONTENT = "notification_content"; + private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0"; + private static final String NOTIFICATION_CONTENT = "message text"; private static final Uri URI = Uri.parse("fake_uri"); private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android); private static final Person PERSON = new Person.Builder() @@ -105,10 +107,14 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private static final PeopleSpaceTile PERSON_TILE = new PeopleSpaceTile .Builder(SHORTCUT_ID, "username", ICON, new Intent()) - .setNotificationKey(NOTIFICATION_KEY) + .setPackageName(TEST_PACKAGE_A) + .setUid(0) + .setNotificationKey(NOTIFICATION_KEY + "1") .setNotificationContent(NOTIFICATION_CONTENT) .setNotificationDataUri(URI) .build(); + private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext, + SHORTCUT_ID).setLongLabel("name").build(); private PeopleSpaceWidgetManager mManager; @@ -119,175 +125,101 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Mock private AppWidgetManager mAppWidgetManager; @Mock - private INotificationManager mINotificationManager; + private IPeopleManager mIPeopleManager; @Captor private ArgumentCaptor<NotificationHandler> mListenerCaptor; + @Captor + private ArgumentCaptor<Bundle> mBundleArgumentCaptor; private final NoManSimulator mNoMan = new NoManSimulator(); private final FakeSystemClock mClock = new FakeSystemClock(); @Before - public void setUp() { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mManager = new PeopleSpaceWidgetManager(mContext); - mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mINotificationManager); + mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager); mManager.attach(mListenerService); verify(mListenerService).addNotificationHandler(mListenerCaptor.capture()); NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue()); mNoMan.addListener(serviceListener); + // Default to single People tile widgets. Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 2); + Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); - SharedPreferences.Editor editor = sp.edit(); - editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID); - editor.apply(); - Bundle options = new Bundle(); - options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE); + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); + Bundle options = new Bundle(); + options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE); when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT))) .thenReturn(options); when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT))) .thenReturn(new Bundle()); + when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn( + getConversationWithShortcutId(SHORTCUT_ID)); } @Test - public void testDoNotNotifyAppWidgetIfNoWidgets() throws RemoteException { - int[] widgetIdsArray = {}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - - NotifEvent notif1 = mNoMan.postNotif( - new NotificationEntryBuilder() - .setId(0) - .setPkg(TEST_PACKAGE_A)); - mClock.advanceTime(MIN_LINGER_DURATION); - - verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt()); - } - - @Test - public void testDoNotNotifySingleConversationAppWidgetIfNoWidgets() throws RemoteException { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); + public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception { int[] widgetIdsArray = {}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - NotifEvent notif1 = mNoMan.postNotif( - new NotificationEntryBuilder() - .setId(0) - .setPkg(TEST_PACKAGE_A)); - mClock.advanceTime(MIN_LINGER_DURATION); - - verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any(RemoteViews.class)); - verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt()); - } - - @Test - public void testNotifyAppWidgetIfNotificationPosted() throws RemoteException { - int[] widgetIdsArray = {1}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - - NotifEvent notif1 = mNoMan.postNotif( - new NotificationEntryBuilder() - .setId(0) - .setPkg(TEST_PACKAGE_A)); - mClock.advanceTime(MIN_LINGER_DURATION); - - verify(mIAppWidgetService, times(1)) - .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt()); - verify(mIAppWidgetService, never()).updateAppWidgetIds(any(), any(), - any(RemoteViews.class)); - } - - @Test - public void testNotifySingleConversationAppWidgetOnceIfNotificationPosted() - throws RemoteException { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); - when(mINotificationManager.getConversations(false)).thenReturn( - new ParceledListSlice(getConversationWithShortcutId())); - int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - + StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setPkg(TEST_PACKAGE_A) + .setSbn(sbn) .setId(1)); + mClock.advanceTime(MIN_LINGER_DURATION); - verify(mIAppWidgetService, never()) - .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt()); - verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), - any(RemoteViews.class)); - verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT), - any(RemoteViews.class)); - } - - @Test - public void testNotifySingleConversationAppWidgetTwiceIfTwoNotificationsPosted() - throws RemoteException { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); - when(mINotificationManager.getConversations(false)).thenReturn( - new ParceledListSlice(getConversationWithShortcutId())); - int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - - NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setPkg(TEST_PACKAGE_A) - .setId(1)); - mClock.advanceTime(4); - NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder() - .setPkg(TEST_PACKAGE_B) - .setId(2)); - - verify(mIAppWidgetService, never()) - .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt()); - verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), - any(RemoteViews.class)); - verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT), - any(RemoteViews.class)); + verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), + any()); } @Test - public void testNotifyAppWidgetTwiceIfTwoNotificationsPosted() throws RemoteException { - int[] widgetIdsArray = {1, 2}; + public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception { + int[] widgetIdsArray = {}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + Notification notificationWithoutShortcut = new Notification.Builder(mContext) + .setContentTitle("TEST_TITLE") + .setContentText("TEST_TEXT") + .setStyle(new Notification.MessagingStyle(PERSON) + .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON)) + ) + .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setPkg(TEST_PACKAGE_A) + .setSbn(new SbnBuilder() + .setNotification(notificationWithoutShortcut) + .setPkg(TEST_PACKAGE_A) + .build()) .setId(1)); - mClock.advanceTime(4); - NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder() - .setPkg(TEST_PACKAGE_B) - .setId(2)); + mClock.advanceTime(MIN_LINGER_DURATION); - verify(mIAppWidgetService, times(2)) - .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt()); verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), - any(RemoteViews.class)); + any()); } @Test - public void testNotifyAppWidgetTwiceIfNotificationPostedAndRemoved() throws RemoteException { - int[] widgetIdsArray = {1, 2}; + public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception { + int[] widgetIdsArray = {}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + StatusBarNotification sbnWithoutPackageName = new SbnBuilder() + .setNotification(createMessagingStyleNotification(SHORTCUT_ID)) + .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setPkg(TEST_PACKAGE_A) + .setSbn(sbnWithoutPackageName) .setId(1)); - mClock.advanceTime(4); - NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0); + mClock.advanceTime(MIN_LINGER_DURATION); - verify(mIAppWidgetService, times(2)) - .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt()); verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), - any(RemoteViews.class)); + any()); } @Test - public void testDoNotNotifyAppWidgetIfNonConversationChannelModified() throws RemoteException { + public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception { int[] widgetIdsArray = {1}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); @@ -298,13 +230,12 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH); mClock.advanceTime(MIN_LINGER_DURATION); - verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt()); verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), - any(RemoteViews.class)); + any()); } @Test - public void testNotifyAppWidgetIfConversationChannelModified() throws RemoteException { + public void testUpdateAppWidgetIfConversationChannelModified() throws Exception { int[] widgetIdsArray = {1}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); @@ -316,45 +247,57 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH); mClock.advanceTime(MIN_LINGER_DURATION); - verify(mIAppWidgetService, times(1)) - .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt()); - verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), - any(RemoteViews.class)); + verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(), + any()); } @Test - public void testDoNotUpdateNotificationPostedIfNoExistingTile() throws RemoteException { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); - when(mINotificationManager.getConversations(false)).thenReturn( - new ParceledListSlice(getConversationWithShortcutId())); + public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) - .setPkg(TEST_PACKAGE_A) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); verify(mAppWidgetManager, never()) - .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any()); + .updateAppWidgetOptions(anyInt(), any()); + verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), + any()); } @Test - public void testDoNotUpdateNotificationRemovedIfNoExistingTile() throws RemoteException { + public void testDoNotUpdateNotificationPostedIfDifferentPackageName() throws Exception { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); - when(mINotificationManager.getConversations(false)).thenReturn( - new ParceledListSlice(getConversationWithShortcutId())); + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder() + .setNotification(createMessagingStyleNotification(SHORTCUT_ID)) + .setPkg(TEST_PACKAGE_B) + .build(); + NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() + .setSbn(sbnWithDifferentPackageName) + .setId(1)); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, never()) + .updateAppWidgetOptions(anyInt(), any()); + verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), + any()); + } + + @Test + public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) - .setPkg(TEST_PACKAGE_A) .setId(1)); mClock.advanceTime(4); NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0); @@ -362,99 +305,215 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { verify(mAppWidgetManager, never()) .updateAppWidgetOptions(anyInt(), any()); + verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), + any()); } @Test - public void testUpdateNotificationPostedIfExistingTile() throws RemoteException { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); - when(mINotificationManager.getConversations(false)).thenReturn( - new ParceledListSlice(getConversationWithShortcutId())); + public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID); + StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder() + .setNotification(createMessagingStyleNotification(SHORTCUT_ID)) + .setPkg(TEST_PACKAGE_B) + .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setSbn(sbn) - .setPkg(TEST_PACKAGE_A) + .setSbn(sbnWithDifferentPackageName) + .setId(1)); + mClock.advanceTime(4); + NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, never()) + .updateAppWidgetOptions(anyInt(), any()); + verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), + any()); + } + + @Test + public void testUpdateNotificationPostedIfExistingTile() throws Exception { + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() + .setSbn(createConversationNotification(SHORTCUT_ID)) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); verify(mAppWidgetManager, times(1)) - .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any()); + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + Bundle bundle = mBundleArgumentCaptor.getValue(); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY); + assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test + public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception { + Bundle options = new Bundle(); + options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE); + when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT))) + .thenReturn(options); + // Set the same Person associated on another People Tile widget ID. + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT); + + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, + SECOND_WIDGET_ID_WITH_SHORTCUT}; + when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() + .setSbn(createConversationNotification(SHORTCUT_ID)) + .setId(1)); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT), + any()); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test + public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson() + throws Exception { + Bundle options = new Bundle(); + options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE); + when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT))) + .thenReturn(options); + // Set the same Person associated on another People Tile widget ID. + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT); + + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, + SECOND_WIDGET_ID_WITH_SHORTCUT}; + when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT); + NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() + .setSbn(createConversationNotification(SHORTCUT_ID)) + .setId(1)); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + verify(mAppWidgetManager, never()) + .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT), + any()); + verify(mAppWidgetManager, never()).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT), + any()); } @Test public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile() - throws RemoteException { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); - when(mINotificationManager.getConversations(false)).thenReturn( - new ParceledListSlice(getConversationWithShortcutId())); + throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - Notification notification = new Notification.Builder(mContext) + Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext) .setContentTitle("TEST_TITLE") .setContentText("TEST_TEXT") .setShortcutId(SHORTCUT_ID) .build(); StatusBarNotification sbn = new SbnBuilder() - .setNotification(notification) + .setNotification(notificationWithoutMessagingStyle) .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) - .setPkg(TEST_PACKAGE_A) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); verify(mAppWidgetManager, never()) .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any()); + verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); } @Test - public void testUpdateNotificationRemovedIfExistingTile() throws RemoteException { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); - when(mINotificationManager.getConversations(false)).thenReturn( - new ParceledListSlice(getConversationWithShortcutId())); + public void testUpdateNotificationRemovedIfExistingTile() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) - .setPkg(TEST_PACKAGE_A) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0); mClock.advanceTime(MIN_LINGER_DURATION); - verify(mAppWidgetManager, times(2)) - .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any()); + verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + Bundle bundle = mBundleArgumentCaptor.getValue(); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + assertThat(tile.getNotificationKey()).isEqualTo(null); + assertThat(tile.getNotificationContent()).isEqualTo(null); + assertThat(tile.getNotificationDataUri()).isEqualTo(null); + verify(mAppWidgetManager, times(2)).updateAppWidget(anyInt(), + any()); } - /** Returns a list of a single conversation associated with {@code SHORTCUT_ID}. */ - private List<ConversationChannelWrapper> getConversationWithShortcutId() { - List<ConversationChannelWrapper> convos = new ArrayList<>(); - ConversationChannelWrapper convo1 = new ConversationChannelWrapper(); - convo1.setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID).setLongLabel( - "name").build()); - convos.add(convo1); - return convos; + /** + * Returns a single conversation associated with {@code shortcutId}. + */ + private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception { + ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel( + "name").build(); + ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null, + 0L, false); + return convo; } - private StatusBarNotification createConversationNotification(String shortcutId) { - Notification notification = new Notification.Builder(mContext) + private Notification createMessagingStyleNotification(String shortcutId) { + return new Notification.Builder(mContext) .setContentTitle("TEST_TITLE") .setContentText("TEST_TEXT") .setShortcutId(shortcutId) .setStyle(new Notification.MessagingStyle(PERSON) - .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON)) + .addMessage( + new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10, + PERSON)) ) .build(); + } + + private StatusBarNotification createConversationNotification(String shortcutId) { + Notification notification = createMessagingStyleNotification(shortcutId); return new SbnBuilder() .setNotification(notification) + .setPkg(TEST_PACKAGE_A) .build(); } + + private void setStorageForTile(String shortcutId, String packageName, int widgetId) { + SharedPreferences widgetSp = mContext.getSharedPreferences( + String.valueOf(widgetId), + Context.MODE_PRIVATE); + SharedPreferences.Editor widgetEditor = widgetSp.edit(); + widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, packageName); + widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, shortcutId); + widgetEditor.putInt(PeopleSpaceUtils.USER_ID, 0); + widgetEditor.apply(); + + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + SharedPreferences.Editor editor = sp.edit(); + editor.putString(String.valueOf(widgetId), shortcutId); + String key = PeopleSpaceUtils.getKey(shortcutId, packageName, 0); + Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>())); + storedWidgetIds.add(String.valueOf(widgetId)); + editor.putStringSet(key, storedWidgetIds); + editor.apply(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java index 78af20f9db31..ffd747e09e23 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java @@ -75,6 +75,7 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase { mFakeSettings = new FakeSettings(); mTile = new ReduceBrightColorsTile( + true, mHost, mTestableLooper.getLooper(), new Handler(mTestableLooper.getLooper()), @@ -95,7 +96,6 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase { assertEquals(Tile.STATE_INACTIVE, mTile.getState().state); assertEquals(mTile.getState().label.toString(), mContext.getString(R.string.quick_settings_reduce_bright_colors_label)); - assertEquals(mTile.getState().secondaryLabel.toString(), ""); } @Test @@ -128,13 +128,5 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase { assertEquals(Tile.STATE_ACTIVE, mTile.getState().state); assertEquals(mTile.getState().label.toString(), mContext.getString(R.string.quick_settings_reduce_bright_colors_label)); - - final int intensity = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mUserTracker.getUserId()); - - assertEquals( - mContext.getString( - R.string.quick_settings_reduce_bright_colors_secondary_label, intensity), - mTile.getState().secondaryLabel.toString()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java index fa253e62ef0a..b9fd75ef5fda 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java @@ -35,6 +35,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.doze.DozeScreenState; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.tuner.TunerService; @@ -57,6 +58,7 @@ public class DozeParametersTest extends SysuiTestCase { @Mock private PowerManager mPowerManager; @Mock private TunerService mTunerService; @Mock private BatteryController mBatteryController; + @Mock private FeatureFlags mFeatureFlags; @Before public void setup() { @@ -67,7 +69,8 @@ public class DozeParametersTest extends SysuiTestCase { mAlwaysOnDisplayPolicy, mPowerManager, mBatteryController, - mTunerService + mTunerService, + mFeatureFlags ); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 8d4470bb1cb6..c07ba723ab43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -61,13 +61,16 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.controls.dagger.ControlsComponent; import com.android.systemui.doze.DozeLog; import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.qs.QSDetailDisplayer; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShelfController; @@ -204,6 +207,14 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private MediaDataManager mMediaDataManager; @Mock + private FeatureFlags mFeatureFlags; + @Mock + private ControlsComponent mControlsComponent; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private NotificationsQuickSettingsContainer mNotificationContainerParent; + @Mock private AmbientState mAmbientState; private NotificationPanelViewController mNotificationPanelViewController; private View.AccessibilityDelegate mAccessibiltyDelegate; @@ -219,6 +230,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); mDisplayMetrics.density = 100; when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true); + when(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)).thenReturn(400); + when(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)).thenReturn(400); when(mView.getContext()).thenReturn(getContext()); when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar); when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch); @@ -237,6 +250,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { when(mView.findViewById(R.id.keyguard_status_view)) .thenReturn(mock(KeyguardStatusView.class)); when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar); + when(mView.findViewById(R.id.notification_container_parent)) + .thenReturn(mNotificationContainerParent); FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder( mDisplayMetrics); @@ -264,6 +279,11 @@ public class NotificationPanelViewTest extends SysuiTestCase { .thenReturn(mKeyguardClockSwitchController); when(mKeyguardStatusViewComponent.getKeyguardStatusViewController()) .thenReturn(mKeyguardStatusViewController); + when(mQsFrame.getLayoutParams()).thenReturn( + new ViewGroup.LayoutParams(600, 400)); + when(mNotificationStackScrollLayoutController.getLayoutParams()).thenReturn( + new ViewGroup.LayoutParams(600, 400)); + mNotificationPanelViewController = new NotificationPanelViewController(mView, mResources, mLayoutInflater, @@ -285,7 +305,10 @@ public class NotificationPanelViewTest extends SysuiTestCase { new QSDetailDisplayer(), mScrimController, mMediaDataManager, - mAmbientState); + mAmbientState, + mFeatureFlags, + mControlsComponent, + mBroadcastDispatcher); mNotificationPanelViewController.initDependencies( mStatusBar, mNotificationShelfController); @@ -400,6 +423,25 @@ public class NotificationPanelViewTest extends SysuiTestCase { verify(mStatusBarKeyguardViewManager).showBouncer(true); } + @Test + public void testAllChildrenOfNotificationContainer_haveIds() { + when(mNotificationContainerParent.getChildCount()).thenReturn(2); + when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); + when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); + + View view1 = new View(mContext); + view1.setId(1); + when(mNotificationContainerParent.getChildAt(0)).thenReturn(view1); + + View view2 = mock(View.class); + when(mNotificationContainerParent.getChildAt(1)).thenReturn(view2); + + mNotificationPanelViewController.updateResources(); + + assertThat(mNotificationContainerParent.getChildAt(0).getId()).isEqualTo(1); + assertThat(mNotificationContainerParent.getChildAt(1).getId()).isNotEqualTo(View.NO_ID); + } + private void onTouchEvent(MotionEvent ev) { mTouchHandler.onTouch(mView, ev); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index cae488a561a2..253460db0d07 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -97,6 +97,7 @@ import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.settings.brightness.BrightnessSlider; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -258,6 +259,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private DemoModeController mDemoModeController; @Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy; @Mock private BrightnessSlider.Factory mBrightnessSliderFactory; + @Mock private FeatureFlags mFeatureFlags; private ShadeController mShadeController; private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private InitController mInitController = new InitController(); @@ -418,7 +420,8 @@ public class StatusBarTest extends SysuiTestCase { mNotificationShadeDepthControllerLazy, mStatusBarTouchableRegionManager, mNotificationIconAreaController, - mBrightnessSliderFactory); + mBrightnessSliderFactory, + mFeatureFlags); when(mNotificationShadeWindowView.findViewById(R.id.lock_icon_container)).thenReturn( mLockIconContainer); diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java index 4d95ef13a2a8..6dcad255eee4 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java @@ -20,14 +20,11 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; -import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; -import android.net.IConnectivityManager; +import android.net.ConnectivityManager; import android.os.Bundle; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; import android.text.SpannableStringBuilder; @@ -45,7 +42,7 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity private static final String TAG = "VpnDisconnected"; - private IConnectivityManager mService; + private ConnectivityManager mService; private int mUserId; private String mVpnPackage; @@ -53,10 +50,9 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mService = IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); mUserId = UserHandle.myUserId(); - mVpnPackage = getAlwaysOnVpnPackage(); + final ConnectivityManager cm = getSystemService(ConnectivityManager.class); + mVpnPackage = cm.getAlwaysOnVpnPackageForUser(mUserId); if (mVpnPackage == null) { finish(); return; @@ -102,15 +98,6 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity } } - private String getAlwaysOnVpnPackage() { - try { - return mService.getAlwaysOnVpnPackage(mUserId); - } catch (RemoteException e) { - Log.e(TAG, "Can't getAlwaysOnVpnPackage()", e); - return null; - } - } - private CharSequence getVpnLabel() { try { return VpnConfig.getVpnLabel(this, mVpnPackage); diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java index e66f2cc17a7f..aab01d03b96d 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java @@ -18,15 +18,12 @@ package com.android.vpndialogs; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; -import android.content.Context; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; -import android.net.IConnectivityManager; +import android.net.ConnectivityManager; import android.net.VpnManager; import android.os.Bundle; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.text.Html; @@ -48,7 +45,8 @@ public class ConfirmDialog extends AlertActivity private String mPackage; - private IConnectivityManager mService; + private ConnectivityManager mCm; // TODO: switch entirely to VpnManager once VPN code moves + private VpnManager mVm; public ConfirmDialog() { this(VpnManager.TYPE_VPN_SERVICE); @@ -62,10 +60,10 @@ public class ConfirmDialog extends AlertActivity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPackage = getCallingPackage(); - mService = IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); + mCm = getSystemService(ConnectivityManager.class); + mVm = getSystemService(VpnManager.class); - if (prepareVpn()) { + if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) { setResult(RESULT_OK); finish(); return; @@ -74,7 +72,7 @@ public class ConfirmDialog extends AlertActivity finish(); return; } - final String alwaysOnVpnPackage = getAlwaysOnVpnPackage(); + final String alwaysOnVpnPackage = mCm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId()); // Can't prepare new vpn app when another vpn is always-on if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) { finish(); @@ -97,24 +95,6 @@ public class ConfirmDialog extends AlertActivity button.setFilterTouchesWhenObscured(true); } - private String getAlwaysOnVpnPackage() { - try { - return mService.getAlwaysOnVpnPackage(UserHandle.myUserId()); - } catch (RemoteException e) { - Log.e(TAG, "fail to call getAlwaysOnVpnPackage", e); - // Fallback to null to show the dialog - return null; - } - } - - private boolean prepareVpn() { - try { - return mService.prepareVpn(mPackage, null, UserHandle.myUserId()); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - } - private CharSequence getVpnLabel() { try { return VpnConfig.getVpnLabel(this, mPackage); @@ -146,10 +126,10 @@ public class ConfirmDialog extends AlertActivity @Override public void onClick(DialogInterface dialog, int which) { try { - if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) { + if (mVm.prepareVpn(null, mPackage, UserHandle.myUserId())) { // Authorize this app to initiate VPN connections in the future without user // intervention. - mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType); + mVm.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType); setResult(RESULT_OK); } } catch (Exception e) { diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java index 01dca7e30e64..1fc74f704f62 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java @@ -16,13 +16,11 @@ package com.android.vpndialogs; -import android.content.Context; import android.content.DialogInterface; -import android.net.IConnectivityManager; +import android.net.VpnManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.util.Log; @@ -41,7 +39,7 @@ public class ManageDialog extends AlertActivity implements private VpnConfig mConfig; - private IConnectivityManager mService; + private VpnManager mVm; private TextView mDuration; private TextView mDataTransmitted; @@ -55,11 +53,9 @@ public class ManageDialog extends AlertActivity implements super.onCreate(savedInstanceState); try { + mVm = getSystemService(VpnManager.class); - mService = IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); - - mConfig = mService.getVpnConfig(UserHandle.myUserId()); + mConfig = mVm.getVpnConfig(UserHandle.myUserId()); // mConfig can be null if we are a restricted user, in that case don't show this dialog if (mConfig == null) { @@ -118,9 +114,9 @@ public class ManageDialog extends AlertActivity implements } else if (which == DialogInterface.BUTTON_NEUTRAL) { final int myUserId = UserHandle.myUserId(); if (mConfig.legacy) { - mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId); + mVm.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId); } else { - mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId); + mVm.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId); } } } catch (Exception e) { diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 8f093c7e6674..065e2bbd3eef 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -1113,7 +1113,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ try { final IBinder overlayWindowToken = new Binder(); mWindowManagerService.addWindowToken(overlayWindowToken, TYPE_ACCESSIBILITY_OVERLAY, - displayId); + displayId, null /* options */); synchronized (mLock) { mOverlayWindowTokens.put(displayId, overlayWindowToken); } diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index f9f064c14c62..e4a86c3744ce 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -55,7 +55,6 @@ import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; @@ -66,9 +65,8 @@ import android.content.pm.UserInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.graphics.Bitmap; import android.graphics.Point; -import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -119,7 +117,6 @@ import com.android.internal.util.DumpUtils; import com.android.internal.widget.IRemoteViewsFactory; import com.android.server.LocalServices; import com.android.server.WidgetBackupProvider; -import com.android.server.policy.IconUtilities; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -253,8 +250,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private boolean mSafeMode; private int mMaxWidgetBitmapMemory; - private IconUtilities mIconUtilities; - AppWidgetServiceImpl(Context context) { mContext = context; } @@ -271,7 +266,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku mCallbackHandler = new CallbackHandler(mContext.getMainLooper()); mBackupRestoreController = new BackupRestoreController(); mSecurityPolicy = new SecurityPolicy(); - mIconUtilities = new IconUtilities(mContext); computeMaximumWidgetBitmapMemory(); registerBroadcastReceiver(); @@ -578,44 +572,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } - private Bitmap createMaskedWidgetBitmap(String providerPackage, int providerUserId) { - final long identity = Binder.clearCallingIdentity(); - try { - // Load the unbadged application icon and pass it to the widget to appear on - // the masked view. - Context userContext = mContext.createPackageContextAsUser(providerPackage, 0, - UserHandle.of(providerUserId)); - PackageManager pm = userContext.getPackageManager(); - Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm).mutate(); - // Create a bitmap of the icon which is what the widget's remoteview requires. - icon.setColorFilter(mIconUtilities.getDisabledColorFilter()); - return mIconUtilities.createIconBitmap(icon); - } catch (NameNotFoundException e) { - Slog.e(TAG, "Fail to get application icon", e); - // Provider package removed, no need to mask its views as its state will be - // purged very soon. - return null; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - private RemoteViews createMaskedWidgetRemoteViews(Bitmap icon, boolean showBadge, - PendingIntent onClickIntent) { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.work_widget_mask_view); - if (icon != null) { - views.setImageViewBitmap(R.id.work_widget_app_icon, icon); - } - if (!showBadge) { - views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE); - } - if (onClickIntent != null) { - views.setOnClickPendingIntent(R.id.work_widget_mask_frame, onClickIntent); - } - return views; - } - /** * Mask the target widget belonging to the specified provider, or all active widgets * of the provider if target widget == null. @@ -625,59 +581,63 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku if (widgetCount == 0) { return; } - final String providerPackage = provider.id.componentName.getPackageName(); - final int providerUserId = provider.getUserId(); - Bitmap iconBitmap = createMaskedWidgetBitmap(providerPackage, providerUserId); - if (iconBitmap == null) { - return; - } - final boolean showBadge; - final Intent onClickIntent; + RemoteViews views = new RemoteViews(mContext.getPackageName(), + R.layout.work_widget_mask_view); + ApplicationInfo appInfo = provider.info.providerInfo.applicationInfo; + final int appUserId = provider.getUserId(); + boolean showBadge; + final long identity = Binder.clearCallingIdentity(); try { + final Intent onClickIntent; + if (provider.maskedBySuspendedPackage) { - showBadge = mUserManager.hasBadge(providerUserId); + showBadge = mUserManager.hasBadge(appUserId); final String suspendingPackage = mPackageManagerInternal.getSuspendingPackage( - providerPackage, providerUserId); + appInfo.packageName, appUserId); if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent( - providerUserId, true); + appUserId, true); } else { final SuspendDialogInfo dialogInfo = - mPackageManagerInternal.getSuspendedDialogInfo(providerPackage, - suspendingPackage, providerUserId); + mPackageManagerInternal.getSuspendedDialogInfo( + appInfo.packageName, suspendingPackage, appUserId); // onUnsuspend is null because we don't want to start any activity on // unsuspending from a suspended widget. onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent( - providerPackage, suspendingPackage, dialogInfo, null, null, - providerUserId); + appInfo.packageName, suspendingPackage, dialogInfo, null, null, + appUserId); } } else if (provider.maskedByQuietProfile) { showBadge = true; - onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent( - providerUserId); + onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(appUserId); } else /* provider.maskedByLockedProfile */ { showBadge = true; - onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null, - providerUserId); + onClickIntent = mKeyguardManager + .createConfirmDeviceCredentialIntent(null, null, appUserId); if (onClickIntent != null) { - onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK - | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + onClickIntent.setFlags( + FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); } } + + if (onClickIntent != null) { + views.setOnClickPendingIntent(R.id.work_widget_mask_frame, + PendingIntent.getActivity(mContext, 0, onClickIntent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)); + } + + Icon icon = appInfo.icon != 0 + ? Icon.createWithResource(appInfo.packageName, appInfo.icon) + : Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon); + views.setImageViewIcon(R.id.work_widget_app_icon, icon); + if (!showBadge) { + views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE); + } + for (int j = 0; j < widgetCount; j++) { Widget widget = provider.widgets.get(j); if (targetWidget != null && targetWidget != widget) continue; - PendingIntent intent = null; - if (onClickIntent != null) { - // Rare informational activity click is okay being - // immutable; the tradeoff is more security in exchange for - // losing bounds-based window animations - intent = PendingIntent.getActivity(mContext, widget.appWidgetId, - onClickIntent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - } - RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent); if (widget.replaceWithMaskedViewsLocked(views)) { scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked()); } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 55e3ef2e427f..96b69dcc45d6 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -17,9 +17,17 @@ package com.android.server.companion; +import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES; +import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED; +import static android.content.Context.BIND_IMPORTANT; +import static android.content.pm.PackageManager.MATCH_ALL; + +import static com.android.internal.util.CollectionUtils.any; import static com.android.internal.util.CollectionUtils.emptyIfNull; +import static com.android.internal.util.CollectionUtils.filter; import static com.android.internal.util.CollectionUtils.find; import static com.android.internal.util.CollectionUtils.forEach; +import static com.android.internal.util.CollectionUtils.map; import static com.android.internal.util.FunctionalUtils.uncheckExceptions; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; @@ -40,22 +48,32 @@ import android.app.PendingIntent; import android.app.role.RoleManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanResult; +import android.bluetooth.le.ScanSettings; import android.companion.Association; import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; +import android.companion.CompanionDeviceService; import android.companion.DeviceNotAssociatedException; import android.companion.ICompanionDeviceDiscoveryService; import android.companion.ICompanionDeviceManager; +import android.companion.ICompanionDeviceService; import android.companion.IFindDeviceCallback; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.net.NetworkPolicyManager; import android.os.Binder; @@ -77,6 +95,7 @@ import android.permission.PermissionControllerManager; import android.provider.Settings; import android.provider.SettingsStringUtil.ComponentNameSet; import android.text.BidiFormatter; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.ExceptionUtils; @@ -115,6 +134,7 @@ import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Objects; import java.util.Set; @@ -135,6 +155,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, ".DeviceDiscoveryService"); + private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000; + private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000; + private static final boolean DEBUG = false; private static final String LOG_TAG = "CompanionDeviceManagerService"; @@ -146,18 +169,23 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private static final String XML_ATTR_PACKAGE = "package"; private static final String XML_ATTR_DEVICE = "device"; private static final String XML_ATTR_PROFILE = "profile"; - private static final String XML_ATTR_PERSISTENT_PROFILE_GRANTS = "persistent_profile_grants"; + private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby"; private static final String XML_FILE_NAME = "companion_device_manager_associations.xml"; private final CompanionDeviceManagerImpl mImpl; private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>(); private PowerWhitelistManager mPowerWhitelistManager; private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors; + /** userId -> packageName -> serviceConnector */ + private PerUser<ArrayMap<String, ServiceConnector<ICompanionDeviceService>>> + mDeviceListenerServiceConnectors; private IAppOpsService mAppOpsManager; private RoleManager mRoleManager; private BluetoothAdapter mBluetoothAdapter; + private UserManager mUserManager; private IFindDeviceCallback mFindDeviceCallback; + private ScanCallback mBleScanCallback = new BleScanCallback(); private AssociationRequest mRequest; private String mCallingPackage; private AndroidFuture<Association> mOngoingDeviceDiscovery; @@ -165,9 +193,16 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener = new BluetoothDeviceConnectedListener(); + private BleStateBroadcastReceiver mBleStateBroadcastReceiver = new BleStateBroadcastReceiver(); private List<String> mCurrentlyConnectedDevices = new ArrayList<>(); + private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>(); + private UnbindDeviceListenersRunnable + mUnbindDeviceListenersRunnable = new UnbindDeviceListenersRunnable(); + private ArrayMap<String, TriggerDeviceDisappearedRunnable> mTriggerDeviceDisappearedRunnables = + new ArrayMap<>(); private final Object mLock = new Object(); + private final Handler mMainHandler = Handler.getMain(); /** userId -> [association] */ @GuardedBy("mLock") @@ -189,6 +224,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mPermissionControllerManager = requireNonNull( context.getSystemService(PermissionControllerManager.class)); + mUserManager = context.getSystemService(UserManager.class); Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO); mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() { @@ -201,6 +237,16 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } }; + mDeviceListenerServiceConnectors = new PerUser<ArrayMap<String, + ServiceConnector<ICompanionDeviceService>>>() { + @NonNull + @Override + protected ArrayMap<String, ServiceConnector<ICompanionDeviceService>> create( + int userId) { + return new ArrayMap<>(); + } + }; + registerPackageMonitor(); } @@ -208,10 +254,13 @@ public class CompanionDeviceManagerService extends SystemService implements Bind new PackageMonitor() { @Override public void onPackageRemoved(String packageName, int uid) { + int userId = getChangingUserId(); updateAssociations( as -> CollectionUtils.filter(as, a -> !Objects.equals(a.getPackageName(), packageName)), - getChangingUserId()); + userId); + + unbindDevicePresenceListener(packageName, userId); } @Override @@ -225,6 +274,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true); } + private void unbindDevicePresenceListener(String packageName, int userId) { + ServiceConnector<ICompanionDeviceService> deviceListener = + mDeviceListenerServiceConnectors.forUser(userId) + .remove(packageName); + if (deviceListener != null) { + deviceListener.unbind(); + } + } + @Override public void onStart() { publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl); @@ -233,11 +291,17 @@ public class CompanionDeviceManagerService extends SystemService implements Bind @Override public void onBootPhase(int phase) { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + // Init Bluetooth mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (mBluetoothAdapter != null) { mBluetoothAdapter.registerBluetoothConnectionCallback( getContext().getMainExecutor(), mBluetoothDeviceConnectedListener); + getContext().registerReceiver( + mBleStateBroadcastReceiver, mBleStateBroadcastReceiver.mIntentFilter); + initBleScanning(); + } else { + Log.w(LOG_TAG, "No BluetoothAdapter available"); } } } @@ -287,7 +351,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind @Override public void binderDied() { - Handler.getMain().post(this::cleanup); + mMainHandler.post(this::cleanup); } private void cleanup() { @@ -399,7 +463,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind checkCallerIsSystemOr(callingPackage, userId); checkUsesFeature(callingPackage, getCallingUserId()); } - return new ArrayList<>(CollectionUtils.map( + return new ArrayList<>(map( getAllAssociations(userId, callingPackage), a -> a.getDeviceMacAddress())); } @@ -497,7 +561,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind return true; } - return CollectionUtils.any( + return any( getAllAssociations(userId, packageName), a -> Objects.equals(a.getDeviceMacAddress(), macAddress)); } @@ -506,22 +570,18 @@ public class CompanionDeviceManagerService extends SystemService implements Bind public void registerDevicePresenceListenerService( String packageName, String deviceAddress) throws RemoteException { - checkCanRegisterObserverService(packageName, deviceAddress); - - //TODO(eugenesusla) implement + registerDevicePresenceListenerActive(packageName, deviceAddress, true); } @Override public void unregisterDevicePresenceListenerService( String packageName, String deviceAddress) throws RemoteException { - checkCanRegisterObserverService(packageName, deviceAddress); - - //TODO(eugenesusla) implement + registerDevicePresenceListenerActive(packageName, deviceAddress, false); } - private void checkCanRegisterObserverService(String packageName, String deviceAddress) - throws RemoteException { + private void registerDevicePresenceListenerActive(String packageName, String deviceAddress, + boolean active) throws RemoteException { getContext().enforceCallingOrSelfPermission( android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE, "[un]registerDevicePresenceListenerService"); @@ -537,6 +597,20 @@ public class CompanionDeviceManagerService extends SystemService implements Bind + " is not associated with device " + deviceAddress + " for user " + userId)); } + + updateAssociations(associations -> map(associations, association -> { + if (Objects.equals(association.getPackageName(), packageName) + && Objects.equals(association.getDeviceMacAddress(), deviceAddress)) { + return new Association( + association.getUserId(), + association.getDeviceMacAddress(), + association.getPackageName(), + association.getDeviceProfile(), + active /* notifyOnDeviceNearby */); + } else { + return association; + } + })); } private void checkCanCallNotificationApi(String callingPackage) throws RemoteException { @@ -693,6 +767,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) { grantDeviceProfile(association); } + + if (association.isNotifyOnDeviceNearby()) { + restartBleScan(); + } } private void exemptFromAutoRevoke(String packageName, int uid) { @@ -795,9 +873,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind association.getDeviceMacAddress()); if (association.getDeviceProfile() != null) { tag.attribute(null, XML_ATTR_PROFILE, association.getDeviceProfile()); - tag.attribute(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS, + tag.attribute(null, XML_ATTR_NOTIFY_DEVICE_NEARBY, Boolean.toString( - association.isKeepProfilePrivilegesWhenDeviceAway())); + association.isNotifyOnDeviceNearby())); } tag.endTag(null, XML_TAG_ASSOCIATION); }); @@ -835,7 +913,12 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } private List<UserInfo> getAllUsers() { - return getContext().getSystemService(UserManager.class).getUsers(); + long identity = Binder.clearCallingIdentity(); + try { + return mUserManager.getUsers(); + } finally { + Binder.restoreCallingIdentity(identity); + } } @Nullable @@ -845,6 +928,19 @@ public class CompanionDeviceManagerService extends SystemService implements Bind a -> Objects.equals(packageFilter, a.getPackageName())); } + private Set<Association> getAllAssociations() { + long identity = Binder.clearCallingIdentity(); + try { + ArraySet<Association> result = new ArraySet<>(); + for (UserInfo user : mUserManager.getAliveUsers()) { + result.addAll(getAllAssociations(user.id)); + } + return result; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + private Set<Association> readAllAssociations(int userId) { final AtomicFile file = getStorageFileForUser(userId); @@ -865,7 +961,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE); final boolean persistentGrants = Boolean.valueOf( - parser.getAttributeValue(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS)); + parser.getAttributeValue(null, XML_ATTR_NOTIFY_DEVICE_NEARBY)); if (appPackage == null || deviceAddress == null) continue; @@ -896,6 +992,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } } + + onDeviceNearby(address); } private void grantDeviceProfile(Association association) { @@ -919,6 +1017,267 @@ public class CompanionDeviceManagerService extends SystemService implements Bind void onDeviceDisconnected(String address) { mCurrentlyConnectedDevices.remove(address); + + onDeviceDisappeared(address); + } + + private ServiceConnector<ICompanionDeviceService> getDeviceListenerServiceConnector( + Association a) { + return mDeviceListenerServiceConnectors.forUser(a.getUserId()).computeIfAbsent( + a.getPackageName(), + pkg -> createDeviceListenerServiceConnector(a)); + } + + private ServiceConnector<ICompanionDeviceService> createDeviceListenerServiceConnector( + Association a) { + List<ResolveInfo> resolveInfos = getContext().getPackageManager().queryIntentServicesAsUser( + new Intent(CompanionDeviceService.SERVICE_INTERFACE), MATCH_ALL, a.getUserId()); + List<ResolveInfo> packageResolveInfos = filter(resolveInfos, + info -> Objects.equals(info.serviceInfo.packageName, a.getPackageName())); + if (packageResolveInfos.size() != 1) { + Log.w(LOG_TAG, "Device presence listener package must have exactly one " + + "CompanionDeviceService, but " + a.getPackageName() + + " has " + packageResolveInfos.size()); + return new ServiceConnector.NoOp<>(); + } + ComponentName componentName = packageResolveInfos.get(0).serviceInfo.getComponentName(); + Log.i(LOG_TAG, "Initializing CompanionDeviceService binding for " + componentName); + return new ServiceConnector.Impl<>(getContext(), + new Intent(CompanionDeviceService.SERVICE_INTERFACE).setComponent(componentName), + BIND_IMPORTANT, + a.getUserId(), + ICompanionDeviceService.Stub::asInterface); + } + + private class BleScanCallback extends ScanCallback { + @Override + public void onScanResult(int callbackType, ScanResult result) { + if (DEBUG) { + Log.i(LOG_TAG, "onScanResult(callbackType = " + + callbackType + ", result = " + result + ")"); + } + + onDeviceNearby(result.getDevice().getAddress()); + } + + @Override + public void onBatchScanResults(List<ScanResult> results) { + for (int i = 0, size = results.size(); i < size; i++) { + onScanResult(CALLBACK_TYPE_ALL_MATCHES, results.get(i)); + } + } + + @Override + public void onScanFailed(int errorCode) { + if (errorCode == SCAN_FAILED_ALREADY_STARTED) { + // ignore - this might happen if BT tries to auto-restore scans for us in the + // future + Log.i(LOG_TAG, "Ignoring BLE scan error: SCAN_FAILED_ALREADY_STARTED"); + } else { + Log.w(LOG_TAG, "Failed to start BLE scan: error " + errorCode); + } + } + } + + private class BleStateBroadcastReceiver extends BroadcastReceiver { + + final IntentFilter mIntentFilter = + new IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED); + + @Override + public void onReceive(Context context, Intent intent) { + int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1); + int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); + Log.i(LOG_TAG, "Received BT state transition broadcast: " + + BluetoothAdapter.nameForState(previousState) + + " -> " + BluetoothAdapter.nameForState(newState)); + + boolean bleOn = newState == BluetoothAdapter.STATE_ON + || newState == BluetoothAdapter.STATE_BLE_ON; + if (bleOn) { + if (mBluetoothAdapter.getBluetoothLeScanner() != null) { + startBleScan(); + } else { + Log.wtf(LOG_TAG, "BLE on, but BluetoothLeScanner == null"); + } + } + } + } + + private class UnbindDeviceListenersRunnable implements Runnable { + + public String getJobId(String address) { + return "CDM_deviceGone_unbind_" + address; + } + + @Override + public void run() { + int size = mDevicesLastNearby.size(); + for (int i = 0; i < size; i++) { + String address = mDevicesLastNearby.keyAt(i); + Date lastNearby = mDevicesLastNearby.valueAt(i); + + if (System.currentTimeMillis() - lastNearby.getTime() + >= DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS) { + for (Association association : getAllAssociations(address)) { + if (association.isNotifyOnDeviceNearby()) { + getDeviceListenerServiceConnector(association).unbind(); + } + } + } + } + } + } + + private class TriggerDeviceDisappearedRunnable implements Runnable { + + private final String mAddress; + + TriggerDeviceDisappearedRunnable(String address) { + mAddress = address; + } + + public void schedule() { + mMainHandler.removeCallbacks(this); + mMainHandler.postDelayed(this, this, DEVICE_DISAPPEARED_TIMEOUT_MS); + } + + @Override + public void run() { + onDeviceDisappeared(mAddress); + } + } + + private Set<Association> getAllAssociations(String deviceAddress) { + List<UserInfo> aliveUsers = mUserManager.getAliveUsers(); + Set<Association> result = new ArraySet<>(); + for (int i = 0, size = aliveUsers.size(); i < size; i++) { + UserInfo user = aliveUsers.get(i); + for (Association association : getAllAssociations(user.id)) { + if (Objects.equals(association.getDeviceMacAddress(), deviceAddress)) { + result.add(association); + } + } + } + return result; + } + + private void onDeviceNearby(String address) { + Date timestamp = new Date(); + Date oldTimestamp = mDevicesLastNearby.put(address, timestamp); + + cancelUnbindDeviceListener(address); + + mTriggerDeviceDisappearedRunnables + .computeIfAbsent(address, addr -> new TriggerDeviceDisappearedRunnable(address)) + .schedule(); + + // Avoid spamming the app if device is already known to be nearby + boolean justAppeared = oldTimestamp == null + || timestamp.getTime() - oldTimestamp.getTime() >= DEVICE_DISAPPEARED_TIMEOUT_MS; + if (justAppeared) { + for (Association association : getAllAssociations(address)) { + if (association.isNotifyOnDeviceNearby()) { + if (DEBUG) { + Log.i(LOG_TAG, "Device " + address + + " managed by " + association.getPackageName() + + " is nearby on " + timestamp); + } + getDeviceListenerServiceConnector(association).run( + service -> service.onDeviceAppeared(association.getDeviceMacAddress())); + } + } + } + } + + private void onDeviceDisappeared(String address) { + boolean hasDeviceListeners = false; + for (Association association : getAllAssociations(address)) { + if (association.isNotifyOnDeviceNearby()) { + if (DEBUG) { + Log.i(LOG_TAG, "Device " + address + + " managed by " + association.getPackageName() + + " disappeared; last seen on " + mDevicesLastNearby.get(address)); + } + + getDeviceListenerServiceConnector(association).run( + service -> service.onDeviceDisappeared(address)); + hasDeviceListeners = true; + } + } + + cancelUnbindDeviceListener(address); + if (hasDeviceListeners) { + mMainHandler.postDelayed( + mUnbindDeviceListenersRunnable, + mUnbindDeviceListenersRunnable.getJobId(address), + DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS); + } + } + + private void cancelUnbindDeviceListener(String address) { + mMainHandler.removeCallbacks( + mUnbindDeviceListenersRunnable, mUnbindDeviceListenersRunnable.getJobId(address)); + } + + private void initBleScanning() { + Log.i(LOG_TAG, "initBleScanning()"); + + boolean bluetoothReady = mBluetoothAdapter.registerServiceLifecycleCallback( + new BluetoothAdapter.ServiceLifecycleCallback() { + @Override + public void onBluetoothServiceUp() { + Log.i(LOG_TAG, "Bluetooth stack is up"); + startBleScan(); + } + + @Override + public void onBluetoothServiceDown() { + Log.w(LOG_TAG, "Bluetooth stack is down"); + } + }); + if (bluetoothReady) { + startBleScan(); + } + } + + void startBleScan() { + Log.i(LOG_TAG, "startBleScan()"); + + List<ScanFilter> filters = getBleScanFilters(); + if (filters.isEmpty()) { + return; + } + BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner(); + if (scanner == null) { + Log.w(LOG_TAG, "scanner == null (likely BLE isn't ON yet)"); + } else { + scanner.startScan( + filters, + new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(), + mBleScanCallback); + } + } + + void restartBleScan() { + mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback); + startBleScan(); + } + + private List<ScanFilter> getBleScanFilters() { + ArrayList<ScanFilter> result = new ArrayList<>(); + ArraySet<String> addressesSeen = new ArraySet<>(); + for (Association association : getAllAssociations()) { + String address = association.getDeviceMacAddress(); + if (addressesSeen.contains(address)) { + continue; + } + if (association.isNotifyOnDeviceNearby()) { + result.add(new ScanFilter.Builder().setDeviceAddress(address).build()); + addressesSeen.add(address); + } + } + return result; } private AndroidFuture<String> getDeviceProfilePermissionDescription(String deviceProfile) { diff --git a/services/core/Android.bp b/services/core/Android.bp index e01c4df42ff5..37d2cdc16926 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -100,10 +100,10 @@ java_library_static { libs: [ "services.net", "android.hardware.light-V2.0-java", - "android.hardware.gnss-java", - "android.hardware.power-java", + "android.hardware.gnss-V1-java", + "android.hardware.power-V1-java", "android.hardware.power-V1.0-java", - "android.hardware.vibrator-java", + "android.hardware.vibrator-V1-java", "android.net.ipsec.ike.stubs.module_lib", "app-compat-annotations", "framework-tethering.stubs.module_lib", @@ -128,22 +128,22 @@ java_library_static { "android.hardware.health-V1.0-java", "android.hardware.health-V2.0-java", "android.hardware.health-V2.1-java", - "android.hardware.light-java", + "android.hardware.light-V1-java", "android.hardware.tv.cec-V1.0-java", "android.hardware.weaver-V1.0-java", "android.hardware.biometrics.face-V1.1-java", - "android.hardware.biometrics.face-java", + "android.hardware.biometrics.face-V1-java", "android.hardware.biometrics.fingerprint-V2.3-java", - "android.hardware.biometrics.fingerprint-java", + "android.hardware.biometrics.fingerprint-V1-java", "android.hardware.oemlock-V1.0-java", "android.hardware.configstore-V1.0-java", "android.hardware.contexthub-V1.0-java", - "android.hardware.rebootescrow-java", + "android.hardware.rebootescrow-V1-java", "android.hardware.soundtrigger-V2.3-java", - "android.hardware.power.stats-java", + "android.hardware.power.stats-V1-java", "android.hidl.manager-V1.2-java", "capture_state_listener-aidl-java", - "dnsresolver_aidl_interface-java", + "dnsresolver_aidl_interface-V7-java", "icu4j_calendar_astronomer", "netd-client", "overlayable_policy_aidl-java", diff --git a/services/core/java/android/content/pm/OWNERS b/services/core/java/android/content/pm/OWNERS new file mode 100644 index 000000000000..5eed0b509688 --- /dev/null +++ b/services/core/java/android/content/pm/OWNERS @@ -0,0 +1 @@ +include /core/java/android/content/pm/OWNERS
\ No newline at end of file diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index dad8bd826e3d..6886cdefc28a 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -997,28 +997,6 @@ public abstract class PackageManagerInternal { public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId); /** - * Register to listen for loading progress of an installed package. - * @param packageName The name of the installed package - * @param callback To loading reporting progress - * @param userId The user under which to check. - * @return Whether the registration was successful. It can fail if the package has not been - * installed yet. - */ - public abstract boolean registerInstalledLoadingProgressCallback(@NonNull String packageName, - @NonNull InstalledLoadingProgressCallback callback, int userId); - - /** - * Unregister to stop listening to loading progress of an installed package - * @param packageName The name of the installed package - * @param callback To unregister - * @return True if the callback is removed from registered callback list. False is the callback - * does not exist on the registered callback list, which can happen if the callback has - * already been unregistered. - */ - public abstract boolean unregisterInstalledLoadingProgressCallback(@NonNull String packageName, - @NonNull InstalledLoadingProgressCallback callback); - - /** * Returns the string representation of a known package. For example, * {@link #PACKAGE_SETUP_WIZARD} is represented by the string Setup Wizard. * @@ -1137,7 +1115,8 @@ public abstract class PackageManagerInternal { */ public abstract void requestChecksums(@NonNull String packageName, boolean includeSplits, @Checksum.Type int optional, @Checksum.Type int required, - @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId, + @Nullable List trustedInstallers, + @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId, @NonNull Executor executor, @NonNull Handler handler); /** diff --git a/services/core/java/android/os/OWNERS b/services/core/java/android/os/OWNERS new file mode 100644 index 000000000000..d0a2daf0905c --- /dev/null +++ b/services/core/java/android/os/OWNERS @@ -0,0 +1 @@ +per-file BatteryStats* = file:/BATTERY_STATS_OWNERS diff --git a/services/core/java/com/android/server/BundleUtils.java b/services/core/java/com/android/server/BundleUtils.java new file mode 100644 index 000000000000..20ebe2985a18 --- /dev/null +++ b/services/core/java/com/android/server/BundleUtils.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Bundle; + +/** + * Utility methods for handling {@link Bundle}. + * + */ +public final class BundleUtils { + private BundleUtils() { + } + + /** + * Returns true if {@code in} is null or empty. + */ + public static boolean isEmpty(@Nullable Bundle in) { + return (in == null) || (in.size() == 0); + } + + /** + * Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty + * bundle. + * + * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return + * {@link Bundle#EMPTY}) + */ + public static @NonNull Bundle clone(@Nullable Bundle in) { + return (in != null) ? new Bundle(in) : new Bundle(); + } + +} diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index c21a78a5b16e..06f935833d0b 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -45,6 +45,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; @@ -120,6 +121,7 @@ import android.net.NetworkState; import android.net.NetworkTestResultParcelable; import android.net.NetworkUtils; import android.net.NetworkWatchlistManager; +import android.net.OemNetworkPreferences; import android.net.PrivateDnsConfigParcel; import android.net.ProxyInfo; import android.net.QosCallbackException; @@ -281,15 +283,18 @@ public class ConnectivityService extends IConnectivityManager.Stub // connect anyway?" dialog after the user selects a network that doesn't validate. private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000; - // Default to 30s linger time-out. Modifiable only for testing. + // Default to 30s linger time-out, and 5s for nascent network. Modifiable only for testing. private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger"; private static final int DEFAULT_LINGER_DELAY_MS = 30_000; + private static final int DEFAULT_NASCENT_DELAY_MS = 5_000; // The maximum number of network request allowed per uid before an exception is thrown. private static final int MAX_NETWORK_REQUESTS_PER_UID = 100; @VisibleForTesting protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it. + @VisibleForTesting + protected int mNascentDelayMs; // How long to delay to removal of a pending intent based request. // See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS @@ -1034,7 +1039,8 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRanker = new NetworkRanker(); final NetworkRequest defaultInternetRequest = createDefaultInternetRequestForTransport( -1, NetworkRequest.Type.REQUEST); - mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder()); + mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder(), + null /* attributionTag */); mNetworkRequests.put(defaultInternetRequest, mDefaultRequest); mDefaultNetworkRequests.add(mDefaultRequest); mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest); @@ -1063,6 +1069,8 @@ public class ConnectivityService extends IConnectivityManager.Stub Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000); mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS); + // TODO: Consider making the timer customizable. + mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS; mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService"); mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService"); @@ -1240,6 +1248,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) { final NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); netCap.removeCapability(NET_CAPABILITY_NOT_VPN); netCap.setSingleUid(uid); return netCap; @@ -1249,6 +1258,7 @@ public class ConnectivityService extends IConnectivityManager.Stub int transportType, NetworkRequest.Type type) { final NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName()); if (transportType > -1) { netCap.addTransportType(transportType); @@ -1302,7 +1312,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (enable) { handleRegisterNetworkRequest(new NetworkRequestInfo( - null, networkRequest, new Binder())); + null, networkRequest, new Binder(), null /* attributionTag */)); } else { handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID, /* callOnUnavailable */ false); @@ -1637,7 +1647,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser( - int userId, String callingPackageName) { + int userId, String callingPackageName, @Nullable String callingAttributionTag) { // 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 @@ -1668,7 +1678,8 @@ public class ConnectivityService extends IConnectivityManager.Stub result.put( nai.network, createWithLocationInfoSanitizedIfNecessaryWhenParceled( - nc, mDeps.getCallingUid(), callingPackageName)); + nc, mDeps.getCallingUid(), callingPackageName, + callingAttributionTag)); } } @@ -1681,7 +1692,8 @@ public class ConnectivityService extends IConnectivityManager.Stub result.put( network, createWithLocationInfoSanitizedIfNecessaryWhenParceled( - nc, mDeps.getCallingUid(), callingPackageName)); + nc, mDeps.getCallingUid(), callingPackageName, + callingAttributionTag)); } } } @@ -1756,12 +1768,13 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override - public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) { + public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName, + @Nullable String callingAttributionTag) { mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName); enforceAccessPermission(); return createWithLocationInfoSanitizedIfNecessaryWhenParceled( getNetworkCapabilitiesInternal(network), - mDeps.getCallingUid(), callingPackageName); + mDeps.getCallingUid(), callingPackageName, callingAttributionTag); } @VisibleForTesting @@ -1780,11 +1793,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return newNc; } - private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName) { + private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName, + @Nullable String callingAttributionTag) { final long token = Binder.clearCallingIdentity(); try { return mLocationPermissionChecker.checkLocationPermission( - callerPkgName, null /* featureId */, callerUid, null /* message */); + callerPkgName, callingAttributionTag, callerUid, null /* message */); } finally { Binder.restoreCallingIdentity(token); } @@ -1793,7 +1807,8 @@ public class ConnectivityService extends IConnectivityManager.Stub @VisibleForTesting @Nullable NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled( - @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) { + @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName, + @Nullable String callingAttributionTag) { if (nc == null) { return null; } @@ -1802,7 +1817,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // Avoid doing location permission check if the transport info has no location sensitive // data. if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) { - hasLocationPermission = hasLocationPermission(callerUid, callerPkgName); + hasLocationPermission = + hasLocationPermission(callerUid, callerPkgName, callingAttributionTag); newNc = new NetworkCapabilities(nc, hasLocationPermission); } else { newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */); @@ -1819,7 +1835,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (hasLocationPermission == null) { // Location permission not checked yet, check now for masking owner UID. - hasLocationPermission = hasLocationPermission(callerUid, callerPkgName); + hasLocationPermission = + hasLocationPermission(callerUid, callerPkgName, callingAttributionTag); } // Reset owner uid if the app has no location permission. if (!hasLocationPermission) { @@ -2030,7 +2047,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mHandler.sendMessage(mHandler.obtainMessage( EVENT_PRIVATE_DNS_VALIDATION_UPDATE, new PrivateDnsValidationUpdate(netId, - InetAddress.parseNumericAddress(ipAddress), + InetAddresses.parseNumericAddress(ipAddress), hostname, validated))); } catch (IllegalArgumentException e) { loge("Error parsing ip address in validation event"); @@ -2142,8 +2159,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered, boolean isBackgroundRestricted) { - return NetworkPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules, - isNetworkMetered, isBackgroundRestricted); + return mPolicyManager.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered, + isBackgroundRestricted); } /** @@ -2726,9 +2743,9 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.println(nai.requestAt(i).toString()); } pw.decreaseIndent(); - pw.println("Lingered:"); + pw.println("Inactivity Timers:"); pw.increaseIndent(); - nai.dumpLingerTimers(pw); + nai.dumpInactivityTimers(pw); pw.decreaseIndent(); pw.decreaseIndent(); } @@ -3323,27 +3340,27 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** - * Updates the linger state from the network requests inside the NAI. + * Updates the inactivity state from the network requests inside the NAI. * @param nai the agent info to update * @param now the timestamp of the event causing this update - * @return whether the network was lingered as a result of this update + * @return whether the network was inactive as a result of this update */ - private boolean updateLingerState(@NonNull final NetworkAgentInfo nai, final long now) { - // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm. - // 2. If the network was lingering and there are now requests, unlinger it. + private boolean updateInactivityState(@NonNull final NetworkAgentInfo nai, final long now) { + // 1. Update the inactivity timer. If it's changed, reschedule or cancel the alarm. + // 2. If the network was inactive and there are now requests, unset inactive. // 3. If this network is unneeded (which implies it is not lingering), and there is at least - // one lingered request, start lingering. - nai.updateLingerTimer(); - if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) { - if (DBG) log("Unlingering " + nai.toShortString()); - nai.unlinger(); + // one lingered request, set inactive. + nai.updateInactivityTimer(); + if (nai.isInactive() && nai.numForegroundNetworkRequests() > 0) { + if (DBG) log("Unsetting inactive " + nai.toShortString()); + nai.unsetInactive(); logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER); - } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) { + } else if (unneeded(nai, UnneededFor.LINGER) && nai.getInactivityExpiry() > 0) { if (DBG) { - final int lingerTime = (int) (nai.getLingerExpiry() - now); - log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms"); + final int lingerTime = (int) (nai.getInactivityExpiry() - now); + log("Setting inactive " + nai.toShortString() + " for " + lingerTime + "ms"); } - nai.linger(); + nai.setInactive(); logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER); return true; } @@ -3357,7 +3374,6 @@ public class ConnectivityService extends IConnectivityManager.Stub if (VDBG) log("NetworkFactory connected"); // Finish setting up the full connection NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo); - npi.completeConnection(); sendAllRequestsToProvider(npi); } else { loge("Error connecting NetworkFactory"); @@ -3482,7 +3498,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } } - nai.clearLingerState(); + nai.clearInactivityState(); // TODO: mLegacyTypeTracker.remove seems redundant given there's a full rematch right after. // Currently, deleting it breaks tests that check for the default network disconnecting. // Find out why, fix the rematch code, and delete this. @@ -3589,10 +3605,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // As this request was not satisfied on rematch and thus never had any scores sent to the // factories, send null now for each request of type REQUEST. for (final NetworkRequest req : nri.mRequests) { - if (!req.isRequest()) { - continue; - } - sendUpdatedScoreToFactories(req, null); + if (req.isRequest()) sendUpdatedScoreToFactories(req, null); } } @@ -3632,7 +3645,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return true; } - if (!nai.everConnected || nai.isVPN() || nai.isLingering() || numRequests > 0) { + if (!nai.everConnected || nai.isVPN() || nai.isInactive() || numRequests > 0) { return false; } for (NetworkRequestInfo nri : mNetworkRequests.values()) { @@ -3769,7 +3782,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRequestInfoLogs.log("RELEASE " + nri); if (null != nri.getActiveRequest()) { - if (nri.getActiveRequest().isRequest()) { + if (!nri.getActiveRequest().isListen()) { removeSatisfiedNetworkRequestFromNetwork(nri); } else { nri.setSatisfier(null, null); @@ -3824,7 +3837,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // If there are still lingered requests on this network, don't tear it down, // but resume lingering instead. final long now = SystemClock.elapsedRealtime(); - if (updateLingerState(nai, now)) { + if (updateInactivityState(nai, now)) { notifyNetworkLosing(nai, now); } if (unneeded(nai, UnneededFor.TEARDOWN)) { @@ -5443,27 +5456,21 @@ public class ConnectivityService extends IConnectivityManager.Stub private static class NetworkProviderInfo { public final String name; public final Messenger messenger; - private final AsyncChannel mAsyncChannel; private final IBinder.DeathRecipient mDeathRecipient; public final int providerId; NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel, - int providerId, IBinder.DeathRecipient deathRecipient) { + int providerId, @NonNull IBinder.DeathRecipient deathRecipient) { this.name = name; this.messenger = messenger; this.providerId = providerId; - mAsyncChannel = asyncChannel; mDeathRecipient = deathRecipient; - if ((mAsyncChannel == null) == (mDeathRecipient == null)) { - throw new AssertionError("Must pass exactly one of asyncChannel or deathRecipient"); + if (mDeathRecipient == null) { + throw new AssertionError("Must pass a deathRecipient"); } } - boolean isLegacyNetworkFactory() { - return mAsyncChannel != null; - } - void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) { try { messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj)); @@ -5474,38 +5481,19 @@ public class ConnectivityService extends IConnectivityManager.Stub } void requestNetwork(NetworkRequest request, int score, int servingProviderId) { - if (isLegacyNetworkFactory()) { - mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, - servingProviderId, request); - } else { - sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score, + sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score, servingProviderId, request); - } } void cancelRequest(NetworkRequest request) { - if (isLegacyNetworkFactory()) { - mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request); - } else { - sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request); - } + sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request); } void connect(Context context, Handler handler) { - if (isLegacyNetworkFactory()) { - mAsyncChannel.connect(context, handler, messenger); - } else { - try { - messenger.getBinder().linkToDeath(mDeathRecipient, 0); - } catch (RemoteException e) { - mDeathRecipient.binderDied(); - } - } - } - - void completeConnection() { - if (isLegacyNetworkFactory()) { - mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); + try { + messenger.getBinder().linkToDeath(mDeathRecipient, 0); + } catch (RemoteException e) { + mDeathRecipient.binderDied(); } } } @@ -5542,7 +5530,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } // The network currently satisfying this NRI. Only one request in an NRI can have a - // satisfier. For non-multilayer requests, only REQUEST-type requests can have a satisfier. + // satisfier. For non-multilayer requests, only non-listen requests can have a satisfier. @Nullable private NetworkAgentInfo mSatisfier; NetworkAgentInfo getSatisfier() { @@ -5564,6 +5552,8 @@ public class ConnectivityService extends IConnectivityManager.Stub final int mPid; final int mUid; final Messenger messenger; + @Nullable + final String mCallingAttributionTag; /** * Get the list of UIDs this nri applies to. @@ -5577,7 +5567,8 @@ public class ConnectivityService extends IConnectivityManager.Stub return uids; } - NetworkRequestInfo(NetworkRequest r, PendingIntent pi) { + NetworkRequestInfo(NetworkRequest r, PendingIntent pi, + @Nullable String callingAttributionTag) { mRequests = initializeRequests(r); ensureAllNetworkRequestsHaveType(mRequests); mPendingIntent = pi; @@ -5586,9 +5577,11 @@ public class ConnectivityService extends IConnectivityManager.Stub mPid = getCallingPid(); mUid = mDeps.getCallingUid(); mNetworkRequestCounter.incrementCountOrThrow(mUid); + mCallingAttributionTag = callingAttributionTag; } - NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) { + NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, + @Nullable String callingAttributionTag) { super(); messenger = m; mRequests = initializeRequests(r); @@ -5598,6 +5591,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mUid = mDeps.getCallingUid(); mPendingIntent = null; mNetworkRequestCounter.incrementCountOrThrow(mUid); + mCallingAttributionTag = callingAttributionTag; try { mBinder.linkToDeath(this, 0); @@ -5607,7 +5601,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } NetworkRequestInfo(NetworkRequest r) { - this(r, null); + this(r, null /* pi */, null /* callingAttributionTag */); } // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer @@ -5800,7 +5794,8 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType, nextNetworkRequestId(), reqType); - NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder); + NetworkRequestInfo nri = + new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag); if (DBG) log("requestNetwork for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri)); @@ -5889,7 +5884,8 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.REQUEST); - NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation); + NetworkRequestInfo nri = + new NetworkRequestInfo(networkRequest, operation, callingAttributionTag); if (DBG) log("pendingRequest for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT, nri)); @@ -5933,7 +5929,8 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities, - Messenger messenger, IBinder binder, @NonNull String callingPackageName) { + Messenger messenger, IBinder binder, @NonNull String callingPackageName, + @Nullable String callingAttributionTag) { final int callingUid = mDeps.getCallingUid(); if (!hasWifiNetworkListenPermission(networkCapabilities)) { enforceAccessPermission(); @@ -5953,7 +5950,8 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); - NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder); + NetworkRequestInfo nri = + new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag); if (VDBG) log("listenForNetwork for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri)); @@ -5962,7 +5960,8 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void pendingListenForNetwork(NetworkCapabilities networkCapabilities, - PendingIntent operation, @NonNull String callingPackageName) { + PendingIntent operation, @NonNull String callingPackageName, + @Nullable String callingAttributionTag) { Objects.requireNonNull(operation, "PendingIntent cannot be null."); final int callingUid = mDeps.getCallingUid(); if (!hasWifiNetworkListenPermission(networkCapabilities)) { @@ -5976,7 +5975,8 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); - NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation); + NetworkRequestInfo nri = + new NetworkRequestInfo(networkRequest, operation, callingAttributionTag); if (VDBG) log("pendingListenForNetwork for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri)); @@ -6000,15 +6000,6 @@ public class ConnectivityService extends IConnectivityManager.Stub EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(), 0, networkRequest)); } - @Override - public int registerNetworkFactory(Messenger messenger, String name) { - enforceNetworkFactoryPermission(); - NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, new AsyncChannel(), - nextNetworkProviderId(), null /* deathRecipient */); - mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi)); - return npi.providerId; - } - private void handleRegisterNetworkProvider(NetworkProviderInfo npi) { if (mNetworkProviderInfos.containsKey(npi.messenger)) { // Avoid creating duplicates. even if an app makes a direct AIDL call. @@ -6022,10 +6013,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) log("Got NetworkProvider Messenger for " + npi.name); mNetworkProviderInfos.put(npi.messenger, npi); npi.connect(mContext, mTrackerHandler); - if (!npi.isLegacyNetworkFactory()) { - // Legacy NetworkFactories get their requests when their AsyncChannel connects. - sendAllRequestsToProvider(npi); - } + sendAllRequestsToProvider(npi); } @Override @@ -6044,11 +6032,6 @@ public class ConnectivityService extends IConnectivityManager.Stub mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger)); } - @Override - public void unregisterNetworkFactory(Messenger messenger) { - unregisterNetworkProvider(messenger); - } - private void handleUnregisterNetworkProvider(Messenger messenger) { NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger); if (npi == null) { @@ -7047,8 +7030,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) { for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest nr = nai.requestAt(i); - // Don't send listening requests to factories. b/17393458 - if (nr.isListen()) continue; + // Don't send listening or track default request to factories. b/17393458 + if (!nr.isRequest()) continue; sendUpdatedScoreToFactories(nr, nai); } } @@ -7110,10 +7093,10 @@ public class ConnectivityService extends IConnectivityManager.Stub ensureRunningOnConnectivityServiceThread(); for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) { for (final NetworkRequest req : nri.mRequests) { - if (req.isListen() && nri.getActiveRequest() == req) { + if (!req.isRequest() && nri.getActiveRequest() == req) { break; } - if (req.isListen()) { + if (!req.isRequest()) { continue; } // Only set the nai for the request it is satisfying. @@ -7208,7 +7191,8 @@ public class ConnectivityService extends IConnectivityManager.Stub putParcelable( bundle, createWithLocationInfoSanitizedIfNecessaryWhenParceled( - nc, nri.mUid, nrForCallback.getRequestorPackageName())); + nc, nri.mUid, nrForCallback.getRequestorPackageName(), + nri.mCallingAttributionTag)); putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions( networkAgent.linkProperties, nri.mPid, nri.mUid)); // For this notification, arg1 contains the blocked status. @@ -7227,7 +7211,8 @@ public class ConnectivityService extends IConnectivityManager.Stub putParcelable( bundle, createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, nri.mUid, nrForCallback.getRequestorPackageName())); + netCap, nri.mUid, nrForCallback.getRequestorPackageName(), + nri.mCallingAttributionTag)); break; } case ConnectivityManager.CALLBACK_IP_CHANGED: { @@ -7263,8 +7248,8 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nai.numRequestNetworkRequests() != 0) { for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest nr = nai.requestAt(i); - // Ignore listening requests. - if (nr.isListen()) continue; + // Ignore listening and track default requests. + if (!nr.isRequest()) continue; loge("Dead network still had at least " + nr); break; } @@ -7281,13 +7266,13 @@ public class ConnectivityService extends IConnectivityManager.Stub // If we get here it means that the last linger timeout for this network expired. So there // must be no other active linger timers, and we must stop lingering. - oldNetwork.clearLingerState(); + oldNetwork.clearInactivityState(); if (unneeded(oldNetwork, UnneededFor.TEARDOWN)) { // Tear the network down. teardownUnneededNetwork(oldNetwork); } else { - // Put the network in the background. + // Put the network in the background if it doesn't satisfy any foreground request. updateCapabilitiesForNetwork(oldNetwork); } } @@ -7541,6 +7526,14 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { if (VDBG || DDBG) log(" accepting network in place of null"); } + + // To prevent constantly CPU wake up for nascent timer, if a network comes up + // and immediately satisfies a request then remove the timer. This will happen for + // all networks except in the case of an underlying network for a VCN. + if (newSatisfier.isNascent()) { + newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE); + } + newSatisfier.unlingerRequest(newRequest.requestId); if (!newSatisfier.addRequest(newRequest)) { Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has " @@ -7683,19 +7676,19 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - // Update the linger state before processing listen callbacks, because the background - // computation depends on whether the network is lingering. Don't send the LOSING callbacks + // Update the inactivity state before processing listen callbacks, because the background + // computation depends on whether the network is inactive. Don't send the LOSING callbacks // just yet though, because they have to be sent after the listens are processed to keep // backward compatibility. - final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>(); + final ArrayList<NetworkAgentInfo> inactiveNetworks = new ArrayList<>(); for (final NetworkAgentInfo nai : nais) { - // Rematching may have altered the linger state of some networks, so update all linger - // timers. updateLingerState reads the state from the network agent and does nothing - // if the state has not changed : the source of truth is controlled with - // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been - // called while rematching the individual networks above. - if (updateLingerState(nai, now)) { - lingeredNetworks.add(nai); + // Rematching may have altered the inactivity state of some networks, so update all + // inactivity timers. updateInactivityState reads the state from the network agent + // and does nothing if the state has not changed : the source of truth is controlled + // with NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which + // have been called while rematching the individual networks above. + if (updateInactivityState(nai, now)) { + inactiveNetworks.add(nai); } } @@ -7712,7 +7705,11 @@ public class ConnectivityService extends IConnectivityManager.Stub processNewlySatisfiedListenRequests(nai); } - for (final NetworkAgentInfo nai : lingeredNetworks) { + for (final NetworkAgentInfo nai : inactiveNetworks) { + // For nascent networks, if connecting with no foreground request, skip broadcasting + // LOSING for backward compatibility. This is typical when mobile data connected while + // wifi connected with mobile data always-on enabled. + if (nai.isNascent()) continue; notifyNetworkLosing(nai, now); } @@ -7721,7 +7718,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Tear down all unneeded networks. for (NetworkAgentInfo nai : mNetworkAgentInfos) { if (unneeded(nai, UnneededFor.TEARDOWN)) { - if (nai.getLingerExpiry() > 0) { + if (nai.getInactivityExpiry() > 0) { // This network has active linger timers and no requests, but is not // lingering. Linger it. // @@ -7729,7 +7726,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // and became unneeded due to another network improving its score to the // point where this network will no longer be able to satisfy any requests // even if it validates. - if (updateLingerState(nai, now)) { + if (updateInactivityState(nai, now)) { notifyNetworkLosing(nai, now); } } else { @@ -7953,6 +7950,15 @@ public class ConnectivityService extends IConnectivityManager.Stub // doing. updateSignalStrengthThresholds(networkAgent, "CONNECT", null); + // Before first rematching networks, put an inactivity timer without any request, this + // allows {@code updateInactivityState} to update the state accordingly and prevent + // tearing down for any {@code unneeded} evaluation in this period. + // Note that the timer will not be rescheduled since the expiry time is + // fixed after connection regardless of the network satisfying other requests or not. + // But it will be removed as soon as the network satisfies a request for the first time. + networkAgent.lingerRequest(NetworkRequest.REQUEST_ID_NONE, + SystemClock.elapsedRealtime(), mNascentDelayMs); + // Consider network even though it is not yet validated. rematchAllNetworksAndRequests(); @@ -8006,7 +8012,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Notify the requests on this NAI that the network is now lingered. private void notifyNetworkLosing(@NonNull final NetworkAgentInfo nai, final long now) { - final int lingerTime = (int) (nai.getLingerExpiry() - now); + final int lingerTime = (int) (nai.getInactivityExpiry() - now); notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime); } @@ -9143,6 +9149,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } } + /** * Registers {@link QosSocketFilter} with {@link IQosCallback}. * @@ -9192,4 +9199,10 @@ public class ConnectivityService extends IConnectivityManager.Stub public void unregisterQosCallback(@NonNull final IQosCallback callback) { mQosCallbackTracker.unregisterCallback(callback); } + + @Override + public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) { + // TODO http://b/176495594 track multiple default networks with networkPreferences + if (DBG) log("setOemNetworkPreference() called with: " + preference.toString()); + } } diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java index e96fd390f15a..96f832d26816 100644 --- a/services/core/java/com/android/server/TestNetworkService.java +++ b/services/core/java/com/android/server/TestNetworkService.java @@ -50,6 +50,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.net.module.util.NetdUtils; +import com.android.net.module.util.NetworkStackConstants; import java.io.UncheckedIOException; import java.net.Inet4Address; @@ -280,10 +281,12 @@ class TestNetworkService extends ITestNetworkManager.Stub { // Add global routes (but as non-default, non-internet providing network) if (allowIPv4) { - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null, iface)); + lp.addRoute(new RouteInfo(new IpPrefix( + NetworkStackConstants.IPV4_ADDR_ANY, 0), null, iface)); } if (allowIPv6) { - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface)); + lp.addRoute(new RouteInfo(new IpPrefix( + NetworkStackConstants.IPV6_ADDR_ANY, 0), null, iface)); } final TestNetworkAgent agent = new TestNetworkAgent(context, looper, nc, lp, diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 916bec27af39..4dce59f23a79 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -66,6 +66,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.concurrent.TimeUnit; /** @@ -291,8 +292,9 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, - @NonNull TelephonySubscriptionSnapshot snapshot) { - return new Vcn(vcnContext, subscriptionGroup, config, snapshot); + @NonNull TelephonySubscriptionSnapshot snapshot, + @NonNull VcnSafemodeCallback safemodeCallback) { + return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback); } /** Gets the subId indicated by the given {@link WifiInfo}. */ @@ -438,7 +440,12 @@ public class VcnManagementService extends IVcnManagementService.Stub { // TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active // VCN. - final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot); + final VcnSafemodeCallbackImpl safemodeCallback = + new VcnSafemodeCallbackImpl(subscriptionGroup); + + final Vcn newInstance = + mDeps.newVcn( + mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback); mVcns.put(subscriptionGroup, newInstance); // Now that a new VCN has started, notify all registered listeners to refresh their @@ -536,7 +543,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { } } - /** Get current configuration list for testing purposes */ + /** Get current VCNs for testing purposes */ @VisibleForTesting(visibility = Visibility.PRIVATE) public Map<ParcelUuid, Vcn> getAllVcns() { synchronized (mLock) { @@ -638,8 +645,8 @@ public class VcnManagementService extends IVcnManagementService.Stub { synchronized (mLock) { ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId); - // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode - if (mVcns.containsKey(subGroup)) { + Vcn vcn = mVcns.get(subGroup); + if (vcn != null && vcn.isActive()) { isVcnManagedNetwork = true; } } @@ -651,4 +658,31 @@ public class VcnManagementService extends IVcnManagementService.Stub { return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities); } + + /** Callback for signalling when a Vcn has entered Safemode. */ + public interface VcnSafemodeCallback { + /** Called by a Vcn to signal that it has entered Safemode. */ + void onEnteredSafemode(); + } + + /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */ + private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback { + @NonNull private final ParcelUuid mSubGroup; + + private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) { + mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup"); + } + + @Override + public void onEnteredSafemode() { + synchronized (mLock) { + // Ignore if this subscription group doesn't exist anymore + if (!mVcns.containsKey(mSubGroup)) { + return; + } + + notifyAllPolicyListenersLocked(); + } + } + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ecb915f49bc3..9382e1aa2a9e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -145,6 +145,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManagerInternal; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.ActivityThread; +import android.app.AnrController; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.AppOpsManagerInternal.CheckOpsDelegate; @@ -5632,7 +5633,7 @@ public class ActivityManagerService extends IActivityManager.Stub final int modeFlags, int userId) { enforceNotIsolatedCaller("grantUriPermission"); GrantUri grantUri = new GrantUri(userId, uri, modeFlags); - synchronized (mProcLock) { + synchronized (this) { final ProcessRecord r = getRecordForAppLOSP(caller); if (r == null) { throw new SecurityException("Unable to find app for caller " @@ -5666,7 +5667,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void revokeUriPermission(IApplicationThread caller, String targetPackage, Uri uri, final int modeFlags, int userId) { enforceNotIsolatedCaller("revokeUriPermission"); - synchronized (mProcLock) { + synchronized (this) { final ProcessRecord r = getRecordForAppLOSP(caller); if (r == null) { throw new SecurityException("Unable to find app for caller " @@ -15861,6 +15862,16 @@ public class ActivityManagerService extends IActivityManager.Stub // PackageManagerService. return mConstants.mBootTimeTempAllowlistDuration; } + + @Override + public void registerAnrController(AnrController controller) { + mActivityTaskManager.registerAnrController(controller); + } + + @Override + public void unregisterAnrController(AnrController controller) { + mActivityTaskManager.unregisterAnrController(controller); + } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index fe71fbf79157..c971bd2ab6d5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2984,7 +2984,13 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println("Reset all changes for " + packageName + " to default value."); return 0; } - if (platformCompat.clearOverride(changeId, packageName)) { + boolean existed; + if (killPackage) { + existed = platformCompat.clearOverride(changeId, packageName); + } else { + existed = platformCompat.clearOverrideForTest(changeId, packageName); + } + if (existed) { pw.println("Reset change " + changeId + " for " + packageName + " to default value."); } else { diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java index 64e307a5f182..165312352990 100644 --- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java @@ -416,6 +416,14 @@ class ProcessErrorStateRecord { return; } + // Retrieve max ANR delay from AnrControllers without the mService lock since the + // controllers might in turn call into apps + long anrDialogDelayMs = mService.mActivityTaskManager.getMaxAnrDelayMillis(aInfo); + if (aInfo != null && aInfo.packageName != null && anrDialogDelayMs > 0) { + Slog.i(TAG, "Delaying ANR dialog for " + aInfo.packageName + " for " + anrDialogDelayMs + + "ms"); + } + synchronized (mService) { // mBatteryStatsService can be null if the AMS is constructed with injector only. This // will only happen in tests. @@ -447,7 +455,7 @@ class ProcessErrorStateRecord { msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG; msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem); - mService.mUiHandler.sendMessage(msg); + mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs); } } } diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 7299e814b020..e022e977e02f 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -81,6 +81,7 @@ public class SettingsToPropertiesMapper { static final String[] sDeviceConfigScopes = new String[] { DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, DeviceConfig.NAMESPACE_CONFIGURATION, + DeviceConfig.NAMESPACE_CONNECTIVITY, DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT, DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS, DeviceConfig.NAMESPACE_MEDIA_NATIVE, diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 9aea7c4c6dad..f72fb1f74fa8 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -765,10 +765,6 @@ import java.util.concurrent.atomic.AtomicBoolean; return mAudioService.getVssVolumeForDevice(streamType, device); } - /*package*/ int getModeOwnerPid() { - return mModeOwnerPid; - } - /*package*/ int getDeviceForStream(int streamType) { return mAudioService.getDeviceForStream(streamType); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 1529d71ad0c7..6f625a745ef6 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -95,6 +95,7 @@ import android.media.IVolumeController; import android.media.MediaExtractor; import android.media.MediaFormat; import android.media.MediaMetrics; +import android.media.MediaRecorder.AudioSource; import android.media.PlayerBase; import android.media.VolumePolicy; import android.media.audiofx.AudioEffect; @@ -161,9 +162,11 @@ import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -300,6 +303,10 @@ public class AudioService extends IAudioService.Stub private static final int MSG_STREAM_DEVICES_CHANGED = 32; private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33; private static final int MSG_REINIT_VOLUMES = 34; + private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35; + private static final int MSG_UPDATE_AUDIO_MODE = 36; + private static final int MSG_RECORDING_CONFIG_CHANGE = 37; + // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -699,8 +706,9 @@ public class AudioService extends IAudioService.Stub private long mLoweredFromNormalToVibrateTime; // Array of Uids of valid accessibility services to check if caller is one of them - private int[] mAccessibilityServiceUids; private final Object mAccessibilityServiceUidsLock = new Object(); + @GuardedBy("mAccessibilityServiceUidsLock") + private int[] mAccessibilityServiceUids; // Uid of the active input method service to check if caller is the one or not. private int mInputMethodServiceUid = android.os.Process.INVALID_UID; @@ -939,6 +947,7 @@ public class AudioService extends IAudioService.Stub mDeviceBroker = new AudioDeviceBroker(mContext, this); mRecordMonitor = new RecordingActivityMonitor(mContext); + mRecordMonitor.registerRecordingCallback(mVoiceRecordingActivityMonitor, true); // must be called before readPersistedSettings() which needs a valid mStreamVolumeAlias[] // array initialized by updateStreamVolumeAlias() @@ -948,6 +957,8 @@ public class AudioService extends IAudioService.Stub mPlaybackMonitor = new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]); + mPlaybackMonitor.registerPlaybackCallback(mVoicePlaybackActivityMonitor, true); + mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor); readAndSetLowRamDevice(); @@ -1196,12 +1207,8 @@ public class AudioService extends IAudioService.Stub // Restore call state synchronized (mDeviceBroker.mSetModeLock) { - if (mAudioSystem.setPhoneState(mMode, getModeOwnerUid()) - == AudioSystem.AUDIO_STATUS_OK) { - mModeLogger.log(new AudioEventLogger.StringEvent( - "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode) - + ", uid=" + getModeOwnerUid() + ")")); - } + onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(), + mContext.getPackageName()); } final int forSys; synchronized (mSettingsLock) { @@ -2989,7 +2996,7 @@ public class AudioService extends IAudioService.Stub } /*package*/ int getHearingAidStreamType() { - return getHearingAidStreamType(mMode); + return getHearingAidStreamType(getMode()); } private int getHearingAidStreamType(int mode) { @@ -3002,15 +3009,15 @@ public class AudioService extends IAudioService.Stub // other conditions will influence the stream type choice, read on... break; } - if (mVoiceActive.get()) { + if (mVoicePlaybackActive.get()) { return AudioSystem.STREAM_VOICE_CALL; } return AudioSystem.STREAM_MUSIC; } - private AtomicBoolean mVoiceActive = new AtomicBoolean(false); + private AtomicBoolean mVoicePlaybackActive = new AtomicBoolean(false); - private final IPlaybackConfigDispatcher mVoiceActivityMonitor = + private final IPlaybackConfigDispatcher mVoicePlaybackActivityMonitor = new IPlaybackConfigDispatcher.Stub() { @Override public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs, @@ -3032,16 +3039,126 @@ public class AudioService extends IAudioService.Stub break; } } - if (mVoiceActive.getAndSet(voiceActive) != voiceActive) { + if (mVoicePlaybackActive.getAndSet(voiceActive) != voiceActive) { updateHearingAidVolumeOnVoiceActivityUpdate(); } + + // Update playback active state for all apps in audio mode stack. + // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE + // and request an audio mode update immediately. Upon any other change, queue the message + // and request an audio mode update after a grace period. + synchronized (mDeviceBroker.mSetModeLock) { + boolean updateAudioMode = false; + int existingMsgPolicy = SENDMSG_QUEUE; + int delay = CHECK_MODE_FOR_UID_PERIOD_MS; + for (SetModeDeathHandler h : mSetModeDeathHandlers) { + boolean wasActive = h.isActive(); + h.setPlaybackActive(false); + for (AudioPlaybackConfiguration config : configs) { + final int usage = config.getAudioAttributes().getUsage(); + if (config.getClientUid() == h.getUid() + && (usage == AudioAttributes.USAGE_VOICE_COMMUNICATION + || usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING) + && config.getPlayerState() + == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + h.setPlaybackActive(true); + break; + } + } + if (wasActive != h.isActive()) { + updateAudioMode = true; + if (h.isActive() && h == getAudioModeOwnerHandler()) { + existingMsgPolicy = SENDMSG_REPLACE; + delay = 0; + } + } + } + if (updateAudioMode) { + sendMsg(mAudioHandler, + MSG_UPDATE_AUDIO_MODE, + existingMsgPolicy, + AudioSystem.MODE_CURRENT, + android.os.Process.myPid(), + mContext.getPackageName(), + delay); + } + } + } + + private final IRecordingConfigDispatcher mVoiceRecordingActivityMonitor = + new IRecordingConfigDispatcher.Stub() { + @Override + public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) { + sendMsg(mAudioHandler, MSG_RECORDING_CONFIG_CHANGE, SENDMSG_REPLACE, + 0 /*arg1 ignored*/, 0 /*arg2 ignored*/, + configs /*obj*/, 0 /*delay*/); + } + }; + + private void onRecordingConfigChange(List<AudioRecordingConfiguration> configs) { + // Update recording active state for all apps in audio mode stack. + // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE + // and request an audio mode update immediately. Upon any other change, queue the message + // and request an audio mode update after a grace period. + synchronized (mDeviceBroker.mSetModeLock) { + boolean updateAudioMode = false; + int existingMsgPolicy = SENDMSG_QUEUE; + int delay = CHECK_MODE_FOR_UID_PERIOD_MS; + for (SetModeDeathHandler h : mSetModeDeathHandlers) { + boolean wasActive = h.isActive(); + h.setRecordingActive(false); + for (AudioRecordingConfiguration config : configs) { + if (config.getClientUid() == h.getUid() + && config.getAudioSource() == AudioSource.VOICE_COMMUNICATION) { + h.setRecordingActive(true); + break; + } + } + if (wasActive != h.isActive()) { + updateAudioMode = true; + if (h.isActive() && h == getAudioModeOwnerHandler()) { + existingMsgPolicy = SENDMSG_REPLACE; + delay = 0; + } + } + } + if (updateAudioMode) { + sendMsg(mAudioHandler, + MSG_UPDATE_AUDIO_MODE, + existingMsgPolicy, + AudioSystem.MODE_CURRENT, + android.os.Process.myPid(), + mContext.getPackageName(), + delay); + } + } + } + + private void dumpAudioMode(PrintWriter pw) { + pw.println("\nAudio mode: "); + pw.println("- Current mode = " + AudioSystem.modeToString(getMode())); + pw.println("- Mode owner: "); + SetModeDeathHandler hdlr = getAudioModeOwnerHandler(); + if (hdlr != null) { + hdlr.dump(pw, -1); + } else { + pw.println(" None"); + } + pw.println("- Mode owner stack: "); + if (mSetModeDeathHandlers.isEmpty()) { + pw.println(" Empty"); + } else { + for (int i = 0; i < mSetModeDeathHandlers.size(); i++) { + mSetModeDeathHandlers.get(i).dump(pw, i); + } + } } private void updateHearingAidVolumeOnVoiceActivityUpdate() { final int streamType = getHearingAidStreamType(); final int index = getStreamVolume(streamType); sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID, - mVoiceActive.get(), streamType, index)); + mVoicePlaybackActive.get(), streamType, index)); mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType); } @@ -4058,65 +4175,46 @@ public class AudioService extends IAudioService.Stub } - /** - * Return the pid of the current audio mode owner - * @return 0 if nobody owns the mode - */ - /*package*/ int getModeOwnerPid() { - int modeOwnerPid = 0; - try { - modeOwnerPid = mSetModeDeathHandlers.get(0).getPid(); - } catch (Exception e) { - // nothing to do, modeOwnerPid is not modified - } - return modeOwnerPid; - } - - /** - * Return the uid of the current audio mode owner - * @return 0 if nobody owns the mode - */ - /*package*/ int getModeOwnerUid() { - int modeOwnerUid = 0; - try { - modeOwnerUid = mSetModeDeathHandlers.get(0).getUid(); - } catch (Exception e) { - // nothing to do, modeOwnerUid is not modified - } - return modeOwnerUid; - } - private class SetModeDeathHandler implements IBinder.DeathRecipient { private final IBinder mCb; // To be notified of client's death private final int mPid; private final int mUid; private final boolean mIsPrivileged; private final String mPackage; - private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client + private int mMode; + private long mUpdateTime; + private boolean mPlaybackActive = false; + private boolean mRecordingActive = false; - SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, String caller) { + SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, + String caller, int mode) { + mMode = mode; mCb = cb; mPid = pid; mUid = uid; - mIsPrivileged = isPrivileged; mPackage = caller; + mIsPrivileged = isPrivileged; + mUpdateTime = java.lang.System.currentTimeMillis(); } public void binderDied() { - int newModeOwnerPid = 0; synchronized (mDeviceBroker.mSetModeLock) { - Log.w(TAG, "setMode() client died"); + Log.w(TAG, "SetModeDeathHandler client died"); int index = mSetModeDeathHandlers.indexOf(this); if (index < 0) { - Log.w(TAG, "unregistered setMode() client died"); + Log.w(TAG, "unregistered SetModeDeathHandler client died"); } else { - newModeOwnerPid = setModeInt( - AudioSystem.MODE_NORMAL, mCb, mPid, mUid, mIsPrivileged, TAG); + SetModeDeathHandler h = mSetModeDeathHandlers.get(index); + mSetModeDeathHandlers.remove(index); + sendMsg(mAudioHandler, + MSG_UPDATE_AUDIO_MODE, + SENDMSG_QUEUE, + AudioSystem.MODE_CURRENT, + android.os.Process.myPid(), + mContext.getPackageName(), + 0); } } - // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all - // SCO connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, AudioService.this.getMode()); } public int getPid() { @@ -4125,6 +4223,7 @@ public class AudioService extends IAudioService.Stub public void setMode(int mode) { mMode = mode; + mUpdateTime = java.lang.System.currentTimeMillis(); } public int getMode() { @@ -4146,197 +4245,284 @@ public class AudioService extends IAudioService.Stub public boolean isPrivileged() { return mIsPrivileged; } + + public long getUpdateTime() { + return mUpdateTime; + } + + public void setPlaybackActive(boolean active) { + mPlaybackActive = active; + } + + public void setRecordingActive(boolean active) { + mRecordingActive = active; + } + + /** + * An app is considered active if: + * - It is privileged (has MODIFY_PHONE_STATE permission) + * or + * - It requests mode MODE_IN_COMMUNICATION, and it is either playing + * or recording for VOICE_COMMUNICATION. + * or + * - It requests a mode different from MODE_IN_COMMUNICATION or MODE_NORMAL + */ + public boolean isActive() { + return mIsPrivileged + || ((mMode == AudioSystem.MODE_IN_COMMUNICATION) + && (mRecordingActive || mPlaybackActive)) + || mMode == AudioSystem.MODE_IN_CALL + || mMode == AudioSystem.MODE_RINGTONE + || mMode == AudioSystem.MODE_CALL_SCREENING; + } + + public void dump(PrintWriter pw, int index) { + SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss:SSS"); + + if (index >= 0) { + pw.println(" Requester # " + (index + 1) + ":"); + } + pw.println(" - Mode: " + AudioSystem.modeToString(mMode)); + pw.println(" - Binder: " + mCb); + pw.println(" - Pid: " + mPid); + pw.println(" - Uid: " + mUid); + pw.println(" - Package: " + mPackage); + pw.println(" - Privileged: " + mIsPrivileged); + pw.println(" - Active: " + isActive()); + pw.println(" Playback active: " + mPlaybackActive); + pw.println(" Recording active: " + mRecordingActive); + pw.println(" - update time: " + format.format(new Date(mUpdateTime))); + } + } + + @GuardedBy("mDeviceBroker.mSetModeLock") + private SetModeDeathHandler getAudioModeOwnerHandler() { + // The Audio mode owner is: + // 1) the most recent privileged app in the stack + // 2) the most recent active app in the tack + SetModeDeathHandler modeOwner = null; + SetModeDeathHandler privilegedModeOwner = null; + for (SetModeDeathHandler h : mSetModeDeathHandlers) { + if (h.isActive()) { + // privileged apps are always active + if (h.isPrivileged()) { + if (privilegedModeOwner == null + || h.getUpdateTime() > privilegedModeOwner.getUpdateTime()) { + privilegedModeOwner = h; + } + } else { + if (modeOwner == null + || h.getUpdateTime() > modeOwner.getUpdateTime()) { + modeOwner = h; + } + } + } + } + return privilegedModeOwner != null ? privilegedModeOwner : modeOwner; + } + + /** + * Return the pid of the current audio mode owner + * @return 0 if nobody owns the mode + */ + @GuardedBy("mDeviceBroker.mSetModeLock") + /*package*/ int getModeOwnerPid() { + SetModeDeathHandler hdlr = getAudioModeOwnerHandler(); + if (hdlr != null) { + return hdlr.getPid(); + } + return 0; + } + + /** + * Return the uid of the current audio mode owner + * @return 0 if nobody owns the mode + */ + @GuardedBy("mDeviceBroker.mSetModeLock") + /*package*/ int getModeOwnerUid() { + SetModeDeathHandler hdlr = getAudioModeOwnerHandler(); + if (hdlr != null) { + return hdlr.getUid(); + } + return 0; } /** @see AudioManager#setMode(int) */ public void setMode(int mode, IBinder cb, String callingPackage) { + int pid = Binder.getCallingPid(); + int uid = Binder.getCallingUid(); if (DEBUG_MODE) { - Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")"); + Log.v(TAG, "setMode(mode=" + mode + ", pid=" + pid + + ", uid=" + uid + ", caller=" + callingPackage + ")"); } if (!checkAudioSettingsPermission("setMode()")) { return; } - final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission( - android.Manifest.permission.MODIFY_PHONE_STATE) - == PackageManager.PERMISSION_GRANTED; - final int callingPid = Binder.getCallingPid(); - if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) { - Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid=" - + callingPid + ", uid=" + Binder.getCallingUid()); + if (cb == null) { + Log.e(TAG, "setMode() called with null binder"); + return; + } + if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) { + Log.w(TAG, "setMode() invalid mode: " + mode); return; } + if (mode == AudioSystem.MODE_CURRENT) { + mode = getMode(); + } + if (mode == AudioSystem.MODE_CALL_SCREENING && !mIsCallScreeningModeSupported) { Log.w(TAG, "setMode(MODE_CALL_SCREENING) not permitted " + "when call screening is not supported"); return; } - if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) { + final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_PHONE_STATE) + == PackageManager.PERMISSION_GRANTED; + if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) { + Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid=" + + pid + ", uid=" + Binder.getCallingUid()); return; } - int newModeOwnerPid; + SetModeDeathHandler currentModeHandler = null; synchronized (mDeviceBroker.mSetModeLock) { - if (mode == AudioSystem.MODE_CURRENT) { - mode = mMode; - } - int oldModeOwnerPid = getModeOwnerPid(); - // Do not allow changing mode if a call is active and the requester - // does not have permission to modify phone state or is not the mode owner, - // unless returning to NORMAL mode (will not change current mode owner) or - // not changing mode in which case the mode owner will reflect the last - // requester of current mode - if (!((mode == mMode) || (mode == AudioSystem.MODE_NORMAL)) - && ((mMode == AudioSystem.MODE_IN_CALL) - || (mMode == AudioSystem.MODE_IN_COMMUNICATION)) - && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) { - Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid - + ", uid=" + Binder.getCallingUid() - + ", cannot change mode from " + mMode - + " without permission or being mode owner"); - return; - } - newModeOwnerPid = setModeInt(mode, cb, callingPid, Binder.getCallingUid(), - hasModifyPhoneStatePermission, callingPackage); - } - // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all - // SCO connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, getMode()); - } - - // setModeInt() returns a valid PID if the audio mode was successfully set to - // any mode other than NORMAL. - @GuardedBy("mDeviceBroker.mSetModeLock") - private int setModeInt( - int mode, IBinder cb, int pid, int uid, boolean isPrivileged, String caller) { - if (DEBUG_MODE) { - Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid - + ", uid=" + uid + ", caller=" + caller + ")"); - } - int newModeOwnerPid = 0; - if (cb == null) { - Log.e(TAG, "setModeInt() called with null binder"); - return newModeOwnerPid; - } - - SetModeDeathHandler hdlr = null; - Iterator iter = mSetModeDeathHandlers.iterator(); - while (iter.hasNext()) { - SetModeDeathHandler h = (SetModeDeathHandler)iter.next(); - if (h.getPid() == pid) { - hdlr = h; - // Remove from client list so that it is re-inserted at top of list - iter.remove(); - if (hdlr.getMode() == AudioSystem.MODE_IN_COMMUNICATION) { - mAudioHandler.removeEqualMessages(MSG_CHECK_MODE_FOR_UID, hdlr); - } - try { - hdlr.getBinder().unlinkToDeath(hdlr, 0); - if (cb != hdlr.getBinder()){ - hdlr = null; - } - } catch (NoSuchElementException e) { - hdlr = null; - Log.w(TAG, "link does not exist ..."); + for (SetModeDeathHandler h : mSetModeDeathHandlers) { + if (h.getPid() == pid) { + currentModeHandler = h; + break; } - break; } - } - final int oldMode = mMode; - int status = AudioSystem.AUDIO_STATUS_OK; - int actualMode; - do { - actualMode = mode; + if (mode == AudioSystem.MODE_NORMAL) { - // get new mode from client at top the list if any - if (!mSetModeDeathHandlers.isEmpty()) { - hdlr = mSetModeDeathHandlers.get(0); - cb = hdlr.getBinder(); - actualMode = hdlr.getMode(); + if (currentModeHandler != null) { + if (!currentModeHandler.isPrivileged() + && currentModeHandler.getMode() == AudioSystem.MODE_IN_COMMUNICATION) { + mAudioHandler.removeEqualMessages( + MSG_CHECK_MODE_FOR_UID, currentModeHandler); + } + mSetModeDeathHandlers.remove(currentModeHandler); if (DEBUG_MODE) { - Log.w(TAG, " using mode=" + mode + " instead due to death hdlr at pid=" - + hdlr.mPid); + Log.v(TAG, "setMode(" + mode + ") removing hldr for pid: " + pid); + } + try { + currentModeHandler.getBinder().unlinkToDeath(currentModeHandler, 0); + } catch (NoSuchElementException e) { + Log.w(TAG, "setMode link does not exist ..."); } } } else { - if (hdlr == null) { - hdlr = new SetModeDeathHandler(cb, pid, uid, isPrivileged, caller); - } - // Register for client death notification - try { - cb.linkToDeath(hdlr, 0); - } catch (RemoteException e) { - // Client has died! - Log.w(TAG, "setMode() could not link to "+cb+" binder death"); - } - - // Last client to call setMode() is always at top of client list - // as required by SetModeDeathHandler.binderDied() - mSetModeDeathHandlers.add(0, hdlr); - hdlr.setMode(mode); - } - - if (actualMode != mMode) { - final long identity = Binder.clearCallingIdentity(); - status = mAudioSystem.setPhoneState(actualMode, getModeOwnerUid()); - Binder.restoreCallingIdentity(identity); - if (status == AudioSystem.AUDIO_STATUS_OK) { - if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); } - mMode = actualMode; + if (currentModeHandler != null) { + currentModeHandler.setMode(mode); + if (DEBUG_MODE) { + Log.v(TAG, "setMode(" + mode + ") updating hldr for pid: " + pid); + } } else { - if (hdlr != null) { - mSetModeDeathHandlers.remove(hdlr); - cb.unlinkToDeath(hdlr, 0); + currentModeHandler = new SetModeDeathHandler(cb, pid, uid, + hasModifyPhoneStatePermission, callingPackage, mode); + // Register for client death notification + try { + cb.linkToDeath(currentModeHandler, 0); + } catch (RemoteException e) { + // Client has died! + Log.w(TAG, "setMode() could not link to " + cb + " binder death"); + return; + } + mSetModeDeathHandlers.add(currentModeHandler); + if (DEBUG_MODE) { + Log.v(TAG, "setMode(" + mode + ") adding handler for pid=" + pid); } - // force reading new top of mSetModeDeathHandlers stack - if (DEBUG_MODE) { Log.w(TAG, " mode set to MODE_NORMAL after phoneState pb"); } - mode = AudioSystem.MODE_NORMAL; } - } else { - status = AudioSystem.AUDIO_STATUS_OK; - } - } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty()); - - if (status == AudioSystem.AUDIO_STATUS_OK) { - if (actualMode != AudioSystem.MODE_NORMAL) { - newModeOwnerPid = getModeOwnerPid(); - if (newModeOwnerPid == 0) { - Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack"); + if (mode == AudioSystem.MODE_IN_COMMUNICATION) { + // Force active state when entering/updating the stack to avoid glitches when + // an app starts playing/recording after settng the audio mode, + // and send a reminder to check activity after a grace period. + if (!currentModeHandler.isPrivileged()) { + currentModeHandler.setPlaybackActive(true); + currentModeHandler.setRecordingActive(true); + sendMsg(mAudioHandler, + MSG_CHECK_MODE_FOR_UID, + SENDMSG_QUEUE, + 0, + 0, + currentModeHandler, + CHECK_MODE_FOR_UID_PERIOD_MS); + } } } - // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL - mModeLogger.log( - new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode)); - int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); - int device = getDeviceForStream(streamType); - int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device); - setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, caller, - true /*hasModifyAudioSettings*/); - - updateStreamVolumeAlias(true /*updateVolumes*/, caller); - - // change of mode may require volume to be re-applied on some devices - updateAbsVolumeMultiModeDevices(oldMode, actualMode); + sendMsg(mAudioHandler, + MSG_UPDATE_AUDIO_MODE, + SENDMSG_REPLACE, + mode, + pid, + callingPackage, + 0); + } + } - if (actualMode == AudioSystem.MODE_IN_COMMUNICATION - && !hdlr.isPrivileged()) { - sendMsg(mAudioHandler, - MSG_CHECK_MODE_FOR_UID, - SENDMSG_QUEUE, - 0, - 0, - hdlr, - CHECK_MODE_FOR_UID_PERIOD_MS); + @GuardedBy("mDeviceBroker.mSetModeLock") + void onUpdateAudioMode(int requestedMode, int requesterPid, String requesterPackage) { + if (requestedMode == AudioSystem.MODE_CURRENT) { + requestedMode = getMode(); + } + int mode = AudioSystem.MODE_NORMAL; + int uid = 0; + int pid = 0; + SetModeDeathHandler currentModeHandler = getAudioModeOwnerHandler(); + if (currentModeHandler != null) { + mode = currentModeHandler.getMode(); + uid = currentModeHandler.getUid(); + pid = currentModeHandler.getPid(); + } + if (DEBUG_MODE) { + Log.v(TAG, "onUpdateAudioMode() mode: " + mode + ", mMode: " + mMode + + " requestedMode: " + requestedMode); + } + if (mode != mMode) { + final long identity = Binder.clearCallingIdentity(); + int status = mAudioSystem.setPhoneState(mode, uid); + Binder.restoreCallingIdentity(identity); + if (status == AudioSystem.AUDIO_STATUS_OK) { + if (DEBUG_MODE) { + Log.v(TAG, "onUpdateAudioMode: mode successfully set to " + mode); + } + int previousMode = mMode; + mMode = mode; + // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL + mModeLogger.log(new PhoneStateEvent(requesterPackage, requesterPid, + requestedMode, pid, mode)); + + int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); + int device = getDeviceForStream(streamType); + int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device); + setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, + requesterPackage, true /*hasModifyAudioSettings*/); + + updateStreamVolumeAlias(true /*updateVolumes*/, requesterPackage); + + // change of mode may require volume to be re-applied on some devices + updateAbsVolumeMultiModeDevices(previousMode, mode); + + // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO + // connections not started by the application changing the mode when pid changes + mDeviceBroker.postSetModeOwnerPid(pid, mode); + } else { + Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode); } } - return newModeOwnerPid; } /** @see AudioManager#getMode() */ public int getMode() { - return mMode; + synchronized (mDeviceBroker.mSetModeLock) { + SetModeDeathHandler currentModeHandler = getAudioModeOwnerHandler(); + if (currentModeHandler != null) { + return currentModeHandler.getMode(); + } + return AudioSystem.MODE_NORMAL; + } } /** cached value read from audiopolicy manager after initialization. */ @@ -5683,11 +5869,6 @@ public class AudioService extends IAudioService.Stub throw new IllegalArgumentException("Illegal BluetoothProfile state for device " + " (dis)connection, got " + state); } - if (state == BluetoothProfile.STATE_CONNECTED) { - mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true); - } else { - mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor); - } mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( device, state, suppressNoisyIntent, musicDevice, "AudioService"); } @@ -7032,6 +7213,9 @@ public class AudioService extends IAudioService.Stub case MSG_PLAYBACK_CONFIG_CHANGE: onPlaybackConfigChange((List<AudioPlaybackConfiguration>) msg.obj); break; + case MSG_RECORDING_CONFIG_CHANGE: + onRecordingConfigChange((List<AudioRecordingConfiguration>) msg.obj); + break; case MSG_BROADCAST_MICROPHONE_MUTE: mSystemServer.sendMicrophoneMuteChangedIntent(); @@ -7042,30 +7226,19 @@ public class AudioService extends IAudioService.Stub if (msg.obj == null) { break; } - // If no other app is currently owning the audio mode and - // the app corresponding to this mode death handler object is still in the - // mode owner stack but not capturing or playing audio after 3 seconds, - // remove it from the stack. - // Otherwise, check again in 3 seconds. + // Update active playback/recording for apps requesting IN_COMMUNICATION + // mode after a grace period following the mode change SetModeDeathHandler h = (SetModeDeathHandler) msg.obj; if (mSetModeDeathHandlers.indexOf(h) < 0) { break; } - if (getModeOwnerUid() != h.getUid() - || mRecordMonitor.isRecordingActiveForUid(h.getUid()) - || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) { - sendMsg(mAudioHandler, - MSG_CHECK_MODE_FOR_UID, - SENDMSG_QUEUE, - 0, - 0, - h, - CHECK_MODE_FOR_UID_PERIOD_MS); - break; + boolean wasActive = h.isActive(); + h.setPlaybackActive(mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())); + h.setRecordingActive(mRecordMonitor.isRecordingActiveForUid(h.getUid())); + if (wasActive != h.isActive()) { + onUpdateAudioMode(AudioSystem.MODE_CURRENT, + android.os.Process.myPid(), mContext.getPackageName()); } - setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(), - h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID"); - mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid())); } break; @@ -7082,6 +7255,16 @@ public class AudioService extends IAudioService.Stub case MSG_REINIT_VOLUMES: onReinitVolumes((String) msg.obj); break; + + case MSG_UPDATE_A11Y_SERVICE_UIDS: + onUpdateAccessibilityServiceUids(); + break; + + case MSG_UPDATE_AUDIO_MODE: + synchronized (mDeviceBroker.mSetModeLock) { + onUpdateAudioMode(msg.arg1, msg.arg2, (String) msg.obj); + } + break; } } } @@ -8112,6 +8295,7 @@ public class AudioService extends IAudioService.Stub dumpStreamStates(pw); dumpVolumeGroups(pw); dumpRingerMode(pw); + dumpAudioMode(pw); pw.println("\nAudio routes:"); pw.print(" mMainType=0x"); pw.println(Integer.toHexString( mDeviceBroker.getCurAudioRoutes().mainType)); @@ -8148,6 +8332,9 @@ public class AudioService extends IAudioService.Stub + " FromRestrictions=" + mMicMuteFromRestrictions + " FromApi=" + mMicMuteFromApi + " from system=" + mMicMuteFromSystemCached); + pw.print("\n mAssistantUid="); pw.println(mAssistantUid); + pw.print(" mCurrentImeUid="); pw.println(mCurrentImeUid); + dumpAccessibilityServiceUids(pw); dumpAudioPolicies(pw); mDynPolicyLogger.dump(pw); @@ -8181,6 +8368,19 @@ public class AudioService extends IAudioService.Stub } } + private void dumpAccessibilityServiceUids(PrintWriter pw) { + synchronized (mSupportedSystemUsagesLock) { + if (mAccessibilityServiceUids != null && mAccessibilityServiceUids.length > 0) { + pw.println(" Accessibility service Uids:"); + for (int uid : mAccessibilityServiceUids) { + pw.println(" - " + uid); + } + } else { + pw.println(" No accessibility service Uids."); + } + } + } + /** * Audio Analytics ids. */ @@ -8484,7 +8684,8 @@ public class AudioService extends IAudioService.Stub mAccessibilityServiceUids = uids.toArray(); } } - AudioSystem.setA11yServicesUids(mAccessibilityServiceUids); + sendMsg(mAudioHandler, MSG_UPDATE_A11Y_SERVICE_UIDS, SENDMSG_REPLACE, + 0, 0, null, 0); } } @@ -8502,6 +8703,14 @@ public class AudioService extends IAudioService.Stub } } + private void onUpdateAccessibilityServiceUids() { + int[] accessibilityServiceUids; + synchronized (mAccessibilityServiceUidsLock) { + accessibilityServiceUids = mAccessibilityServiceUids; + } + AudioSystem.setA11yServicesUids(accessibilityServiceUids); + } + //========================================================================================== // Audio policy management //========================================================================================== @@ -9057,6 +9266,18 @@ public class AudioService extends IAudioService.Stub } /** + * Update player session ID + * @param piid Player id to update + * @param sessionId The new audio session ID + */ + public void playerSessionId(int piid, int sessionId) { + if (sessionId <= AudioSystem.AUDIO_SESSION_ALLOCATE) { + throw new IllegalArgumentException("invalid session Id " + sessionId); + } + mPlaybackMonitor.playerSessionId(piid, sessionId, Binder.getCallingUid()); + } + + /** * Update player event * @param piid Player id to update * @param event The new player event diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index 36c67cdbac4b..68a084e6d249 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -197,6 +197,28 @@ public final class PlaybackActivityMonitor } } + /** + * Update player session ID + * @param piid Player id to update + * @param sessionId The new audio session ID + * @param binderUid Calling binder uid + */ + public void playerSessionId(int piid, int sessionId, int binderUid) { + final boolean change; + synchronized (mPlayerLock) { + final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); + if (checkConfigurationCaller(piid, apc, binderUid)) { + change = apc.handleSessionIdEvent(sessionId); + } else { + Log.e(TAG, "Error updating audio session"); + change = false; + } + } + if (change) { + dispatchPlaybackChange(false); + } + } + private static final int FLAGS_FOR_SILENCE_OVERRIDE = AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY | AudioAttributes.FLAG_BYPASS_MUTE; @@ -921,6 +943,7 @@ public final class PlaybackActivityMonitor private final int mClientUid; private final int mClientPid; private final AudioAttributes mPlayerAttr; + private final int mSessionId; NewPlayerEvent(AudioPlaybackConfiguration apc) { mPlayerIId = apc.getPlayerInterfaceId(); @@ -928,6 +951,7 @@ public final class PlaybackActivityMonitor mClientUid = apc.getClientUid(); mClientPid = apc.getClientPid(); mPlayerAttr = apc.getAudioAttributes(); + mSessionId = apc.getSessionId(); } @Override @@ -935,7 +959,8 @@ public final class PlaybackActivityMonitor return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/" + mClientPid + " type:" + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType) - + " attr:" + mPlayerAttr); + + " attr:" + mPlayerAttr + + " session:" + mSessionId); } } diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index 6905b3da9bc4..6851d7148191 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -90,6 +90,7 @@ class PreAuthInfo { int userId, PromptInfo promptInfo, String opPackageName, boolean checkDevicePolicyManager) throws RemoteException { + final boolean confirmationRequested = promptInfo.isConfirmationRequested(); final boolean biometricRequested = Utils.isBiometricRequested(promptInfo); final int requestedStrength = Utils.getPublicBiometricStrength(promptInfo); @@ -111,7 +112,7 @@ class PreAuthInfo { @AuthenticatorStatus int status = getStatusForBiometricAuthenticator( devicePolicyManager, settingObserver, sensor, userId, opPackageName, - checkDevicePolicyManager, requestedStrength); + checkDevicePolicyManager, requestedStrength, promptInfo.getSensorId()); Slog.d(TAG, "Package: " + opPackageName + " Sensor ID: " + sensor.id @@ -141,7 +142,11 @@ class PreAuthInfo { DevicePolicyManager devicePolicyManager, BiometricService.SettingObserver settingObserver, BiometricSensor sensor, int userId, String opPackageName, - boolean checkDevicePolicyManager, int requestedStrength) { + boolean checkDevicePolicyManager, int requestedStrength, int requestedSensorId) { + + if (requestedSensorId != BiometricManager.SENSOR_ID_ANY && sensor.id != requestedSensorId) { + return BIOMETRIC_NO_HARDWARE; + } final boolean wasStrongEnough = Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength); diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index d87af4280ca3..5cd0bbfa4500 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -399,10 +399,15 @@ public class Utils { } } - public static boolean isKeyguard(Context context, String clientPackage) { - final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL) - == PackageManager.PERMISSION_GRANTED; - + /** + * Checks if a client package matches Keyguard and can perform internal biometric operations. + * + * @param context The system context. + * @param clientPackage The name of the package to be checked against Keyguard. + * @return Whether the given package matches Keyguard. + */ + public static boolean isKeyguard(@NonNull Context context, @Nullable String clientPackage) { + final boolean hasPermission = hasInternalPermission(context); final ComponentName keyguardComponent = ComponentName.unflattenFromString( context.getResources().getString(R.string.config_keyguardComponent)); final String keyguardPackage = keyguardComponent != null @@ -410,6 +415,34 @@ public class Utils { return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage); } + /** + * Checks if a client package matches the Android system and can perform internal biometric + * operations. + * + * @param context The system context. + * @param clientPackage The name of the package to be checked against the Android system. + * @return Whether the given package matches the Android system. + */ + public static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) { + return hasInternalPermission(context) && "android".equals(clientPackage); + } + + /** + * Checks if a client package matches Settings and can perform internal biometric operations. + * + * @param context The system context. + * @param clientPackage The name of the package to be checked against Settings. + * @return Whether the given package matches Settings. + */ + public static boolean isSettings(@NonNull Context context, @Nullable String clientPackage) { + return hasInternalPermission(context) && "com.android.settings".equals(clientPackage); + } + + private static boolean hasInternalPermission(@NonNull Context context) { + return context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL) + == PackageManager.PERMISSION_GRANTED; + } + public static String getClientName(@Nullable BaseClientMonitor client) { return client != null ? client.getClass().getSimpleName() : "null"; } diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index 14433fb0ea9a..0536e78e58f6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -149,9 +149,10 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> pm.incrementAuthForUser(getTargetUserId(), authenticated); } - // Ensure authentication only succeeds if the client activity is on top or is keyguard. + // Ensure authentication only succeeds if the client activity is on top. boolean isBackgroundAuth = false; - if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())) { + if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString()) + && !Utils.isSystem(getContext(), getOwnerString())) { final List<ActivityManager.RunningTaskInfo> tasks = mActivityTaskManager.getTasks(1); if (tasks == null || tasks.isEmpty()) { @@ -166,7 +167,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> final String topPackage = topActivity.getPackageName(); if (!topPackage.contentEquals(getOwnerString())) { Slog.e(TAG, "Background authentication detected, top: " + topPackage - + ", client: " + this); + + ", client: " + getOwnerString()); isBackgroundAuth = true; } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java new file mode 100644 index 000000000000..769c47a94ae3 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face.aidl; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.biometrics.face.AuthenticationFrame; +import android.hardware.biometrics.face.BaseFrame; +import android.hardware.biometrics.face.Cell; +import android.hardware.biometrics.face.EnrollmentFrame; +import android.hardware.face.FaceAuthenticationFrame; +import android.hardware.face.FaceDataFrame; +import android.hardware.face.FaceEnrollCell; +import android.hardware.face.FaceEnrollFrame; + +/** + * Utilities for converting between hardware and framework-defined AIDL models. + */ +final class AidlConversionUtils { + // Prevent instantiation. + private AidlConversionUtils() {} + + @NonNull + public static FaceAuthenticationFrame convert(@NonNull AuthenticationFrame frame) { + return new FaceAuthenticationFrame(convert(frame.data)); + } + + @NonNull + public static AuthenticationFrame convert(@NonNull FaceAuthenticationFrame frame) { + final AuthenticationFrame convertedFrame = new AuthenticationFrame(); + convertedFrame.data = convert(frame.getData()); + return convertedFrame; + } + + @NonNull + public static FaceEnrollFrame convert(@NonNull EnrollmentFrame frame) { + return new FaceEnrollFrame(convert(frame.cell), frame.stage, convert(frame.data)); + } + + @NonNull + public static EnrollmentFrame convert(@NonNull FaceEnrollFrame frame) { + final EnrollmentFrame convertedFrame = new EnrollmentFrame(); + convertedFrame.cell = convert(frame.getCell()); + convertedFrame.stage = (byte) frame.getStage(); + convertedFrame.data = convert(frame.getData()); + return convertedFrame; + } + + @NonNull + public static FaceDataFrame convert(@NonNull BaseFrame frame) { + return new FaceDataFrame( + frame.acquiredInfo, + frame.vendorCode, + frame.pan, + frame.tilt, + frame.distance, + frame.isCancellable); + } + + @NonNull + public static BaseFrame convert(@NonNull FaceDataFrame frame) { + final BaseFrame convertedFrame = new BaseFrame(); + convertedFrame.acquiredInfo = (byte) frame.getAcquiredInfo(); + convertedFrame.vendorCode = frame.getVendorCode(); + convertedFrame.pan = frame.getPan(); + convertedFrame.tilt = frame.getTilt(); + convertedFrame.distance = frame.getDistance(); + convertedFrame.isCancellable = frame.isCancellable(); + return convertedFrame; + } + + @Nullable + public static FaceEnrollCell convert(@Nullable Cell cell) { + return cell == null ? null : new FaceEnrollCell(cell.x, cell.y, cell.z); + } + + @Nullable + public static Cell convert(@Nullable FaceEnrollCell cell) { + if (cell == null) { + return null; + } + + final Cell convertedCell = new Cell(); + convertedCell.x = cell.getX(); + convertedCell.y = cell.getY(); + convertedCell.z = cell.getZ(); + return convertedCell; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index a7bfc4b16dc8..30577667e5e4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -28,6 +28,8 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.ISession; +import android.hardware.face.FaceAuthenticationFrame; +import android.hardware.face.FaceDataFrame; import android.hardware.face.FaceManager; import android.os.IBinder; import android.os.RemoteException; @@ -193,6 +195,17 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements onAcquiredInternal(acquireInfo, vendorCode, shouldSend); } + /** + * Called each time a new frame is received during face authentication. + * + * @param frame Information about the current frame. + */ + public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame) { + // TODO(b/178414967): Send additional frame data to the client callback. + final FaceDataFrame data = frame.getData(); + onAcquired(data.getAcquiredInfo(), data.getVendorCode()); + } + @Override public void onLockoutTimed(long durationMillis) { mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED); // Lockout metrics are logged as an error code. diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index afc7f6485bc9..da657b96afd5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -27,6 +27,8 @@ import android.hardware.biometrics.face.Feature; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.ISession; import android.hardware.face.Face; +import android.hardware.face.FaceDataFrame; +import android.hardware.face.FaceEnrollFrame; import android.hardware.face.FaceManager; import android.os.IBinder; import android.os.NativeHandle; @@ -110,6 +112,17 @@ public class FaceEnrollClient extends EnrollClient<ISession> { onAcquiredInternal(acquireInfo, vendorCode, shouldSend); } + /** + * Called each time a new frame is received during face enrollment. + * + * @param frame Information about the current frame. + */ + public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) { + // TODO(b/178414967): Send additional frame data to the client callback. + final FaceDataFrame data = frame.getData(); + onAcquired(data.getAcquiredInfo(), data.getVendorCode()); + } + @Override protected void startHalOperation() { final ArrayList<Byte> token = new ArrayList<>(); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index e685ee2899af..1b6b9d70d5ac 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -643,8 +643,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { final Sensor sensor = mSensors.valueAt(i); final int sensorId = mSensors.keyAt(i); PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount(); - sensor.getScheduler().recordCrashState(); - sensor.getScheduler().reset(); + sensor.onBinderDied(); } }); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index f49601ab3fdc..baeb3fdda67d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java @@ -33,7 +33,6 @@ import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.keymaster.HardwareAuthToken; import android.os.Handler; -import android.os.IBinder; import android.os.RemoteException; import android.os.UserManager; import android.util.Slog; @@ -45,7 +44,6 @@ import com.android.server.biometrics.SensorServiceStateProto; import com.android.server.biometrics.SensorStateProto; import com.android.server.biometrics.UserStateProto; import com.android.server.biometrics.Utils; -import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; @@ -64,7 +62,7 @@ import java.util.Map; /** * Maintains the state of a single sensor within an instance of the {@link IFace} HAL. */ -public class Sensor implements IBinder.DeathRecipient { +public class Sensor { private boolean mTestHalEnabled; @@ -170,33 +168,39 @@ public class Sensor implements IBinder.DeathRecipient { @Override public void onAuthenticationFrame(AuthenticationFrame frame) { - // TODO(b/174619156): propagate the frame to an AuthenticationClient mHandler.post(() -> { final BaseClientMonitor client = mScheduler.getCurrentClient(); - if (!(client instanceof AcquisitionClient)) { - Slog.e(mTag, "onAcquired for non-acquisition client: " + if (!(client instanceof FaceAuthenticationClient)) { + Slog.e(mTag, "onAuthenticationFrame for incompatible client: " + Utils.getClientName(client)); return; - } - final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client; - acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode); + } + if (frame == null) { + Slog.e(mTag, "Received null authentication frame for client: " + + Utils.getClientName(client)); + return; + } + ((FaceAuthenticationClient) client).onAuthenticationFrame( + AidlConversionUtils.convert(frame)); }); } @Override public void onEnrollmentFrame(EnrollmentFrame frame) { - // TODO(b/174619156): propagate the frame to an EnrollmentClient mHandler.post(() -> { final BaseClientMonitor client = mScheduler.getCurrentClient(); - if (!(client instanceof AcquisitionClient)) { - Slog.e(mTag, "onAcquired for non-acquisition client: " + if (!(client instanceof FaceEnrollClient)) { + Slog.e(mTag, "onEnrollmentFrame for incompatible client: " + Utils.getClientName(client)); return; } - - final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client; - acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode); + if (frame == null) { + Slog.e(mTag, "Received null enrollment frame for client: " + + Utils.getClientName(client)); + return; + } + ((FaceEnrollClient) client).onEnrollmentFrame(AidlConversionUtils.convert(frame)); }); } @@ -476,7 +480,6 @@ public class Sensor implements IBinder.DeathRecipient { mTag, mScheduler, sensorId, userId, callback); final ISession newSession = daemon.createSession(sensorId, userId, resultController); - newSession.asBinder().linkToDeath(this, 0 /* flags */); mCurrentSession = new Session(mTag, newSession, userId, resultController); } @@ -518,24 +521,21 @@ public class Sensor implements IBinder.DeathRecipient { proto.end(sensorToken); } - @Override - public void binderDied() { - Slog.e(mTag, "Binder died"); - mHandler.post(() -> { - final BaseClientMonitor client = mScheduler.getCurrentClient(); - if (client instanceof Interruptable) { - Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); - final Interruptable interruptable = (Interruptable) client; - interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, - 0 /* vendorCode */); - - mScheduler.recordCrashState(); + public void onBinderDied() { + final BaseClientMonitor client = mScheduler.getCurrentClient(); + if (client instanceof Interruptable) { + Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); + final Interruptable interruptable = (Interruptable) client; + interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */); + + FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, + BiometricsProtoEnums.MODALITY_FACE, + BiometricsProtoEnums.ISSUE_HAL_DEATH); + } - FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, - BiometricsProtoEnums.MODALITY_FACE, - BiometricsProtoEnums.ISSUE_HAL_DEATH); - mCurrentSession = null; - } - }); + mScheduler.recordCrashState(); + mScheduler.reset(); + mCurrentSession = null; } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 0265cb93ac8b..b0e42cd137eb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -25,6 +25,10 @@ import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR; +import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; import android.annotation.NonNull; import android.annotation.Nullable; @@ -32,6 +36,7 @@ import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricService; @@ -49,6 +54,7 @@ import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.Binder; import android.os.Build; +import android.os.CancellationSignal; import android.os.Handler; import android.os.IBinder; import android.os.Process; @@ -80,6 +86,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.Executor; /** * A service to manage multiple clients that want to access the fingerprint HAL API. @@ -190,7 +197,7 @@ public class FingerprintService extends SystemService implements BiometricServic @Override // Binder call public void enroll(final IBinder token, final byte[] hardwareAuthToken, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName, - boolean shouldLogMetrics) { + @FingerprintManager.EnrollReason int enrollReason) { Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); final Pair<Integer, ServiceProvider> provider = getSingleProvider(); @@ -200,7 +207,7 @@ public class FingerprintService extends SystemService implements BiometricServic } provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId, - receiver, opPackageName, shouldLogMetrics); + receiver, opPackageName, enrollReason); } @Override // Binder call @@ -219,8 +226,8 @@ public class FingerprintService extends SystemService implements BiometricServic @SuppressWarnings("deprecation") @Override // Binder call public void authenticate(final IBinder token, final long operationId, - @FingerprintManager.SensorId final int sensorId, final int userId, - final IFingerprintServiceReceiver receiver, final String opPackageName) { + final int sensorId, final int userId, final IFingerprintServiceReceiver receiver, + final String opPackageName) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); @@ -236,7 +243,7 @@ public class FingerprintService extends SystemService implements BiometricServic final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName); // Clear calling identity when checking LockPatternUtils for StrongAuth flags. - final long identity = Binder.clearCallingIdentity(); + long identity = Binder.clearCallingIdentity(); try { if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) { // If this happens, something in KeyguardUpdateMonitor is wrong. @@ -266,9 +273,101 @@ public class FingerprintService extends SystemService implements BiometricServic return; } - provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, - 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName, - restricted, statsClient, isKeyguard); + final FingerprintSensorPropertiesInternal sensorProps = + provider.second.getSensorProperties(sensorId); + if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName) + && sensorProps != null && sensorProps.isAnyUdfpsType()) { + identity = Binder.clearCallingIdentity(); + try { + authenticateWithPrompt(operationId, sensorProps, userId, receiver); + } finally { + Binder.restoreCallingIdentity(identity); + } + } else { + provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, + 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName, + restricted, statsClient, isKeyguard); + } + } + + private void authenticateWithPrompt( + final long operationId, + @NonNull final FingerprintSensorPropertiesInternal props, + final int userId, + final IFingerprintServiceReceiver receiver) { + + final Context context = getUiContext(); + final Executor executor = context.getMainExecutor(); + + final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context) + .setTitle(context.getString(R.string.biometric_dialog_default_title)) + .setSubtitle(context.getString(R.string.fingerprint_dialog_default_subtitle)) + .setNegativeButton( + context.getString(R.string.cancel), + executor, + (dialog, which) -> { + try { + receiver.onError( + FINGERPRINT_ERROR_USER_CANCELED, 0 /* vendorCode */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in negative button onClick()", e); + } + }) + .setSensorId(props.sensorId) + .build(); + + final BiometricPrompt.AuthenticationCallback promptCallback = + new BiometricPrompt.AuthenticationCallback() { + @Override + public void onAuthenticationError(int errorCode, CharSequence errString) { + try { + if (FingerprintUtils.isKnownErrorCode(errorCode)) { + receiver.onError(errorCode, 0 /* vendorCode */); + } else { + receiver.onError(FINGERPRINT_ERROR_VENDOR, errorCode); + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAuthenticationError()", e); + } + } + + @Override + public void onAuthenticationSucceeded( + BiometricPrompt.AuthenticationResult result) { + final Fingerprint fingerprint = new Fingerprint("", 0, 0L); + final boolean isStrong = props.sensorStrength == STRENGTH_STRONG; + try { + receiver.onAuthenticationSucceeded(fingerprint, userId, isStrong); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAuthenticationSucceeded()", e); + } + } + + @Override + public void onAuthenticationFailed() { + try { + receiver.onAuthenticationFailed(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAuthenticationFailed()", e); + } + } + + @Override + public void onAuthenticationAcquired(int acquireInfo) { + try { + if (FingerprintUtils.isKnownAcquiredCode(acquireInfo)) { + receiver.onAcquired(acquireInfo, 0 /* vendorCode */); + } else { + receiver.onAcquired(FINGERPRINT_ACQUIRED_VENDOR, acquireInfo); + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAuthenticationAcquired()", e); + } + } + }; + + biometricPrompt.authenticateUserForOperation( + new CancellationSignal(), executor, promptCallback, userId, operationId); } @Override @@ -374,6 +473,7 @@ public class FingerprintService extends SystemService implements BiometricServic @Override // Binder call public void cancelAuthenticationFromService(final int sensorId, final IBinder token, final String opPackageName) { + Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); final ServiceProvider provider = getProviderForSensor(sensorId); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java index dc6fd3a1b26d..d69151da55f6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java @@ -16,8 +16,18 @@ package com.android.server.biometrics.sensors.fingerprint; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_IMAGER_DIRTY; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_INSUFFICIENT; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_PARTIAL; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_FAST; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_SLOW; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; + import android.annotation.Nullable; import android.content.Context; +import android.hardware.biometrics.fingerprint.V2_1.FingerprintError; import android.hardware.fingerprint.Fingerprint; import android.text.TextUtils; import android.util.SparseArray; @@ -138,5 +148,51 @@ public class FingerprintUtils implements BiometricUtils<Fingerprint> { return state; } } + + /** + * Checks if the given error code corresponds to a known fingerprint error. + * + * @param errorCode The error code to be checked. + * @return Whether the error code corresponds to a known error. + */ + public static boolean isKnownErrorCode(int errorCode) { + switch (errorCode) { + case FingerprintError.ERROR_HW_UNAVAILABLE: + case FingerprintError.ERROR_UNABLE_TO_PROCESS: + case FingerprintError.ERROR_TIMEOUT: + case FingerprintError.ERROR_NO_SPACE: + case FingerprintError.ERROR_CANCELED: + case FingerprintError.ERROR_UNABLE_TO_REMOVE: + case FingerprintError.ERROR_LOCKOUT: + case FingerprintError.ERROR_VENDOR: + return true; + + default: + return false; + } + } + + /** + * Checks if the given acquired code corresponds to a known fingerprint error. + * + * @param acquiredCode The acquired code to be checked. + * @return Whether the acquired code corresponds to a known error. + */ + public static boolean isKnownAcquiredCode(int acquiredCode) { + switch (acquiredCode) { + case FINGERPRINT_ACQUIRED_GOOD: + case FINGERPRINT_ACQUIRED_PARTIAL: + case FINGERPRINT_ACQUIRED_INSUFFICIENT: + case FINGERPRINT_ACQUIRED_IMAGER_DIRTY: + case FINGERPRINT_ACQUIRED_TOO_SLOW: + case FINGERPRINT_ACQUIRED_TOO_FAST: + case FINGERPRINT_ACQUIRED_VENDOR: + case FINGERPRINT_ACQUIRED_START: + return true; + + default: + return false; + } + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 303c080c044c..f672ae56e020 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -78,7 +78,7 @@ public interface ServiceProvider { void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, - boolean shouldLogMetrics); + @FingerprintManager.EnrollReason int enrollReason); void cancelEnrollment(int sensorId, @NonNull IBinder token); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java index d092e860e208..37f8e8c2c1ee 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java @@ -65,6 +65,17 @@ public class UdfpsHelper { } } + public static int getReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) { + switch (reason) { + case FingerprintManager.ENROLL_FIND_SENSOR: + return IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR; + case FingerprintManager.ENROLL_ENROLL: + return IUdfpsOverlayController.REASON_ENROLL_ENROLLING; + default: + return IUdfpsOverlayController.REASON_UNKNOWN; + } + } + public static void showUdfpsOverlay(int sensorId, int reason, @Nullable IUdfpsOverlayController udfpsOverlayController) { if (udfpsOverlayController == null) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java index c2a30be6e2cb..ea9c709ec79f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.ITestSession; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.os.Binder; import android.util.Slog; @@ -131,7 +132,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, - mContext.getOpPackageName(), true /* shouldLogMetrics */); + mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index 08cc464b4766..ae64c77f1365 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -25,6 +25,7 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.os.RemoteException; @@ -43,6 +44,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { private static final String TAG = "FingerprintEnrollClient"; @Nullable private final IUdfpsOverlayController mUdfpsOverlayController; + private final @FingerprintManager.EnrollReason int mEnrollReason; @Nullable private ICancellationSignal mCancellationSignal; private final int mMaxTemplatesPerUser; @@ -52,13 +54,17 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, @Nullable IUdfpsOverlayController udfpsOvelayController, int maxTemplatesPerUser, - boolean shouldLogMetrics) { + @FingerprintManager.EnrollReason int enrollReason) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, true /* shouldVibrate */); mUdfpsOverlayController = udfpsOvelayController; mMaxTemplatesPerUser = maxTemplatesPerUser; - setShouldLog(shouldLogMetrics); + + mEnrollReason = enrollReason; + if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) { + setShouldLog(false); + } } @Override @@ -72,6 +78,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { } } + @Override public void onAcquired(int acquiredInfo, int vendorCode) { super.onAcquired(acquiredInfo, vendorCode); @@ -112,7 +119,8 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { @Override protected void startHalOperation() { - UdfpsHelper.showUdfpsOverlay(getSensorId(), IUdfpsOverlayController.REASON_ENROLL, + UdfpsHelper.showUdfpsOverlay(getSensorId(), + UdfpsHelper.getReasonFromEnrollReason(mEnrollReason), mUdfpsOverlayController); try { mCancellationSignal = getFreshDaemon().enroll(mSequentialId, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index f8450245a18d..0bd2f241ed8d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -28,6 +28,7 @@ import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.SensorProps; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IUdfpsOverlayController; @@ -96,7 +97,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi Slog.e(getTag(), "Task stack changed for client: " + client); continue; } - if (Utils.isKeyguard(mContext, client.getOwnerString())) { + if (Utils.isKeyguard(mContext, client.getOwnerString()) + || Utils.isSystem(mContext, client.getOwnerString())) { continue; // Keyguard is always allowed } @@ -365,7 +367,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, - @NonNull String opPackageName, boolean shouldLogMetrics) { + @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason) { mHandler.post(() -> { final IFingerprint daemon = getHalInstance(); if (daemon == null) { @@ -387,7 +389,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(sensorId), sensorId, - mUdfpsOverlayController, maxTemplatesPerUser, shouldLogMetrics); + mUdfpsOverlayController, maxTemplatesPerUser, enrollReason); scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, @@ -695,8 +697,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi final Sensor sensor = mSensors.valueAt(i); final int sensorId = mSensors.keyAt(i); PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount(); - sensor.getScheduler().recordCrashState(); - sensor.getScheduler().reset(); + sensor.onBinderDied(); } }); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index f0e7e1cf5d25..7e4ee9e77ab2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -31,7 +31,6 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.keymaster.HardwareAuthToken; import android.os.Handler; -import android.os.IBinder; import android.os.RemoteException; import android.os.UserManager; import android.util.Slog; @@ -65,7 +64,7 @@ import java.util.Map; * {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL. */ @SuppressWarnings("deprecation") -class Sensor implements IBinder.DeathRecipient { +class Sensor { private boolean mTestHalEnabled; @@ -461,7 +460,6 @@ class Sensor implements IBinder.DeathRecipient { mTag, mScheduler, sensorId, userId, callback); final ISession newSession = daemon.createSession(sensorId, userId, resultController); - newSession.asBinder().linkToDeath(this, 0 /* flags */); mCurrentSession = new Session(mTag, newSession, userId, resultController); } @@ -503,24 +501,21 @@ class Sensor implements IBinder.DeathRecipient { proto.end(sensorToken); } - @Override - public void binderDied() { - Slog.e(mTag, "Binder died"); - mHandler.post(() -> { - final BaseClientMonitor client = mScheduler.getCurrentClient(); - if (client instanceof Interruptable) { - Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); - final Interruptable interruptable = (Interruptable) client; - interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, - 0 /* vendorCode */); - - mScheduler.recordCrashState(); + public void onBinderDied() { + final BaseClientMonitor client = mScheduler.getCurrentClient(); + if (client instanceof Interruptable) { + Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); + final Interruptable interruptable = (Interruptable) client; + interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */); + + FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, + BiometricsProtoEnums.MODALITY_FINGERPRINT, + BiometricsProtoEnums.ISSUE_HAL_DEATH); + } - FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, - BiometricsProtoEnums.MODALITY_FINGERPRINT, - BiometricsProtoEnums.ISSUE_HAL_DEATH); - mCurrentSession = null; - } - }); + mScheduler.recordCrashState(); + mScheduler.reset(); + mCurrentSession = null; } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java index 6893e72486bc..312ee0a267ac 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.ITestSession; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.os.Binder; import android.util.Slog; @@ -132,7 +133,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, - mContext.getOpPackageName(), true/* shouldLogMetrics */); + mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 1135126d86fb..7a74c6a39aa1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -33,6 +33,7 @@ import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintServiceReceiver; @@ -49,6 +50,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.server.biometrics.SensorServiceStateProto; import com.android.server.biometrics.SensorStateProto; @@ -113,7 +115,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull private final HalResultController mHalResultController; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; private int mCurrentUserId = UserHandle.USER_NULL; - private boolean mIsUdfps = false; + private final boolean mIsUdfps; private final int mSensorId; private final class BiometricTaskStackListener extends TaskStackListener { @@ -125,7 +127,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider Slog.e(TAG, "Task stack changed for client: " + client); return; } - if (Utils.isKeyguard(mContext, client.getOwnerString())) { + if (Utils.isKeyguard(mContext, client.getOwnerString()) + || Utils.isSystem(mContext, client.getOwnerString())) { return; // Keyguard is always allowed } @@ -335,23 +338,14 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider Slog.e(TAG, "Unable to register user switch observer"); } - final IBiometricsFingerprint daemon = getDaemon(); - mIsUdfps = false; - android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension = - android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom( - daemon); - if (extension != null) { - try { - mIsUdfps = extension.isUdfps(sensorId); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception while quering udfps", e); - mIsUdfps = false; - } - } + // TODO(b/179175438): Remove this code block after transition to AIDL. + // The existence of config_udfps_sensor_props indicates that the sensor is UDFPS. + mIsUdfps = !ArrayUtils.isEmpty( + mContext.getResources().getIntArray(R.array.config_udfps_sensor_props)); final @FingerprintSensorProperties.SensorType int sensorType = mIsUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL - : FingerprintSensorProperties.TYPE_REAR; + : FingerprintSensorProperties.TYPE_REAR; // resetLockout is controlled by the framework, so hardwareAuthToken is not required final boolean resetLockoutRequiresHardwareAuthToken = false; final int maxEnrollmentsPerUser = mContext.getResources() @@ -415,7 +409,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider Slog.d(TAG, "Daemon was null, reconnecting, current operation: " + mScheduler.getCurrentClient()); try { - mDaemon = IBiometricsFingerprint.getService(true /* retry */); + mDaemon = IBiometricsFingerprint.getService(); } catch (java.util.NoSuchElementException e) { // Service doesn't exist or cannot be opened. Slog.w(TAG, "NoSuchElementException", e); @@ -554,7 +548,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider public void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, - boolean shouldLogMetrics) { + @FingerprintManager.EnrollReason int enrollReason) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); @@ -562,7 +556,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController, - shouldLogMetrics); + enrollReason); mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index d927aa717fbc..33db64c3259b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -24,6 +24,7 @@ import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.os.RemoteException; @@ -46,6 +47,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint private static final String TAG = "FingerprintEnrollClient"; @Nullable private final IUdfpsOverlayController mUdfpsOverlayController; + private final @FingerprintManager.EnrollReason int mEnrollReason; FingerprintEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, @@ -53,12 +55,16 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId, @Nullable IUdfpsOverlayController udfpsOverlayController, - boolean shouldLogMetrics) { + @FingerprintManager.EnrollReason int enrollReason) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, true /* shouldVibrate */); mUdfpsOverlayController = udfpsOverlayController; - setShouldLog(shouldLogMetrics); + + mEnrollReason = enrollReason; + if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) { + setShouldLog(false); + } } @Override @@ -76,7 +82,8 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint @Override protected void startHalOperation() { - UdfpsHelper.showUdfpsOverlay(getSensorId(), IUdfpsOverlayController.REASON_ENROLL, + UdfpsHelper.showUdfpsOverlay(getSensorId(), + UdfpsHelper.getReasonFromEnrollReason(mEnrollReason), mUdfpsOverlayController); try { // GroupId was never used. In fact, groupId is always the same as userId. diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 6b2a1c950e38..51ba5f775880 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -211,9 +211,9 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override - public void clearOverrideForTest(long changeId, String packageName) { + public boolean clearOverrideForTest(long changeId, String packageName) { checkCompatChangeOverridePermission(); - mCompatConfig.removeOverride(changeId, packageName); + return mCompatConfig.removeOverride(changeId, packageName); } @Override diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java index c70bb080b0b1..43d9ade67a11 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/services/core/java/com/android/server/connectivity/DnsManager.java @@ -32,6 +32,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.net.IDnsResolver; +import android.net.InetAddresses; import android.net.LinkProperties; import android.net.Network; import android.net.ResolverOptionsParcel; @@ -190,7 +191,7 @@ public class DnsManager { for (String ipAddress : ipAddresses) { try { latestDnses.add(new Pair(hostname, - InetAddress.parseNumericAddress(ipAddress))); + InetAddresses.parseNumericAddress(ipAddress))); } catch (IllegalArgumentException e) {} } // Remove <hostname, ipAddress> pairs that should not be tracked. diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index 952193b77681..46c49e7fc28c 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -34,9 +34,9 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.net.module.util.NetworkStackConstants; import com.android.server.net.BaseNetworkObserver; -import java.net.Inet4Address; import java.net.Inet6Address; import java.util.Objects; @@ -433,7 +433,7 @@ public class Nat464Xlat extends BaseNetworkObserver { // clat IPv4 address itself (for those apps, it doesn't matter what // the IP of the gateway is, only that there is one). RouteInfo ipv4Default = new RouteInfo( - new LinkAddress(Inet4Address.ANY, 0), + new LinkAddress(NetworkStackConstants.IPV4_ADDR_ANY, 0), clatAddress.getAddress(), mIface); stacked.addRoute(ipv4Default); stacked.addLinkAddress(clatAddress); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 1a4f20c7101e..bff1a5c99dd2 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -122,6 +122,13 @@ import java.util.TreeSet; // // When ConnectivityService disconnects a network: // ----------------------------------------------- +// If a network is just connected, ConnectivityService will think it will be used soon, but might +// not be used. Thus, a 5s timer will be held to prevent the network being torn down immediately. +// This "nascent" state is implemented by the "lingering" logic below without relating to any +// request, and is used in some cases where network requests race with network establishment. The +// nascent state ends when the 5-second timer fires, or as soon as the network satisfies a +// request, whichever is earlier. In this state, the network is considered in the background. +// // If a network has no chance of satisfying any requests (even if it were to become validated // and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel. // @@ -210,23 +217,23 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // network is taken down. This usually only happens to the default network. Lingering ends with // either the linger timeout expiring and the network being taken down, or the network // satisfying a request again. - public static class LingerTimer implements Comparable<LingerTimer> { + public static class InactivityTimer implements Comparable<InactivityTimer> { public final int requestId; public final long expiryMs; - public LingerTimer(int requestId, long expiryMs) { + public InactivityTimer(int requestId, long expiryMs) { this.requestId = requestId; this.expiryMs = expiryMs; } public boolean equals(Object o) { - if (!(o instanceof LingerTimer)) return false; - LingerTimer other = (LingerTimer) o; + if (!(o instanceof InactivityTimer)) return false; + InactivityTimer other = (InactivityTimer) o; return (requestId == other.requestId) && (expiryMs == other.expiryMs); } public int hashCode() { return Objects.hash(requestId, expiryMs); } - public int compareTo(LingerTimer other) { + public int compareTo(InactivityTimer other) { return (expiryMs != other.expiryMs) ? Long.compare(expiryMs, other.expiryMs) : Integer.compare(requestId, other.requestId); @@ -269,30 +276,32 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { */ public static final int ARG_AGENT_SUCCESS = 1; - // All linger timers for this network, sorted by expiry time. A linger timer is added whenever + // All inactivity timers for this network, sorted by expiry time. A timer is added whenever // a request is moved to a network with a better score, regardless of whether the network is or - // was lingering or not. + // was lingering or not. An inactivity timer is also added when a network connects + // without immediately satisfying any requests. // TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g., // SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire. - private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>(); + private final SortedSet<InactivityTimer> mInactivityTimers = new TreeSet<>(); - // For fast lookups. Indexes into mLingerTimers by request ID. - private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>(); + // For fast lookups. Indexes into mInactivityTimers by request ID. + private final SparseArray<InactivityTimer> mInactivityTimerForRequest = new SparseArray<>(); - // Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the - // network is lingering or not. Always set to the expiry of the LingerTimer that expires last. - // When the timer fires, all linger state is cleared, and if the network has no requests, it is - // torn down. - private WakeupMessage mLingerMessage; + // Inactivity expiry timer. Armed whenever mInactivityTimers is non-empty, regardless of + // whether the network is inactive or not. Always set to the expiry of the mInactivityTimers + // that expires last. When the timer fires, all inactivity state is cleared, and if the network + // has no requests, it is torn down. + private WakeupMessage mInactivityMessage; - // Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed. - private long mLingerExpiryMs; + // Inactivity expiry. Holds the expiry time of the inactivity timer, or 0 if the timer is not + // armed. + private long mInactivityExpiryMs; - // Whether the network is lingering or not. Must be maintained separately from the above because + // Whether the network is inactive or not. Must be maintained separately from the above because // it depends on the state of other networks and requests, which only ConnectivityService knows. // (Example: we don't linger a network if it would become the best for a NetworkRequest if it // validated). - private boolean mLingering; + private boolean mInactive; // This represents the quality of the network with no clear scale. private int mScore; @@ -895,20 +904,25 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { /** * Sets the specified requestId to linger on this network for the specified time. Called by - * ConnectivityService when the request is moved to another network with a higher score. + * ConnectivityService when the request is moved to another network with a higher score, or + * when a network is newly created. + * + * @param requestId The requestId of the request that no longer need to be served by this + * network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the + * {@code LingerTimer} for a newly created network. */ public void lingerRequest(int requestId, long now, long duration) { - if (mLingerTimerForRequest.get(requestId) != null) { + if (mInactivityTimerForRequest.get(requestId) != null) { // Cannot happen. Once a request is lingering on a particular network, we cannot // re-linger it unless that network becomes the best for that request again, in which // case we should have unlingered it. Log.wtf(TAG, toShortString() + ": request " + requestId + " already lingered"); } final long expiryMs = now + duration; - LingerTimer timer = new LingerTimer(requestId, expiryMs); - if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString()); - mLingerTimers.add(timer); - mLingerTimerForRequest.put(requestId, timer); + InactivityTimer timer = new InactivityTimer(requestId, expiryMs); + if (VDBG) Log.d(TAG, "Adding InactivityTimer " + timer + " to " + toShortString()); + mInactivityTimers.add(timer); + mInactivityTimerForRequest.put(requestId, timer); } /** @@ -916,23 +930,25 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { * Returns true if the given requestId was lingering on this network, false otherwise. */ public boolean unlingerRequest(int requestId) { - LingerTimer timer = mLingerTimerForRequest.get(requestId); + InactivityTimer timer = mInactivityTimerForRequest.get(requestId); if (timer != null) { - if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString()); - mLingerTimers.remove(timer); - mLingerTimerForRequest.remove(requestId); + if (VDBG) { + Log.d(TAG, "Removing InactivityTimer " + timer + " from " + toShortString()); + } + mInactivityTimers.remove(timer); + mInactivityTimerForRequest.remove(requestId); return true; } return false; } - public long getLingerExpiry() { - return mLingerExpiryMs; + public long getInactivityExpiry() { + return mInactivityExpiryMs; } - public void updateLingerTimer() { - long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs; - if (newExpiry == mLingerExpiryMs) return; + public void updateInactivityTimer() { + long newExpiry = mInactivityTimers.isEmpty() ? 0 : mInactivityTimers.last().expiryMs; + if (newExpiry == mInactivityExpiryMs) return; // Even if we're going to reschedule the timer, cancel it first. This is because the // semantics of WakeupMessage guarantee that if cancel is called then the alarm will @@ -940,49 +956,65 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage // has already been dispatched, rescheduling to some time in the future won't stop it // from calling its callback immediately. - if (mLingerMessage != null) { - mLingerMessage.cancel(); - mLingerMessage = null; + if (mInactivityMessage != null) { + mInactivityMessage.cancel(); + mInactivityMessage = null; } if (newExpiry > 0) { - mLingerMessage = new WakeupMessage( + mInactivityMessage = new WakeupMessage( mContext, mHandler, "NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */, EVENT_NETWORK_LINGER_COMPLETE /* cmd */, 0 /* arg1 (unused) */, 0 /* arg2 (unused) */, this /* obj (NetworkAgentInfo) */); - mLingerMessage.schedule(newExpiry); + mInactivityMessage.schedule(newExpiry); } - mLingerExpiryMs = newExpiry; + mInactivityExpiryMs = newExpiry; } - public void linger() { - mLingering = true; + public void setInactive() { + mInactive = true; } - public void unlinger() { - mLingering = false; + public void unsetInactive() { + mInactive = false; + } + + public boolean isInactive() { + return mInactive; } public boolean isLingering() { - return mLingering; + return mInactive && !isNascent(); } - public void clearLingerState() { - if (mLingerMessage != null) { - mLingerMessage.cancel(); - mLingerMessage = null; + /** + * Return whether the network is just connected and about to be torn down because of not + * satisfying any request. + */ + public boolean isNascent() { + return mInactive && mInactivityTimers.size() == 1 + && mInactivityTimers.first().requestId == NetworkRequest.REQUEST_ID_NONE; + } + + public void clearInactivityState() { + if (mInactivityMessage != null) { + mInactivityMessage.cancel(); + mInactivityMessage = null; } - mLingerTimers.clear(); - mLingerTimerForRequest.clear(); - updateLingerTimer(); // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage. - mLingering = false; + mInactivityTimers.clear(); + mInactivityTimerForRequest.clear(); + // Sets mInactivityExpiryMs, cancels and nulls out mInactivityMessage. + updateInactivityTimer(); + mInactive = false; } - public void dumpLingerTimers(PrintWriter pw) { - for (LingerTimer timer : mLingerTimers) { pw.println(timer); } + public void dumpInactivityTimers(PrintWriter pw) { + for (InactivityTimer timer : mInactivityTimers) { + pw.println(timer); + } } /** @@ -1016,7 +1048,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { + "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{" + networkInfo.toShortString() + "} " + " Score{" + getCurrentScore() + "} " - + (isLingering() ? " lingering" : "") + + (isNascent() ? " nascent" : (isLingering() ? " lingering" : "")) + (everValidated ? " everValidated" : "") + (lastValidated ? " lastValidated" : "") + (partialConnectivity ? " partialConnectivity" : "") diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index d507b5f82bd0..8d21f6f0f59f 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -265,7 +265,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse for (Entry<Integer, Boolean> app : apps.entrySet()) { List<Integer> list = app.getValue() ? system : network; for (int user : users) { - list.add(UserHandle.getUid(user, app.getKey())); + final UserHandle handle = UserHandle.of(user); + if (handle == null) continue; + + list.add(UserHandle.getUid(handle, app.getKey())); } } try { @@ -550,7 +553,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse for (UidRange range : ranges) { for (int userId = range.getStartUser(); userId <= range.getEndUser(); userId++) { for (int appId : appIds) { - final int uid = UserHandle.getUid(userId, appId); + final UserHandle handle = UserHandle.of(userId); + if (handle == null) continue; + + final int uid = UserHandle.getUid(handle, appId); if (range.contains(uid)) { result.add(uid); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index d956ba375ba1..fc2c7e01efde 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -51,6 +51,7 @@ import android.net.DnsResolver; import android.net.INetd; import android.net.INetworkManagementEventObserver; import android.net.Ikev2VpnProfile; +import android.net.InetAddresses; import android.net.IpPrefix; import android.net.IpSecManager; import android.net.IpSecManager.IpSecTunnelInterface; @@ -111,6 +112,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; +import com.android.net.module.util.NetworkStackConstants; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.net.BaseNetworkObserver; @@ -203,6 +205,7 @@ public class Vpn { protected final NetworkCapabilities mNetworkCapabilities; private final SystemServices mSystemServices; private final Ikev2SessionCreator mIkev2SessionCreator; + private final UserManager mUserManager; /** * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This @@ -277,6 +280,10 @@ public class Vpn { return LocalServices.getService(DeviceIdleInternal.class); } + public PendingIntent getIntentForStatusPanel(Context context) { + return VpnConfig.getIntentForStatusPanel(context); + } + public void sendArgumentsToDaemon( final String daemon, final LocalSocket socket, final String[] arguments, final RetryScheduler retryScheduler) throws IOException, InterruptedException { @@ -327,7 +334,7 @@ public class Vpn { public InetAddress resolve(final String endpoint) throws ExecutionException, InterruptedException { try { - return InetAddress.parseNumericAddress(endpoint); + return InetAddresses.parseNumericAddress(endpoint); } catch (IllegalArgumentException e) { // Endpoint is not numeric : fall through and resolve } @@ -405,6 +412,7 @@ public class Vpn { mLooper = looper; mSystemServices = systemServices; mIkev2SessionCreator = ikev2SessionCreator; + mUserManager = mContext.getSystemService(UserManager.class); mPackage = VpnConfig.LEGACY_VPN; mOwnerUID = getAppUid(mPackage, mUserId); @@ -1119,7 +1127,7 @@ public class Vpn { if (mConfig.dnsServers != null) { for (String dnsServer : mConfig.dnsServers) { - InetAddress address = InetAddress.parseNumericAddress(dnsServer); + InetAddress address = InetAddresses.parseNumericAddress(dnsServer); lp.addDnsServer(address); allowIPv4 |= address instanceof Inet4Address; allowIPv6 |= address instanceof Inet6Address; @@ -1129,10 +1137,12 @@ public class Vpn { lp.setHttpProxy(mConfig.proxyInfo); if (!allowIPv4) { - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); + lp.addRoute(new RouteInfo(new IpPrefix( + NetworkStackConstants.IPV4_ADDR_ANY, 0), RTN_UNREACHABLE)); } if (!allowIPv6) { - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); + lp.addRoute(new RouteInfo(new IpPrefix( + NetworkStackConstants.IPV6_ADDR_ANY, 0), RTN_UNREACHABLE)); } // Concatenate search domains into a string. @@ -1431,7 +1441,7 @@ public class Vpn { final long token = Binder.clearCallingIdentity(); List<UserInfo> users; try { - users = UserManager.get(mContext).getAliveUsers(); + users = mUserManager.getAliveUsers(); } finally { Binder.restoreCallingIdentity(token); } @@ -1515,7 +1525,7 @@ public class Vpn { */ public void onUserAdded(int userId) { // If the user is restricted tie them to the parent user's VPN - UserInfo user = UserManager.get(mContext).getUserInfo(userId); + UserInfo user = mUserManager.getUserInfo(userId); if (user.isRestricted() && user.restrictedProfileParentId == mUserId) { synchronized(Vpn.this) { final Set<UidRange> existingRanges = mNetworkCapabilities.getUids(); @@ -1543,7 +1553,7 @@ public class Vpn { */ public void onUserRemoved(int userId) { // clean up if restricted - UserInfo user = UserManager.get(mContext).getUserInfo(userId); + UserInfo user = mUserManager.getUserInfo(userId); if (user.isRestricted() && user.restrictedProfileParentId == mUserId) { synchronized(Vpn.this) { final Set<UidRange> existingRanges = mNetworkCapabilities.getUids(); @@ -1768,7 +1778,7 @@ public class Vpn { private void prepareStatusIntent() { final long token = Binder.clearCallingIdentity(); try { - mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext); + mStatusIntent = mDeps.getIntentForStatusPanel(mContext); } finally { Binder.restoreCallingIdentity(token); } @@ -1968,8 +1978,7 @@ public class Vpn { private void enforceNotRestrictedUser() { Binder.withCleanCallingIdentity(() -> { - final UserManager mgr = UserManager.get(mContext); - final UserInfo user = mgr.getUserInfo(mUserId); + final UserInfo user = mUserManager.getUserInfo(mUserId); if (user.isRestricted()) { throw new SecurityException("Restricted users cannot configure VPNs"); @@ -2004,9 +2013,8 @@ public class Vpn { */ public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore, @Nullable Network underlying, @NonNull LinkProperties egress) { - UserManager mgr = UserManager.get(mContext); - UserInfo user = mgr.getUserInfo(mUserId); - if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN, + UserInfo user = mUserManager.getUserInfo(mUserId); + if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN, new UserHandle(mUserId))) { throw new SecurityException("Restricted users cannot establish VPNs"); } diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java index 802472fcfba8..e496d77deaf5 100644 --- a/services/core/java/com/android/server/devicestate/DeviceState.java +++ b/services/core/java/com/android/server/devicestate/DeviceState.java @@ -16,8 +16,6 @@ package com.android.server.devicestate; -import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; - import android.annotation.IntRange; import android.annotation.NonNull; @@ -37,16 +35,16 @@ import java.util.Objects; */ public final class DeviceState { /** Unique identifier for the device state. */ - @IntRange(from = INVALID_DEVICE_STATE) + @IntRange(from = 0) private final int mIdentifier; /** String description of the device state. */ @NonNull private final String mName; - public DeviceState(@IntRange(from = INVALID_DEVICE_STATE) int identifier, + public DeviceState(@IntRange(from = 0) int identifier, @NonNull String name) { - if (identifier != INVALID_DEVICE_STATE && identifier < 0) { + if (identifier < 0) { throw new IllegalArgumentException("Identifier must be greater than or equal to zero."); } mIdentifier = identifier; @@ -54,7 +52,7 @@ public final class DeviceState { } /** Returns the unique identifier for the device state. */ - @IntRange(from = INVALID_DEVICE_STATE) + @IntRange(from = 0) public int getIdentifier() { return mIdentifier; } diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index 375ec3a0f95f..984a17694e07 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -17,13 +17,13 @@ package com.android.server.devicestate; import static android.Manifest.permission.CONTROL_DEVICE_STATE; -import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; +import static android.hardware.devicestate.DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.content.pm.PackageManager; +import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.IDeviceStateManager; import android.hardware.devicestate.IDeviceStateManagerCallback; import android.os.Binder; @@ -31,6 +31,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; @@ -62,8 +64,12 @@ import java.util.Optional; * the {@link DeviceStateProvider} to modify the current device state and communicating with the * {@link DeviceStatePolicy policy} to ensure the system is configured to match the requested state. * </p> + * The service also provides the {@link DeviceStateManager} API allowing clients to listen for + * changes in device state and submit requests to override the device state provided by the + * {@link DeviceStateProvider}. * * @see DeviceStatePolicy + * @see DeviceStateManager */ public final class DeviceStateManagerService extends SystemService { private static final String TAG = "DeviceStateManagerService"; @@ -79,11 +85,11 @@ public final class DeviceStateManagerService extends SystemService { @GuardedBy("mLock") private SparseArray<DeviceState> mDeviceStates = new SparseArray<>(); - // The current committed device state. The default of INVALID_DEVICE_STATE will be replaced by + // The current committed device state. The default of UNSET will be replaced by // the current state after the initial callback from the DeviceStateProvider. @GuardedBy("mLock") @NonNull - private DeviceState mCommittedState = new DeviceState(INVALID_DEVICE_STATE, "INVALID"); + private DeviceState mCommittedState = new DeviceState(0, "UNSET"); // The device state that is currently awaiting callback from the policy to be committed. @GuardedBy("mLock") @NonNull @@ -91,19 +97,23 @@ public final class DeviceStateManagerService extends SystemService { // Whether or not the policy is currently waiting to be notified of the current pending state. @GuardedBy("mLock") private boolean mIsPolicyWaitingForState = false; - // The device state that is currently requested and is next to be configured and committed. - // Can be overwritten by an override state value if requested. - @GuardedBy("mLock") - @NonNull - private Optional<DeviceState> mRequestedState = Optional.empty(); - // The most recently requested override state, or empty if no override is requested. + + // The device state that is set by the device state provider. @GuardedBy("mLock") @NonNull - private Optional<DeviceState> mRequestedOverrideState = Optional.empty(); + private Optional<DeviceState> mBaseState = Optional.empty(); - // List of registered callbacks indexed by process id. + // List of processes registered to receive notifications about changes to device state and + // request status indexed by process id. @GuardedBy("mLock") - private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>(); + private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>(); + // List of override requests with the highest precedence request at the end. + @GuardedBy("mLock") + private final ArrayList<OverrideRequestRecord> mRequestRecords = new ArrayList<>(); + // Set of override requests that are pending a call to notifyStatusIfNeeded() to be notified + // of a change in status. + @GuardedBy("mLock") + private final ArraySet<OverrideRequestRecord> mRequestsPendingStatusChange = new ArraySet<>(); public DeviceStateManagerService(@NonNull Context context) { this(context, new DeviceStatePolicyImpl(context)); @@ -148,55 +158,32 @@ public final class DeviceStateManagerService extends SystemService { } /** - * Returns the requested state. The service will configure the device to match the requested - * state when possible. - */ - @NonNull - Optional<DeviceState> getRequestedState() { - synchronized (mLock) { - return mRequestedState; - } - } - - /** - * Overrides the current device state with the provided state. + * Returns the base state. The service will configure the device to match the base state when + * there is no active request to override the base state. * - * @return {@code true} if the override state is valid and supported, {@code false} otherwise. + * @see #getOverrideState() */ - boolean setOverrideState(int overrideState) { - if (getContext().checkCallingOrSelfPermission(CONTROL_DEVICE_STATE) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + CONTROL_DEVICE_STATE); - } - + @NonNull + Optional<DeviceState> getBaseState() { synchronized (mLock) { - if (overrideState != INVALID_DEVICE_STATE && !isSupportedStateLocked(overrideState)) { - return false; - } - - mRequestedOverrideState = getStateLocked(overrideState); - updatePendingStateLocked(); + return mBaseState; } - - notifyPolicyIfNeeded(); - return true; - } - - /** - * Clears an override state set with {@link #setOverrideState(int)}. - */ - void clearOverrideState() { - setOverrideState(INVALID_DEVICE_STATE); } /** - * Returns the current requested override state, or {@link Optional#empty()} if no override - * state is requested. + * Returns the current override state, or {@link Optional#empty()} if no override state is + * requested. If an override states is present, the returned state will take precedence over + * the base state returned from {@link #getBaseState()}. */ @NonNull Optional<DeviceState> getOverrideState() { synchronized (mLock) { - return mRequestedOverrideState; + if (mRequestRecords.isEmpty()) { + return Optional.empty(); + } + + OverrideRequestRecord topRequest = mRequestRecords.get(mRequestRecords.size() - 1); + return Optional.of(topRequest.mRequestedState); } } @@ -211,6 +198,17 @@ public final class DeviceStateManagerService extends SystemService { } } + /** Returns the list of currently supported device state identifiers. */ + private int[] getSupportedStateIdentifiers() { + synchronized (mLock) { + int[] supportedStates = new int[mDeviceStates.size()]; + for (int i = 0; i < supportedStates.length; i++) { + supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier(); + } + return supportedStates; + } + } + @VisibleForTesting IDeviceStateManager getBinderService() { return mBinderService; @@ -224,22 +222,26 @@ public final class DeviceStateManagerService extends SystemService { mDeviceStates.put(state.getIdentifier(), state); } - if (mRequestedState.isPresent() - && !isSupportedStateLocked(mRequestedState.get().getIdentifier())) { - // The current requested state is no longer valid. We'll clear it here, though + if (mBaseState.isPresent() + && !isSupportedStateLocked(mBaseState.get().getIdentifier())) { + // The current base state is no longer valid. We'll clear it here, though // we won't actually update the current state until a callback comes from the // provider with the most recent state. - mRequestedState = Optional.empty(); + mBaseState = Optional.empty(); } - if (mRequestedOverrideState.isPresent() - && !isSupportedStateLocked(mRequestedOverrideState.get().getIdentifier())) { - // The current override state is no longer valid. We'll clear it here and update - // the committed state if necessary. - mRequestedOverrideState = Optional.empty(); + + final int requestSize = mRequestRecords.size(); + for (int i = 0; i < requestSize; i++) { + OverrideRequestRecord request = mRequestRecords.get(i); + if (!isSupportedStateLocked(request.mRequestedState.getIdentifier())) { + request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED); + } } + updatePendingStateLocked(); } + notifyRequestsOfStatusChangeIfNeeded(); notifyPolicyIfNeeded(); } @@ -261,20 +263,37 @@ public final class DeviceStateManagerService extends SystemService { } /** - * Requests that the system enter the provided {@code state}. The request may not be honored - * under certain conditions, for example if the provided state is not supported. + * Requests to set the base state. The request may not be honored under certain conditions, for + * example if the provided state is not supported. * * @see #isSupportedStateLocked(int) */ - private void requestState(int identifier) { + private void setBaseState(int identifier) { synchronized (mLock) { - final Optional<DeviceState> requestedState = getStateLocked(identifier); - if (requestedState.isPresent()) { - mRequestedState = requestedState; + if (mBaseState.isPresent() && mBaseState.get().getIdentifier() == identifier) { + // Base state hasn't changed. Nothing to do. + return; + } + + final Optional<DeviceState> baseState = getStateLocked(identifier); + if (!baseState.isPresent()) { + throw new IllegalArgumentException("Base state is not supported"); } + + mBaseState = baseState; + + final int requestSize = mRequestRecords.size(); + for (int i = 0; i < requestSize; i++) { + OverrideRequestRecord request = mRequestRecords.get(i); + if ((request.mFlags & FLAG_CANCEL_WHEN_BASE_CHANGES) > 0) { + request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED); + } + } + updatePendingStateLocked(); } + notifyRequestsOfStatusChangeIfNeeded(); notifyPolicyIfNeeded(); } @@ -290,10 +309,10 @@ public final class DeviceStateManagerService extends SystemService { } final DeviceState stateToConfigure; - if (mRequestedOverrideState.isPresent()) { - stateToConfigure = mRequestedOverrideState.get(); + if (!mRequestRecords.isEmpty()) { + stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState; } else { - stateToConfigure = mRequestedState.orElse(null); + stateToConfigure = mBaseState.orElse(null); } if (stateToConfigure == null) { @@ -360,6 +379,13 @@ public final class DeviceStateManagerService extends SystemService { } mCommittedState = mPendingState.get(); newState = mCommittedState.getIdentifier(); + + if (!mRequestRecords.isEmpty()) { + final OverrideRequestRecord topRequest = + mRequestRecords.get(mRequestRecords.size() - 1); + topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE); + } + mPendingState = Optional.empty(); updatePendingStateLocked(); } @@ -367,6 +393,9 @@ public final class DeviceStateManagerService extends SystemService { // Notify callbacks of a change. notifyDeviceStateChanged(newState); + // Notify the top request that it's active. + notifyRequestsOfStatusChangeIfNeeded(); + // Try to configure the next state if needed. notifyPolicyIfNeeded(); } @@ -377,43 +406,69 @@ public final class DeviceStateManagerService extends SystemService { "Attempting to notify callbacks with service lock held."); } - // Grab the lock and copy the callbacks. - ArrayList<CallbackRecord> callbacks; + // Grab the lock and copy the process records. + ArrayList<ProcessRecord> registeredProcesses; synchronized (mLock) { - if (mCallbacks.size() == 0) { + if (mProcessRecords.size() == 0) { return; } - callbacks = new ArrayList<>(); - for (int i = 0; i < mCallbacks.size(); i++) { - callbacks.add(mCallbacks.valueAt(i)); + registeredProcesses = new ArrayList<>(); + for (int i = 0; i < mProcessRecords.size(); i++) { + registeredProcesses.add(mProcessRecords.valueAt(i)); + } + } + + // After releasing the lock, send the notifications out. + for (int i = 0; i < registeredProcesses.size(); i++) { + registeredProcesses.get(i).notifyDeviceStateAsync(deviceState); + } + } + + /** + * Notifies all dirty requests (requests that have a change in status, but have not yet been + * notified) that their status has changed. + */ + private void notifyRequestsOfStatusChangeIfNeeded() { + if (Thread.holdsLock(mLock)) { + throw new IllegalStateException( + "Attempting to notify requests with service lock held."); + } + + ArraySet<OverrideRequestRecord> dirtyRequests; + synchronized (mLock) { + if (mRequestsPendingStatusChange.isEmpty()) { + return; } + + dirtyRequests = new ArraySet<>(mRequestsPendingStatusChange); + mRequestsPendingStatusChange.clear(); } // After releasing the lock, send the notifications out. - for (int i = 0; i < callbacks.size(); i++) { - callbacks.get(i).notifyDeviceStateAsync(deviceState); + for (int i = 0; i < dirtyRequests.size(); i++) { + dirtyRequests.valueAt(i).notifyStatusIfNeeded(); } } - private void registerCallbackInternal(IDeviceStateManagerCallback callback, int callingPid) { + private void registerProcess(int pid, IDeviceStateManagerCallback callback) { int currentState; - CallbackRecord record; + ProcessRecord record; // Grab the lock to register the callback and get the current state. synchronized (mLock) { - if (mCallbacks.contains(callingPid)) { + if (mProcessRecords.contains(pid)) { throw new SecurityException("The calling process has already registered an" + " IDeviceStateManagerCallback."); } - record = new CallbackRecord(callback, callingPid); + record = new ProcessRecord(callback, pid); try { callback.asBinder().linkToDeath(record, 0); } catch (RemoteException ex) { throw new RuntimeException(ex); } - mCallbacks.put(callingPid, record); + mProcessRecords.put(pid, record); currentState = mCommittedState.getIdentifier(); } @@ -421,10 +476,86 @@ public final class DeviceStateManagerService extends SystemService { record.notifyDeviceStateAsync(currentState); } - private void unregisterCallbackInternal(CallbackRecord record) { + private void handleProcessDied(ProcessRecord processRecord) { + synchronized (mLock) { + // Cancel all requests from this process. + final int requestCount = processRecord.mRequestRecords.size(); + for (int i = 0; i < requestCount; i++) { + final OverrideRequestRecord request = processRecord.mRequestRecords.valueAt(i); + // Cancel the request but don't mark it as dirty since there's no need to send + // notifications if the process has died. + request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED, + false /* markDirty */); + } + + mProcessRecords.remove(processRecord.mPid); + + updatePendingStateLocked(); + } + + notifyPolicyIfNeeded(); + } + + private void requestStateInternal(int state, int flags, int callingPid, + @NonNull IBinder token) { + synchronized (mLock) { + final ProcessRecord processRecord = mProcessRecords.get(callingPid); + if (processRecord == null) { + throw new IllegalStateException("Process " + callingPid + + " has no registered callback."); + } + + if (processRecord.mRequestRecords.get(token) != null) { + throw new IllegalStateException("Request has already been made for the supplied" + + " token: " + token); + } + + final Optional<DeviceState> deviceState = getStateLocked(state); + if (!deviceState.isPresent()) { + throw new IllegalArgumentException("Requested state: " + state + + " is not supported."); + } + + OverrideRequestRecord topRecord = mRequestRecords.isEmpty() + ? null : mRequestRecords.get(mRequestRecords.size() - 1); + if (topRecord != null) { + topRecord.setStatusLocked(OverrideRequestRecord.STATUS_SUSPENDED); + } + + final OverrideRequestRecord request = + new OverrideRequestRecord(processRecord, token, deviceState.get(), flags); + mRequestRecords.add(request); + processRecord.mRequestRecords.put(request.mToken, request); + // We don't set the status of the new request to ACTIVE here as it will be set in + // commitPendingState(). + + updatePendingStateLocked(); + } + + notifyRequestsOfStatusChangeIfNeeded(); + notifyPolicyIfNeeded(); + } + + private void cancelRequestInternal(int callingPid, @NonNull IBinder token) { synchronized (mLock) { - mCallbacks.remove(record.mPid); + final ProcessRecord processRecord = mProcessRecords.get(callingPid); + if (processRecord == null) { + throw new IllegalStateException("Process " + callingPid + + " has no registered callback."); + } + + OverrideRequestRecord request = processRecord.mRequestRecords.get(token); + if (request == null) { + throw new IllegalStateException("No known request for the given token"); + } + + request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED); + + updatePendingStateLocked(); } + + notifyRequestsOfStatusChangeIfNeeded(); + notifyPolicyIfNeeded(); } private void dumpInternal(PrintWriter pw) { @@ -433,15 +564,26 @@ public final class DeviceStateManagerService extends SystemService { synchronized (mLock) { pw.println(" mCommittedState=" + mCommittedState); pw.println(" mPendingState=" + mPendingState); - pw.println(" mRequestedState=" + mRequestedState); - pw.println(" mRequestedOverrideState=" + mRequestedOverrideState); + pw.println(" mBaseState=" + mBaseState); + pw.println(" mOverrideState=" + getOverrideState()); - final int callbackCount = mCallbacks.size(); + final int processCount = mProcessRecords.size(); pw.println(); - pw.println("Callbacks: size=" + callbackCount); - for (int i = 0; i < callbackCount; i++) { - CallbackRecord callback = mCallbacks.valueAt(i); - pw.println(" " + i + ": mPid=" + callback.mPid); + pw.println("Registered processes: size=" + processCount); + for (int i = 0; i < processCount; i++) { + ProcessRecord processRecord = mProcessRecords.valueAt(i); + pw.println(" " + i + ": mPid=" + processRecord.mPid); + } + + final int requestCount = mRequestRecords.size(); + pw.println(); + pw.println("Override requests: size=" + requestCount); + for (int i = 0; i < requestCount; i++) { + OverrideRequestRecord requestRecord = mRequestRecords.get(i); + pw.println(" " + i + ": mPid=" + requestRecord.mProcessRecord.mPid + + ", mRequestedState=" + requestRecord.mRequestedState + + ", mFlags=" + requestRecord.mFlags + + ", mStatus=" + requestRecord.statusToString(requestRecord.mStatus)); } } } @@ -452,12 +594,6 @@ public final class DeviceStateManagerService extends SystemService { if (newDeviceStates.length == 0) { throw new IllegalArgumentException("Supported device states must not be empty"); } - for (int i = 0; i < newDeviceStates.length; i++) { - if (newDeviceStates[i].getIdentifier() == INVALID_DEVICE_STATE) { - throw new IllegalArgumentException( - "Supported device states includes INVALID_DEVICE_STATE identifier"); - } - } updateSupportedStates(newDeviceStates); } @@ -467,22 +603,24 @@ public final class DeviceStateManagerService extends SystemService { throw new IllegalArgumentException("Invalid identifier: " + identifier); } - requestState(identifier); + setBaseState(identifier); } } - private final class CallbackRecord implements IBinder.DeathRecipient { + private final class ProcessRecord implements IBinder.DeathRecipient { private final IDeviceStateManagerCallback mCallback; private final int mPid; - CallbackRecord(IDeviceStateManagerCallback callback, int pid) { + private final ArrayMap<IBinder, OverrideRequestRecord> mRequestRecords = new ArrayMap<>(); + + ProcessRecord(IDeviceStateManagerCallback callback, int pid) { mCallback = callback; mPid = pid; } @Override public void binderDied() { - unregisterCallbackInternal(this); + handleProcessDied(this); } public void notifyDeviceStateAsync(int devicestate) { @@ -493,6 +631,119 @@ public final class DeviceStateManagerService extends SystemService { ex); } } + + public void notifyRequestActiveAsync(OverrideRequestRecord request) { + try { + mCallback.onRequestActive(request.mToken); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.", + ex); + } + } + + public void notifyRequestSuspendedAsync(OverrideRequestRecord request) { + try { + mCallback.onRequestSuspended(request.mToken); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.", + ex); + } + } + + public void notifyRequestCanceledAsync(OverrideRequestRecord request) { + try { + mCallback.onRequestCanceled(request.mToken); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.", + ex); + } + } + } + + /** A record describing a request to override the state of the device. */ + private final class OverrideRequestRecord { + public static final int STATUS_UNKNOWN = 0; + public static final int STATUS_ACTIVE = 1; + public static final int STATUS_SUSPENDED = 2; + public static final int STATUS_CANCELED = 3; + + @Nullable + public String statusToString(int status) { + switch (status) { + case STATUS_ACTIVE: + return "ACTIVE"; + case STATUS_SUSPENDED: + return "SUSPENDED"; + case STATUS_CANCELED: + return "CANCELED"; + case STATUS_UNKNOWN: + return "UNKNOWN"; + default: + return null; + } + } + + private final ProcessRecord mProcessRecord; + @NonNull + private final IBinder mToken; + @NonNull + private final DeviceState mRequestedState; + private final int mFlags; + + private int mStatus = STATUS_UNKNOWN; + private int mLastNotifiedStatus = STATUS_UNKNOWN; + + OverrideRequestRecord(@NonNull ProcessRecord processRecord, @NonNull IBinder token, + @NonNull DeviceState requestedState, int flags) { + mProcessRecord = processRecord; + mToken = token; + mRequestedState = requestedState; + mFlags = flags; + } + + public void setStatusLocked(int status) { + setStatusLocked(status, true /* markDirty */); + } + + public void setStatusLocked(int status, boolean markDirty) { + if (mStatus != status) { + if (mStatus == STATUS_CANCELED) { + throw new IllegalStateException( + "Can not alter the status of a request after set to CANCELED."); + } + + mStatus = status; + + if (mStatus == STATUS_CANCELED) { + mRequestRecords.remove(this); + mProcessRecord.mRequestRecords.remove(mToken); + } + + if (markDirty) { + mRequestsPendingStatusChange.add(this); + } + } + } + + public void notifyStatusIfNeeded() { + int stateToReport; + synchronized (mLock) { + if (mLastNotifiedStatus == mStatus) { + return; + } + + stateToReport = mStatus; + mLastNotifiedStatus = mStatus; + } + + if (stateToReport == STATUS_ACTIVE) { + mProcessRecord.notifyRequestActiveAsync(this); + } else if (stateToReport == STATUS_SUSPENDED) { + mProcessRecord.notifyRequestSuspendedAsync(this); + } else if (stateToReport == STATUS_CANCELED) { + mProcessRecord.notifyRequestCanceledAsync(this); + } + } } /** Implementation of {@link IDeviceStateManager} published as a binder service. */ @@ -506,13 +757,59 @@ public final class DeviceStateManagerService extends SystemService { final int callingPid = Binder.getCallingPid(); final long token = Binder.clearCallingIdentity(); try { - registerCallbackInternal(callback, callingPid); + registerProcess(callingPid, callback); } finally { Binder.restoreCallingIdentity(token); } } @Override // Binder call + public int[] getSupportedDeviceStates() { + final long token = Binder.clearCallingIdentity(); + try { + return getSupportedStateIdentifiers(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public void requestState(IBinder token, int state, int flags) { + getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, + "Permission required to request device state."); + + if (token == null) { + throw new IllegalArgumentException("Request token must not be null."); + } + + final int callingPid = Binder.getCallingPid(); + final long callingIdentity = Binder.clearCallingIdentity(); + try { + requestStateInternal(state, flags, callingPid, token); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + + @Override // Binder call + public void cancelRequest(IBinder token) { + getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, + "Permission required to clear requested device state."); + + if (token == null) { + throw new IllegalArgumentException("Request token must not be null."); + } + + final int callingPid = Binder.getCallingPid(); + final long callingIdentity = Binder.clearCallingIdentity(); + try { + cancelRequestInternal(callingPid, token); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + + @Override // Binder call public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver result) { new DeviceStateManagerShellCommand(DeviceStateManagerService.this) diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java index 7914531f9910..6cc55a6c4774 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java @@ -16,6 +16,13 @@ package com.android.server.devicestate; +import static android.Manifest.permission.CONTROL_DEVICE_STATE; + +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.devicestate.DeviceStateManager; +import android.hardware.devicestate.DeviceStateRequest; +import android.os.Binder; import android.os.ShellCommand; import java.io.PrintWriter; @@ -27,10 +34,15 @@ import java.util.Optional; * Use with {@code adb shell cmd device_state ...}. */ public class DeviceStateManagerShellCommand extends ShellCommand { - private final DeviceStateManagerService mInternal; + @Nullable + private static DeviceStateRequest sLastRequest; + + private final DeviceStateManagerService mService; + private final DeviceStateManager mClient; public DeviceStateManagerShellCommand(DeviceStateManagerService service) { - mInternal = service; + mService = service; + mClient = service.getContext().getSystemService(DeviceStateManager.class); } @Override @@ -51,15 +63,15 @@ public class DeviceStateManagerShellCommand extends ShellCommand { } private void printState(PrintWriter pw) { - DeviceState committedState = mInternal.getCommittedState(); - Optional<DeviceState> requestedState = mInternal.getRequestedState(); - Optional<DeviceState> requestedOverrideState = mInternal.getOverrideState(); + DeviceState committedState = mService.getCommittedState(); + Optional<DeviceState> baseState = mService.getBaseState(); + Optional<DeviceState> overrideState = mService.getOverrideState(); pw.println("Committed state: " + committedState); - if (requestedOverrideState.isPresent()) { + if (overrideState.isPresent()) { pw.println("----------------------"); - pw.println("Base state: " + requestedState.orElse(null)); - pw.println("Override state: " + requestedOverrideState.get()); + pw.println("Base state: " + baseState.orElse(null)); + pw.println("Override state: " + overrideState.get()); } } @@ -67,32 +79,51 @@ public class DeviceStateManagerShellCommand extends ShellCommand { final String nextArg = getNextArg(); if (nextArg == null) { printState(pw); - } else if ("reset".equals(nextArg)) { - mInternal.clearOverrideState(); - } else { - int requestedState; - try { - requestedState = Integer.parseInt(nextArg); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Error: requested state should be an integer"); - return -1; - } + } - boolean success = mInternal.setOverrideState(requestedState); - if (!success) { - getErrPrintWriter().println("Error: failed to set override state. Run:"); - getErrPrintWriter().println(""); - getErrPrintWriter().println(" print-states"); - getErrPrintWriter().println(""); - getErrPrintWriter().println("to get the list of currently supported device states"); - return -1; + final Context context = mService.getContext(); + context.enforceCallingOrSelfPermission( + CONTROL_DEVICE_STATE, + "Permission required to request device state."); + final long callingIdentity = Binder.clearCallingIdentity(); + try { + if ("reset".equals(nextArg)) { + if (sLastRequest != null) { + mClient.cancelRequest(sLastRequest); + sLastRequest = null; + } + } else { + int requestedState = Integer.parseInt(nextArg); + DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build(); + + mClient.requestState(request, null /* executor */, null /* callback */); + if (sLastRequest != null) { + mClient.cancelRequest(sLastRequest); + } + + sLastRequest = request; } + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: requested state should be an integer"); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println("Error: " + e.getMessage()); + getErrPrintWriter().println("-------------------"); + getErrPrintWriter().println("Run:"); + getErrPrintWriter().println(""); + getErrPrintWriter().println(" print-states"); + getErrPrintWriter().println(""); + getErrPrintWriter().println("to get the list of currently supported device states"); + return -1; + } finally { + Binder.restoreCallingIdentity(callingIdentity); } + return 0; } private int runPrintStates(PrintWriter pw) { - DeviceState[] states = mInternal.getSupportedStates(); + DeviceState[] states = mService.getSupportedStates(); pw.print("Supported states: [\n"); for (int i = 0; i < states.length; i++) { pw.print(" " + states[i] + ",\n"); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index e5151d84c33e..0950d5dd076f 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -514,8 +514,8 @@ public final class DisplayManagerService extends SystemService { DeviceStateManager deviceStateManager = mContext.getSystemService(DeviceStateManager.class); - deviceStateManager.registerDeviceStateListener(new DeviceStateListener(), - new HandlerExecutor(mHandler)); + deviceStateManager.addDeviceStateListener(new HandlerExecutor(mHandler), + new DeviceStateListener()); scheduleTraversalLocked(false); } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 7e6a13766746..73ebb2e48037 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -105,17 +105,18 @@ final class LocalDisplayAdapter extends DisplayAdapter { Slog.w(TAG, "No valid info found for display device " + physicalDisplayId); return; } - SurfaceControl.DisplayConfig[] configs = SurfaceControl.getDisplayConfigs(displayToken); - if (configs == null) { - // There are no valid configs for this device, so we can't use it - Slog.w(TAG, "No valid configs found for display device " + physicalDisplayId); + SurfaceControl.DisplayMode[] displayModes = + SurfaceControl.getDisplayModes(displayToken); + if (displayModes == null) { + // There are no valid modes for this device, so we can't use it + Slog.w(TAG, "No valid modes found for display device " + physicalDisplayId); return; } - int activeConfig = SurfaceControl.getActiveConfig(displayToken); - if (activeConfig < 0) { - // There is no active config, and for now we don't have the + int activeDisplayMode = SurfaceControl.getActiveDisplayMode(displayToken); + if (activeDisplayMode < 0) { + // There is no active mode, and for now we don't have the // policy to set one. - Slog.w(TAG, "No active config found for display device " + physicalDisplayId); + Slog.w(TAG, "No active mode found for display device " + physicalDisplayId); return; } int activeColorMode = SurfaceControl.getActiveColorMode(displayToken); @@ -127,8 +128,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { physicalDisplayId); activeColorMode = Display.COLOR_MODE_INVALID; } - SurfaceControl.DesiredDisplayConfigSpecs configSpecs = - SurfaceControl.getDesiredDisplayConfigSpecs(displayToken); + SurfaceControl.DesiredDisplayModeSpecs modeSpecsSpecs = + SurfaceControl.getDesiredDisplayModeSpecs(displayToken); int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken); Display.HdrCapabilities hdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken); @@ -136,13 +137,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { if (device == null) { // Display was added. final boolean isDefaultDisplay = mDevices.size() == 0; - device = new LocalDisplayDevice(displayToken, physicalDisplayId, info, - configs, activeConfig, configSpecs, colorModes, activeColorMode, + device = new LocalDisplayDevice(displayToken, physicalDisplayId, info, displayModes, + activeDisplayMode, modeSpecsSpecs, colorModes, activeColorMode, hdrCapabilities, isDefaultDisplay); mDevices.put(physicalDisplayId, device); sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); - } else if (device.updateDisplayPropertiesLocked(info, configs, activeConfig, - configSpecs, colorModes, activeColorMode, hdrCapabilities)) { + } else if (device.updateDisplayPropertiesLocked(info, displayModes, activeDisplayMode, + modeSpecsSpecs, colorModes, activeColorMode, hdrCapabilities)) { sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED); } } else { @@ -189,12 +190,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { // This is only set in the runnable returned from requestDisplayStateLocked. private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; private int mDefaultModeId; - private int mDefaultConfigGroup; + private int mDefaultModeGroup; private int mActiveModeId; private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs = new DisplayModeDirector.DesiredDisplayModeSpecs(); private boolean mDisplayModeSpecsInvalid; - private int mActiveConfigId; + private int mActiveDisplayModeId; private int mActiveColorMode; private Display.HdrCapabilities mHdrCapabilities; private boolean mAllmSupported; @@ -204,7 +205,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private boolean mSidekickActive; private SidekickInternal mSidekickInternal; private SurfaceControl.DisplayInfo mDisplayInfo; - private SurfaceControl.DisplayConfig[] mDisplayConfigs; + private SurfaceControl.DisplayMode[] mDisplayModes; private Spline mSystemBrightnessToNits; private Spline mNitsToHalBrightness; private DisplayDeviceConfig mDisplayDeviceConfig; @@ -213,15 +214,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { new DisplayEventReceiver.FrameRateOverride[0]; LocalDisplayDevice(IBinder displayToken, long physicalDisplayId, - SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs, - int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs, + SurfaceControl.DisplayInfo info, SurfaceControl.DisplayMode[] displayModes, + int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs, int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities, boolean isDefaultDisplay) { super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId); mPhysicalDisplayId = physicalDisplayId; mIsDefaultDisplay = isDefaultDisplay; - updateDisplayPropertiesLocked(info, configs, activeConfigId, configSpecs, colorModes, - activeColorMode, hdrCapabilities); + updateDisplayPropertiesLocked(info, displayModes, activeDisplayModeId, modeSpecs, + colorModes, activeColorMode, hdrCapabilities); mSidekickInternal = LocalServices.getService(SidekickInternal.class); mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay); mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken); @@ -241,10 +242,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { * Returns true if there is a change. **/ public boolean updateDisplayPropertiesLocked(SurfaceControl.DisplayInfo info, - SurfaceControl.DisplayConfig[] configs, - int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs, + SurfaceControl.DisplayMode[] displayModes, + int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs, int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities) { - boolean changed = updateDisplayConfigsLocked(configs, activeConfigId, configSpecs); + boolean changed = updateDisplayModesLocked( + displayModes, activeDisplayModeId, modeSpecs); changed |= updateDisplayInfo(info); changed |= updateColorModesLocked(colorModes, activeColorMode); changed |= updateHdrCapabilitiesLocked(hdrCapabilities); @@ -255,35 +257,35 @@ final class LocalDisplayAdapter extends DisplayAdapter { return changed; } - public boolean updateDisplayConfigsLocked( - SurfaceControl.DisplayConfig[] configs, int activeConfigId, - SurfaceControl.DesiredDisplayConfigSpecs configSpecs) { - mDisplayConfigs = Arrays.copyOf(configs, configs.length); - mActiveConfigId = activeConfigId; + public boolean updateDisplayModesLocked( + SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId, + SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { + mDisplayModes = Arrays.copyOf(displayModes, displayModes.length); + mActiveDisplayModeId = activeDisplayModeId; // Build an updated list of all existing modes. ArrayList<DisplayModeRecord> records = new ArrayList<>(); boolean modesAdded = false; - for (int i = 0; i < configs.length; i++) { - SurfaceControl.DisplayConfig config = configs[i]; + for (int i = 0; i < displayModes.length; i++) { + SurfaceControl.DisplayMode mode = displayModes[i]; List<Float> alternativeRefreshRates = new ArrayList<>(); - for (int j = 0; j < configs.length; j++) { - SurfaceControl.DisplayConfig other = configs[j]; - boolean isAlternative = j != i && other.width == config.width - && other.height == config.height - && other.refreshRate != config.refreshRate - && other.configGroup == config.configGroup; + for (int j = 0; j < displayModes.length; j++) { + SurfaceControl.DisplayMode other = displayModes[j]; + boolean isAlternative = j != i && other.width == mode.width + && other.height == mode.height + && other.refreshRate != mode.refreshRate + && other.group == mode.group; if (isAlternative) { - alternativeRefreshRates.add(configs[j].refreshRate); + alternativeRefreshRates.add(displayModes[j].refreshRate); } } Collections.sort(alternativeRefreshRates); // First, check to see if we've already added a matching mode. Since not all // configuration options are exposed via Display.Mode, it's possible that we have - // multiple DisplayConfigs that would generate the same Display.Mode. + // multiple DisplayModess that would generate the same Display.Mode. boolean existingMode = false; for (DisplayModeRecord record : records) { - if (record.hasMatchingMode(config) + if (record.hasMatchingMode(mode) && refreshRatesEquals(alternativeRefreshRates, record.mMode.getAlternativeRefreshRates())) { existingMode = true; @@ -296,13 +298,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { // If we haven't already added a mode for this configuration to the new set of // supported modes then check to see if we have one in the prior set of supported // modes to reuse. - DisplayModeRecord record = findDisplayModeRecord(config, alternativeRefreshRates); + DisplayModeRecord record = findDisplayModeRecord(mode, alternativeRefreshRates); if (record == null) { float[] alternativeRates = new float[alternativeRefreshRates.size()]; for (int j = 0; j < alternativeRates.length; j++) { alternativeRates[j] = alternativeRefreshRates.get(j); } - record = new DisplayModeRecord(config, alternativeRates); + record = new DisplayModeRecord(mode, alternativeRates); modesAdded = true; } records.add(record); @@ -312,7 +314,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { DisplayModeRecord activeRecord = null; for (int i = 0; i < records.size(); i++) { DisplayModeRecord record = records.get(i); - if (record.hasMatchingMode(configs[activeConfigId])) { + if (record.hasMatchingMode(displayModes[activeDisplayModeId])) { activeRecord = record; break; } @@ -334,20 +336,20 @@ final class LocalDisplayAdapter extends DisplayAdapter { // Check whether surface flinger spontaneously changed display config specs out from // under us. If so, schedule a traversal to reapply our display config specs. if (mDisplayModeSpecs.baseModeId != NO_DISPLAY_MODE_ID) { - int activeBaseMode = findMatchingModeIdLocked(configSpecs.defaultConfig); - // If we can't map the defaultConfig index to a mode, then the physical display - // configs must have changed, and the code below for handling changes to the - // list of available modes will take care of updating display config specs. + int activeBaseMode = findMatchingModeIdLocked(modeSpecs.defaultMode); + // If we can't map the defaultMode index to a mode, then the physical display + // modes must have changed, and the code below for handling changes to the + // list of available modes will take care of updating display mode specs. if (activeBaseMode != NO_DISPLAY_MODE_ID) { if (mDisplayModeSpecs.baseModeId != activeBaseMode || mDisplayModeSpecs.primaryRefreshRateRange.min - != configSpecs.primaryRefreshRateMin + != modeSpecs.primaryRefreshRateMin || mDisplayModeSpecs.primaryRefreshRateRange.max - != configSpecs.primaryRefreshRateMax + != modeSpecs.primaryRefreshRateMax || mDisplayModeSpecs.appRequestRefreshRateRange.min - != configSpecs.appRequestRefreshRateMin + != modeSpecs.appRequestRefreshRateMin || mDisplayModeSpecs.appRequestRefreshRateRange.max - != configSpecs.appRequestRefreshRateMax) { + != modeSpecs.appRequestRefreshRateMax) { mDisplayModeSpecsInvalid = true; sendTraversalRequestLocked(); } @@ -368,17 +370,17 @@ final class LocalDisplayAdapter extends DisplayAdapter { // For a new display, we need to initialize the default mode ID. if (mDefaultModeId == NO_DISPLAY_MODE_ID) { mDefaultModeId = activeRecord.mMode.getModeId(); - mDefaultConfigGroup = configs[activeConfigId].configGroup; + mDefaultModeGroup = displayModes[activeDisplayModeId].group; } else if (modesAdded && activeModeChanged) { Slog.d(TAG, "New display modes are added and the active mode has changed, " + "use active mode as default mode."); mDefaultModeId = activeRecord.mMode.getModeId(); - mDefaultConfigGroup = configs[activeConfigId].configGroup; - } else if (findDisplayConfigIdLocked(mDefaultModeId, mDefaultConfigGroup) < 0) { + mDefaultModeGroup = displayModes[activeDisplayModeId].group; + } else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) { Slog.w(TAG, "Default display mode no longer available, using currently" + " active mode as default."); mDefaultModeId = activeRecord.mMode.getModeId(); - mDefaultConfigGroup = configs[activeConfigId].configGroup; + mDefaultModeGroup = displayModes[activeDisplayModeId].group; } // Determine whether the display mode specs' base mode is still there. @@ -518,11 +520,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } - private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayConfig config, + private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayMode mode, List<Float> alternativeRefreshRates) { for (int i = 0; i < mSupportedModes.size(); i++) { DisplayModeRecord record = mSupportedModes.valueAt(i); - if (record.hasMatchingMode(config) + if (record.hasMatchingMode(mode) && refreshRatesEquals(alternativeRefreshRates, record.mMode.getAlternativeRefreshRates())) { return record; @@ -554,10 +556,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { @Override public DisplayDeviceInfo getDisplayDeviceInfoLocked() { if (mInfo == null) { - SurfaceControl.DisplayConfig config = mDisplayConfigs[mActiveConfigId]; + SurfaceControl.DisplayMode mode = mDisplayModes[mActiveDisplayModeId]; mInfo = new DisplayDeviceInfo(); - mInfo.width = config.width; - mInfo.height = config.height; + mInfo.width = mode.width; + mInfo.height = mode.height; mInfo.modeId = mActiveModeId; mInfo.defaultModeId = mDefaultModeId; mInfo.supportedModes = getDisplayModes(mSupportedModes); @@ -570,16 +572,16 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.supportedColorModes[i] = mSupportedColorModes.get(i); } mInfo.hdrCapabilities = mHdrCapabilities; - mInfo.appVsyncOffsetNanos = config.appVsyncOffsetNanos; - mInfo.presentationDeadlineNanos = config.presentationDeadlineNanos; + mInfo.appVsyncOffsetNanos = mode.appVsyncOffsetNanos; + mInfo.presentationDeadlineNanos = mode.presentationDeadlineNanos; mInfo.state = mState; mInfo.uniqueId = getUniqueId(); final DisplayAddress.Physical physicalAddress = DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId); mInfo.address = physicalAddress; mInfo.densityDpi = (int) (mDisplayInfo.density * 160 + 0.5f); - mInfo.xDpi = config.xDpi; - mInfo.yDpi = config.yDpi; + mInfo.xDpi = mode.xDpi; + mInfo.yDpi = mode.yDpi; mInfo.deviceProductInfo = mDisplayInfo.deviceProductInfo; // Assume that all built-in displays that have secure output (eg. HDCP) also @@ -835,16 +837,16 @@ final class LocalDisplayAdapter extends DisplayAdapter { return; } - // Find the config Id based on the desired mode specs. In case there is more than one - // config matching the mode spec, prefer the one that is in the default config group. - // For now the default config group is taken from the active config when we got the + // Find the mode Id based on the desired mode specs. In case there is more than one + // mode matching the mode spec, prefer the one that is in the default mode group. + // For now the default config mode is taken from the active mode when we got the // hotplug event for the display. In the future we might want to change the default - // config based on vendor requirements. - // Note: We prefer the default config group over the current one as this is the config + // mode based on vendor requirements. + // Note: We prefer the default mode group over the current one as this is the mode // group the vendor prefers. - int baseConfigId = findDisplayConfigIdLocked(displayModeSpecs.baseModeId, - mDefaultConfigGroup); - if (baseConfigId < 0) { + int baseModeId = findDisplayModeIdLocked(displayModeSpecs.baseModeId, + mDefaultModeGroup); + if (baseModeId < 0) { // When a display is hotplugged, it's possible for a mode to be removed that was // previously valid. Because of the way display changes are propagated through the // framework, and the caching of the display mode specs in LogicalDisplay, it's @@ -862,7 +864,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { getHandler().sendMessage(PooledLambda.obtainMessage( LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this, getDisplayTokenLocked(), - new SurfaceControl.DesiredDisplayConfigSpecs(baseConfigId, + new SurfaceControl.DesiredDisplayModeSpecs(baseModeId, mDisplayModeSpecs.allowGroupSwitching, mDisplayModeSpecs.primaryRefreshRateRange.min, mDisplayModeSpecs.primaryRefreshRateRange.max, @@ -872,13 +874,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { } private void setDesiredDisplayModeSpecsAsync(IBinder displayToken, - SurfaceControl.DesiredDisplayConfigSpecs configSpecs) { + SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { // Do not lock when calling these SurfaceControl methods because they are sync // operations that may block for a while when setting display power mode. - SurfaceControl.setDesiredDisplayConfigSpecs(displayToken, configSpecs); - final int activePhysIndex = SurfaceControl.getActiveConfig(displayToken); + SurfaceControl.setDesiredDisplayModeSpecs(displayToken, modeSpecs); + final int activeMode = SurfaceControl.getActiveDisplayMode(displayToken); synchronized (getSyncRoot()) { - if (updateActiveModeLocked(activePhysIndex)) { + if (updateActiveModeLocked(activeMode)) { updateDeviceInfoLocked(); } } @@ -889,8 +891,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { updateDeviceInfoLocked(); } - public void onActiveDisplayConfigChangedLocked(int configId) { - if (updateActiveModeLocked(configId)) { + public void onActiveDisplayModeChangedLocked(int modeId) { + if (updateActiveModeLocked(modeId)) { updateDeviceInfoLocked(); } } @@ -902,15 +904,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - public boolean updateActiveModeLocked(int activeConfigId) { - if (mActiveConfigId == activeConfigId) { + public boolean updateActiveModeLocked(int activeModeId) { + if (mActiveDisplayModeId == activeModeId) { return false; } - mActiveConfigId = activeConfigId; - mActiveModeId = findMatchingModeIdLocked(activeConfigId); + mActiveDisplayModeId = activeModeId; + mActiveModeId = findMatchingModeIdLocked(activeModeId); if (mActiveModeId == NO_DISPLAY_MODE_ID) { - Slog.w(TAG, "In unknown mode after setting allowed configs" - + ", activeConfigId=" + mActiveConfigId); + Slog.w(TAG, "In unknown mode after setting allowed modes" + + ", activeModeId=" + mActiveDisplayModeId); } return true; } @@ -990,7 +992,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId); pw.println("mDisplayModeSpecs={" + mDisplayModeSpecs + "}"); pw.println("mDisplayModeSpecsInvalid=" + mDisplayModeSpecsInvalid); - pw.println("mActiveConfigId=" + mActiveConfigId); + pw.println("mActiveDisplayModeId=" + mActiveDisplayModeId); pw.println("mActiveModeId=" + mActiveModeId); pw.println("mActiveColorMode=" + mActiveColorMode); pw.println("mDefaultModeId=" + mDefaultModeId); @@ -1002,9 +1004,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mGameContentTypeSupported=" + mGameContentTypeSupported); pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested); pw.println("mDisplayInfo=" + mDisplayInfo); - pw.println("mDisplayConfigs="); - for (int i = 0; i < mDisplayConfigs.length; i++) { - pw.println(" " + mDisplayConfigs[i]); + pw.println("mDisplayModes="); + for (int i = 0; i < mDisplayModes.length; i++) { + pw.println(" " + mDisplayModes[i]); } pw.println("mSupportedModes="); for (int i = 0; i < mSupportedModes.size(); i++) { @@ -1014,37 +1016,37 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig); } - private int findDisplayConfigIdLocked(int modeId, int configGroup) { - int matchingConfigId = SurfaceControl.DisplayConfig.INVALID_DISPLAY_CONFIG_ID; + private int findDisplayModeIdLocked(int modeId, int modeGroup) { + int matchingModeId = SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID; DisplayModeRecord record = mSupportedModes.get(modeId); if (record != null) { - for (int i = 0; i < mDisplayConfigs.length; i++) { - SurfaceControl.DisplayConfig config = mDisplayConfigs[i]; - if (record.hasMatchingMode(config)) { - if (matchingConfigId - == SurfaceControl.DisplayConfig.INVALID_DISPLAY_CONFIG_ID) { - matchingConfigId = i; + for (int i = 0; i < mDisplayModes.length; i++) { + SurfaceControl.DisplayMode mode = mDisplayModes[i]; + if (record.hasMatchingMode(mode)) { + if (matchingModeId + == SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID) { + matchingModeId = i; } - // Prefer to return a config that matches the configGroup - if (config.configGroup == configGroup) { + // Prefer to return a mode that matches the modeGroup + if (mode.group == modeGroup) { return i; } } } } - return matchingConfigId; + return matchingModeId; } - private int findMatchingModeIdLocked(int configId) { - if (configId < 0 || configId >= mDisplayConfigs.length) { - Slog.e(TAG, "Invalid display config index " + configId); + private int findMatchingModeIdLocked(int modeId) { + if (modeId < 0 || modeId >= mDisplayModes.length) { + Slog.e(TAG, "Invalid display config index " + modeId); return NO_DISPLAY_MODE_ID; } - SurfaceControl.DisplayConfig config = mDisplayConfigs[configId]; + SurfaceControl.DisplayMode mode = mDisplayModes[modeId]; for (int i = 0; i < mSupportedModes.size(); i++) { DisplayModeRecord record = mSupportedModes.valueAt(i); - if (record.hasMatchingMode(config)) { + if (record.hasMatchingMode(mode)) { return record.mMode.getModeId(); } } @@ -1091,30 +1093,29 @@ final class LocalDisplayAdapter extends DisplayAdapter { } /** - * Keeps track of a display configuration. + * Keeps track of a display mode. */ private static final class DisplayModeRecord { public final Display.Mode mMode; - DisplayModeRecord(SurfaceControl.DisplayConfig config, + DisplayModeRecord(SurfaceControl.DisplayMode mode, float[] alternativeRefreshRates) { - mMode = createMode(config.width, config.height, config.refreshRate, + mMode = createMode(mode.width, mode.height, mode.refreshRate, alternativeRefreshRates); } /** - * Returns whether the mode generated by the given DisplayConfig matches the mode + * Returns whether the mode generated by the given DisplayModes matches the mode * contained by the record modulo mode ID. * - * Note that this doesn't necessarily mean that the DisplayConfigs are identical, just + * Note that this doesn't necessarily mean that the DisplayModes are identical, just * that they generate identical modes. */ - public boolean hasMatchingMode(SurfaceControl.DisplayConfig config) { - int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate()); - int configRefreshRate = Float.floatToIntBits(config.refreshRate); - return mMode.getPhysicalWidth() == config.width - && mMode.getPhysicalHeight() == config.height - && modeRefreshRate == configRefreshRate; + public boolean hasMatchingMode(SurfaceControl.DisplayMode mode) { + return mMode.getPhysicalWidth() == mode.width + && mMode.getPhysicalHeight() == mode.height + && Float.floatToIntBits(mMode.getRefreshRate()) + == Float.floatToIntBits(mode.refreshRate); } public String toString() { @@ -1131,7 +1132,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { public interface DisplayEventListener { void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected); - void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId); + void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId); void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId, DisplayEventReceiver.FrameRateOverride[] overrides); @@ -1141,7 +1142,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final DisplayEventListener mListener; ProxyDisplayEventReceiver(Looper looper, DisplayEventListener listener) { super(looper, VSYNC_SOURCE_APP, - EVENT_REGISTRATION_CONFIG_CHANGED_FLAG + EVENT_REGISTRATION_MODE_CHANGED_FLAG | EVENT_REGISTRATION_FRAME_RATE_OVERRIDE_FLAG); mListener = listener; } @@ -1152,8 +1153,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override - public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { - mListener.onConfigChanged(timestampNanos, physicalDisplayId, configId); + public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) { + mListener.onModeChanged(timestampNanos, physicalDisplayId, modeId); } @Override @@ -1176,23 +1177,23 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override - public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { + public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) { if (DEBUG) { - Slog.d(TAG, "onConfigChanged(" + Slog.d(TAG, "onModeChanged(" + "timestampNanos=" + timestampNanos + ", physicalDisplayId=" + physicalDisplayId - + ", configId=" + configId + ")"); + + ", modeId=" + modeId + ")"); } synchronized (getSyncRoot()) { LocalDisplayDevice device = mDevices.get(physicalDisplayId); if (device == null) { if (DEBUG) { - Slog.d(TAG, "Received config change for unhandled physical display: " + Slog.d(TAG, "Received mode change for unhandled physical display: " + "physicalDisplayId=" + physicalDisplayId); } return; } - device.onActiveDisplayConfigChangedLocked(configId); + device.onActiveDisplayModeChangedLocked(modeId); } } diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java index 3e2b5ab795be..1b27572ad8de 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -345,6 +345,7 @@ public final class FontManagerService extends IFontManager.Stub { synchronized (mSerializedFontMapLock) { mSerializedFontMap = serializeFontMap; } + return; } catch (IOException | ErrnoException e) { Slog.w(TAG, "Failed to serialize updatable font map. " + "Retrying with system image fonts.", e); diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java index afc97c7fd803..dac94f6aa9d2 100644 --- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java @@ -27,6 +27,7 @@ import android.os.FileUtils; import android.system.ErrnoException; import android.system.Os; import android.text.FontConfig; +import android.util.ArrayMap; import android.util.Base64; import android.util.Slog; @@ -39,7 +40,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.security.SecureRandom; import java.time.Instant; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -116,7 +116,7 @@ final class UpdatableFontDir { * FontFileInfo}. All files in this map are validated, and have higher revision numbers than * corresponding font files in {@link #mPreinstalledFontDirs}. */ - private final Map<String, FontFileInfo> mFontFileInfoMap = new HashMap<>(); + private final ArrayMap<String, FontFileInfo> mFontFileInfoMap = new ArrayMap<>(); UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser, FsverityUtil fsverityUtil) { @@ -205,7 +205,7 @@ final class UpdatableFontDir { */ public void update(List<FontUpdateRequest> requests) throws SystemFontException { // Backup the mapping for rollback. - HashMap<String, FontFileInfo> backupMap = new HashMap<>(mFontFileInfoMap); + ArrayMap<String, FontFileInfo> backupMap = new ArrayMap<>(mFontFileInfoMap); long backupLastModifiedDate = mLastModifiedDate; boolean success = false; try { @@ -464,7 +464,7 @@ final class UpdatableFontDir { } Map<String, File> getFontFileMap() { - Map<String, File> map = new HashMap<>(); + Map<String, File> map = new ArrayMap<>(); for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) { map.put(entry.getKey(), entry.getValue().getFile()); } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 2e4200c1f7d9..4e974112a5c3 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -57,6 +57,7 @@ import android.os.CombinedVibrationEffect; import android.os.Environment; import android.os.Handler; import android.os.IBinder; +import android.os.IVibratorStateListener; import android.os.InputEventInjectionResult; import android.os.InputEventInjectionSync; import android.os.LocaleList; @@ -64,6 +65,7 @@ import android.os.Looper; import android.os.Message; import android.os.MessageQueue; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; @@ -77,6 +79,7 @@ import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.view.Display; import android.view.IInputFilter; import android.view.IInputFilterHost; @@ -100,6 +103,7 @@ import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; +import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.DisplayThread; import com.android.server.LocalServices; @@ -221,13 +225,16 @@ public class InputManagerService extends IInputManager.Stub private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>(); private int mNextVibratorTokenValue; + // List of currently registered vibrator state changed listeners by device id. + @GuardedBy("mVibratorLock") + private final SparseArray<RemoteCallbackList<IVibratorStateListener>> mVibratorStateListeners = + new SparseArray<RemoteCallbackList<IVibratorStateListener>>(); + // List of vibrator states by device id. + @GuardedBy("mVibratorLock") + private final SparseBooleanArray mIsVibrating = new SparseBooleanArray(); + // State for lid switch - // Lock for the lid switch state. Held when triggering callbacks to guarantee lid switch events - // are delivered in order. For ex, when a new lid switch callback is registered the lock is held - // while the callback is processing the initial lid switch event which guarantees that any - // events that occur at the same time are delivered after the callback has returned. private final Object mLidSwitchLock = new Object(); - @GuardedBy("mLidSwitchLock") private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>(); // State for the currently installed input filter. @@ -375,6 +382,9 @@ public class InputManagerService extends IInputManager.Stub public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER; public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE; + /** Indicates an open state for the lid switch. */ + public static final int SW_STATE_LID_OPEN = 0; + /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */ final boolean mUseDevInputEventForAudioJack; @@ -410,18 +420,13 @@ public class InputManagerService extends IInputManager.Stub } void registerLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) { + boolean lidOpen; synchronized (mLidSwitchLock) { mLidSwitchCallbacks.add(callback); - - // Skip triggering the initial callback if the system is not yet ready as the switch - // state will be reported as KEY_STATE_UNKNOWN. The callback will be triggered in - // systemRunning(). - if (mSystemReady) { - boolean lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID) - == KEY_STATE_UP; - callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen); - } + lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID) + == SW_STATE_LID_OPEN; } + callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen); } void unregisterLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) { @@ -469,18 +474,7 @@ public class InputManagerService extends IInputManager.Stub } mNotificationManager = (NotificationManager)mContext.getSystemService( Context.NOTIFICATION_SERVICE); - - synchronized (mLidSwitchLock) { - mSystemReady = true; - - // Send the initial lid switch state to any callback registered before the system was - // ready. - int switchState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID); - for (int i = 0; i < mLidSwitchCallbacks.size(); i++) { - LidSwitchCallback callback = mLidSwitchCallbacks.get(i); - callback.notifyLidSwitchChanged(0 /* whenNanos */, switchState == KEY_STATE_UP); - } - } + mSystemReady = true; IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -2008,6 +2002,92 @@ public class InputManagerService extends IInputManager.Stub } } + // Native callback. + private void notifyVibratorState(int deviceId, boolean isOn) { + if (DEBUG) { + Slog.d(TAG, "notifyVibratorState: deviceId=" + deviceId + " isOn=" + isOn); + } + synchronized (mVibratorLock) { + mIsVibrating.put(deviceId, isOn); + notifyVibratorStateListenersLocked(deviceId); + } + } + + @GuardedBy("mVibratorLock") + private void notifyVibratorStateListenersLocked(int deviceId) { + if (!mVibratorStateListeners.contains(deviceId)) { + if (DEBUG) { + Slog.v(TAG, "Device " + deviceId + " doesn't have vibrator state listener."); + } + return; + } + RemoteCallbackList<IVibratorStateListener> listeners = + mVibratorStateListeners.get(deviceId); + final int length = listeners.beginBroadcast(); + try { + for (int i = 0; i < length; i++) { + notifyVibratorStateListenerLocked(deviceId, listeners.getBroadcastItem(i)); + } + } finally { + listeners.finishBroadcast(); + } + } + + @GuardedBy("mVibratorLock") + private void notifyVibratorStateListenerLocked(int deviceId, IVibratorStateListener listener) { + try { + listener.onVibrating(mIsVibrating.get(deviceId)); + } catch (RemoteException | RuntimeException e) { + Slog.e(TAG, "Vibrator state listener failed to call", e); + } + } + + @Override // Binder call + public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) { + Preconditions.checkNotNull(listener, "listener must not be null"); + + RemoteCallbackList<IVibratorStateListener> listeners; + synchronized (mVibratorLock) { + if (!mVibratorStateListeners.contains(deviceId)) { + listeners = new RemoteCallbackList<>(); + mVibratorStateListeners.put(deviceId, listeners); + } else { + listeners = mVibratorStateListeners.get(deviceId); + } + + final long token = Binder.clearCallingIdentity(); + try { + if (!listeners.register(listener)) { + Slog.e(TAG, "Could not register vibrator state listener " + listener); + return false; + } + // Notify its callback after new client registered. + notifyVibratorStateListenerLocked(deviceId, listener); + return true; + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + @Override // Binder call + public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) { + synchronized (mVibratorLock) { + final long token = Binder.clearCallingIdentity(); + try { + if (!mVibratorStateListeners.contains(deviceId)) { + Slog.w(TAG, "Vibrator state listener " + deviceId + " doesn't exist"); + return false; + } + RemoteCallbackList<IVibratorStateListener> listeners = + mVibratorStateListeners.get(deviceId); + return listeners.unregister(listener); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + // Binder call @Override public int getBatteryStatus(int deviceId) { @@ -2251,13 +2331,14 @@ public class InputManagerService extends IInputManager.Stub if ((switchMask & SW_LID_BIT) != 0) { final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0); + + ArrayList<LidSwitchCallback> callbacksCopy; synchronized (mLidSwitchLock) { - if (mSystemReady) { - for (int i = 0; i < mLidSwitchCallbacks.size(); i++) { - LidSwitchCallback callbacks = mLidSwitchCallbacks.get(i); - callbacks.notifyLidSwitchChanged(whenNanos, lidOpen); - } - } + callbacksCopy = new ArrayList<>(mLidSwitchCallbacks); + } + for (int i = 0; i < callbacksCopy.size(); i++) { + LidSwitchCallback callbacks = callbacksCopy.get(i); + callbacks.notifyLidSwitchChanged(whenNanos, lidOpen); } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index e5b53501d6e3..1a4c8b7d6571 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -110,7 +110,6 @@ import android.os.ServiceManager; import android.os.ShellCallback; import android.os.ShellCommand; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; @@ -300,45 +299,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private static final String ACTION_SHOW_INPUT_METHOD_PICKER = "com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER"; - /** - * Debug flag for overriding runtime {@link SystemProperties}. - */ - @AnyThread - private static final class DebugFlag { - private static final Object LOCK = new Object(); - private final String mKey; - private final boolean mDefaultValue; - @GuardedBy("LOCK") - private boolean mValue; - - public DebugFlag(String key, boolean defaultValue) { - mKey = key; - mDefaultValue = defaultValue; - mValue = SystemProperties.getBoolean(key, defaultValue); - } - - void refresh() { - synchronized (LOCK) { - mValue = SystemProperties.getBoolean(mKey, mDefaultValue); - } - } - - boolean value() { - synchronized (LOCK) { - return mValue; - } - } - } - - /** - * Debug flags that can be overridden using "adb shell setprop <key>" - * Note: These flags are cached. To refresh, run "adb shell ime refresh_debug_properties". - */ - private static final class DebugFlags { - static final DebugFlag FLAG_OPTIMIZE_START_INPUT = - new DebugFlag("debug.optimize_startinput", false); - } - @UserIdInt private int mLastSwitchUserId; @@ -2586,7 +2546,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + mCurTokenDisplayId); } mIWindowManager.addWindowToken(mCurToken, LayoutParams.TYPE_INPUT_METHOD, - mCurTokenDisplayId); + mCurTokenDisplayId, null /* options */); } catch (RemoteException e) { } return new InputBindResult( @@ -3687,12 +3647,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute, startInputFlags, startInputReason); - } else if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value() - || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) { + } else { res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute, startInputFlags, startInputReason); - } else { - res = InputBindResult.NO_EDITOR; } } else { res = InputBindResult.NULL_EDITOR_INFO; @@ -5270,6 +5227,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + boolean asProto = false; for (int argIndex = 0; argIndex < args.length; argIndex++) { if (args[argIndex].equals(PROTO_ARG)) { @@ -5292,8 +5251,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) { - if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - if (useProto) { final ProtoOutputStream proto = new ProtoOutputStream(fd); dumpDebug(proto, InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE); @@ -5467,10 +5424,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @ShellCommandResult private int onCommandWithSystemIdentity(@Nullable String cmd) { - if ("refresh_debug_properties".equals(cmd)) { - return refreshDebugProperties(); - } - if ("get-last-switch-user-id".equals(cmd)) { return mService.getLastSwitchUserId(this); } @@ -5505,13 +5458,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @BinderThread - @ShellCommandResult - private int refreshDebugProperties() { - DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh(); - return ShellCommandResult.SUCCESS; - } - - @BinderThread @Override public void onHelp() { try (PrintWriter pw = getOutPrintWriter()) { diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 6fec9063ba94..1dd3d4190e8a 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1309,7 +1309,8 @@ public final class MultiClientInputMethodManagerService { final Binder token = new Binder(); Binder.withCleanCallingIdentity( PooledLambda.obtainRunnable(WindowManagerInternal::addWindowToken, - mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId)); + mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId, + null /* options */)); mPerUserData.mDisplayIdToImeWindowTokenMap.add(new TokenInfo(token, displayId)); return token; } diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 142f64f0a510..ea759bf500dd 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -66,6 +66,7 @@ import android.location.LocationManagerInternal; import android.location.LocationProvider; import android.location.LocationRequest; import android.location.LocationTime; +import android.location.provider.IProviderRequestListener; import android.location.provider.ProviderProperties; import android.location.util.identity.CallerIdentity; import android.os.Binder; @@ -882,6 +883,20 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override + public void addProviderRequestListener(IProviderRequestListener listener) { + for (LocationProviderManager manager : mProviderManagers) { + manager.addProviderRequestListener(listener); + } + } + + @Override + public void removeProviderRequestListener(IProviderRequestListener listener) { + for (LocationProviderManager manager : mProviderManagers) { + manager.removeProviderRequestListener(listener); + } + } + + @Override public void injectGnssMeasurementCorrections(GnssMeasurementCorrections corrections) { if (mGnssManagerService != null) { mGnssManagerService.injectGnssMeasurementCorrections(corrections); diff --git a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java index fe51d7408153..b5746bbf310a 100644 --- a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java +++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.utils.eventlog; +package com.android.server.location.eventlog; import android.os.SystemClock; import android.util.TimeUtils; diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/injector/LocationEventLog.java index b8b54b3a42e7..8d73518bced1 100644 --- a/services/core/java/com/android/server/location/injector/LocationEventLog.java +++ b/services/core/java/com/android/server/location/injector/LocationEventLog.java @@ -31,7 +31,7 @@ import android.location.util.identity.CallerIdentity; import android.os.Build; import android.os.PowerManager.LocationPowerSaveMode; -import com.android.server.utils.eventlog.LocalEventLog; +import com.android.server.location.eventlog.LocalEventLog; /** In memory event log for location events. */ public class LocationEventLog extends LocalEventLog { diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index 221d4fb40ef9..48a012e57a02 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -55,6 +55,7 @@ import android.location.LocationManagerInternal; import android.location.LocationManagerInternal.ProviderEnabledListener; import android.location.LocationRequest; import android.location.LocationResult; +import android.location.provider.IProviderRequestListener; import android.location.provider.ProviderProperties; import android.location.provider.ProviderRequest; import android.location.util.identity.CallerIdentity; @@ -117,6 +118,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; /** @@ -1219,6 +1221,8 @@ public class LocationProviderManager extends @GuardedBy("mLock") private final ArrayList<ProviderEnabledListener> mEnabledListeners; + private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners; + protected final LocationManagerInternal mLocationManagerInternal; protected final SettingsHelper mSettingsHelper; protected final UserInfoHelper mUserHelper; @@ -1279,6 +1283,7 @@ public class LocationProviderManager extends mLastLocations = new SparseArray<>(2); mEnabledListeners = new ArrayList<>(); + mProviderRequestListeners = new CopyOnWriteArrayList<>(); mLocationManagerInternal = Objects.requireNonNull( LocalServices.getService(LocationManagerInternal.class)); @@ -1344,6 +1349,7 @@ public class LocationProviderManager extends // if external entities are registering listeners it's their responsibility to // unregister them before stopManager() is called Preconditions.checkState(mEnabledListeners.isEmpty()); + mProviderRequestListeners.clear(); mEnabled.clear(); mLastLocations.clear(); @@ -1404,6 +1410,16 @@ public class LocationProviderManager extends } } + /** Add a {@link IProviderRequestListener}. */ + public void addProviderRequestListener(IProviderRequestListener listener) { + mProviderRequestListeners.add(listener); + } + + /** Remove a {@link IProviderRequestListener}. */ + public void removeProviderRequestListener(IProviderRequestListener listener) { + mProviderRequestListeners.remove(listener); + } + public void setRealProvider(@Nullable AbstractLocationProvider provider) { synchronized (mLock) { Preconditions.checkState(mState != STATE_STOPPED); @@ -1873,8 +1889,7 @@ public class LocationProviderManager extends Preconditions.checkState(delayMs >= 0 && delayMs <= newRequest.getIntervalMillis()); if (delayMs < MIN_REQUEST_DELAY_MS) { - mLocationEventLog.logProviderUpdateRequest(mName, newRequest); - mProvider.getController().setRequest(newRequest); + setProviderRequest(newRequest); } else { if (D) { Log.d(TAG, mName + " provider delaying request update " + newRequest + " by " @@ -1886,8 +1901,7 @@ public class LocationProviderManager extends public void onAlarm() { synchronized (mLock) { if (mDelayedRegister == this) { - mLocationEventLog.logProviderUpdateRequest(mName, newRequest); - mProvider.getController().setRequest(newRequest); + setProviderRequest(newRequest); mDelayedRegister = null; } } @@ -1906,8 +1920,23 @@ public class LocationProviderManager extends Preconditions.checkState(Thread.holdsLock(mLock)); } - mLocationEventLog.logProviderUpdateRequest(mName, ProviderRequest.EMPTY_REQUEST); - mProvider.getController().setRequest(ProviderRequest.EMPTY_REQUEST); + setProviderRequest(ProviderRequest.EMPTY_REQUEST); + } + + @GuardedBy("mLock") + private void setProviderRequest(ProviderRequest request) { + mLocationEventLog.logProviderUpdateRequest(mName, request); + mProvider.getController().setRequest(request); + + FgThread.getHandler().post(() -> { + for (IProviderRequestListener listener : mProviderRequestListeners) { + try { + listener.onProviderRequestChanged(mName, request); + } catch (RemoteException e) { + mProviderRequestListeners.remove(listener); + } + } + }); } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java index e2e5046d98bf..87e170ad12df 100644 --- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java +++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java @@ -18,6 +18,7 @@ package com.android.server.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.app.BroadcastOptions; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -58,6 +59,16 @@ final class MediaButtonReceiverHolder { private static final String TAG = "PendingIntentHolder"; private static final boolean DEBUG_KEY_EVENT = MediaSessionService.DEBUG_KEY_EVENT; private static final String COMPONENT_NAME_USER_ID_DELIM = ","; + // Filter apps regardless of the phone's locked/unlocked state. + private static final int PACKAGE_MANAGER_COMMON_FLAGS = + PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + + /** + * Denotes the duration during which a media button receiver will be exempted from + * FGS-from-BG restriction and so will be allowed to start an FGS even if it is in the + * background state while it receives a media key event. + */ + private static final long FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS = 10_000; private final int mUserId; private final PendingIntent mPendingIntent; @@ -105,40 +116,22 @@ final class MediaButtonReceiverHolder { * @return Can be {@code null} if pending intent was null. */ public static MediaButtonReceiverHolder create(Context context, int userId, - PendingIntent pendingIntent) { + PendingIntent pendingIntent, String sessionPackageName) { if (pendingIntent == null) { return null; } - ComponentName componentName = (pendingIntent != null && pendingIntent.getIntent() != null) - ? pendingIntent.getIntent().getComponent() : null; + int componentType = getComponentType(pendingIntent); + ComponentName componentName = getComponentName(pendingIntent, componentType); if (componentName != null) { - // Explicit intent, where component name is in the PendingIntent. return new MediaButtonReceiverHolder(userId, pendingIntent, componentName, - getComponentType(context, componentName)); - } - - // Implicit intent, where component name isn't in the PendingIntent. Try resolve. - PackageManager pm = context.getPackageManager(); - Intent intent = pendingIntent.getIntent(); - if ((componentName = resolveImplicitServiceIntent(pm, intent)) != null) { - return new MediaButtonReceiverHolder( - userId, pendingIntent, componentName, COMPONENT_TYPE_SERVICE); - } else if ((componentName = resolveManifestDeclaredBroadcastReceiverIntent(pm, intent)) - != null) { - return new MediaButtonReceiverHolder( - userId, pendingIntent, componentName, COMPONENT_TYPE_BROADCAST); - } else if ((componentName = resolveImplicitActivityIntent(pm, intent)) != null) { - return new MediaButtonReceiverHolder( - userId, pendingIntent, componentName, COMPONENT_TYPE_ACTIVITY); + componentType); } // Failed to resolve target component for the pending intent. It's unlikely to be usable. - // However, the pending intent would be still used, just to follow the legacy behavior. + // However, the pending intent would be still used, so setting the package name to the + // package name of the session that set this pending intent. Log.w(TAG, "Unresolvable implicit intent is set, pi=" + pendingIntent); - String packageName = (pendingIntent != null && pendingIntent.getIntent() != null) - ? pendingIntent.getIntent().getPackage() : null; - return new MediaButtonReceiverHolder(userId, pendingIntent, - packageName != null ? packageName : ""); + return new MediaButtonReceiverHolder(userId, pendingIntent, sessionPackageName); } public static MediaButtonReceiverHolder create(int userId, ComponentName broadcastReceiver) { @@ -201,6 +194,9 @@ final class MediaButtonReceiverHolder { // TODO: Find a way to also send PID/UID in secure way. mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackageName); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setTemporaryAppWhitelistDuration( + FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS); if (mPendingIntent != null) { if (DEBUG_KEY_EVENT) { Log.d(TAG, "Sending " + keyEvent + " to the last known PendingIntent " @@ -208,7 +204,8 @@ final class MediaButtonReceiverHolder { } try { mPendingIntent.send( - context, resultCode, mediaButtonIntent, onFinishedListener, handler); + context, resultCode, mediaButtonIntent, onFinishedListener, handler, + /* requiredPermission= */ null, options.toBundle()); } catch (PendingIntent.CanceledException e) { Log.w(TAG, "Error sending key event to media button receiver " + mPendingIntent, e); return false; @@ -231,7 +228,8 @@ final class MediaButtonReceiverHolder { break; default: // Legacy behavior for other cases. - context.sendBroadcastAsUser(mediaButtonIntent, userHandle); + context.sendBroadcastAsUser(mediaButtonIntent, userHandle, + /* receiverPermission= */ null, options.toBundle()); } } catch (Exception e) { Log.w(TAG, "Error sending media button to the restored intent " @@ -269,6 +267,18 @@ final class MediaButtonReceiverHolder { String.valueOf(mComponentType)); } + @ComponentType + private static int getComponentType(PendingIntent pendingIntent) { + if (pendingIntent.isBroadcast()) { + return COMPONENT_TYPE_BROADCAST; + } else if (pendingIntent.isActivity()) { + return COMPONENT_TYPE_ACTIVITY; + } else if (pendingIntent.isForegroundService() || pendingIntent.isService()) { + return COMPONENT_TYPE_SERVICE; + } + return COMPONENT_TYPE_INVALID; + } + /** * Gets the type of the component * @@ -284,9 +294,7 @@ final class MediaButtonReceiverHolder { PackageManager pm = context.getPackageManager(); try { ActivityInfo activityInfo = pm.getActivityInfo(componentName, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.GET_ACTIVITIES); + PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_ACTIVITIES); if (activityInfo != null) { return COMPONENT_TYPE_ACTIVITY; } @@ -294,9 +302,7 @@ final class MediaButtonReceiverHolder { } try { ServiceInfo serviceInfo = pm.getServiceInfo(componentName, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.GET_SERVICES); + PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_SERVICES); if (serviceInfo != null) { return COMPONENT_TYPE_SERVICE; } @@ -306,40 +312,29 @@ final class MediaButtonReceiverHolder { return COMPONENT_TYPE_BROADCAST; } - private static ComponentName resolveImplicitServiceIntent(PackageManager pm, Intent intent) { - // Flag explanations. - // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE: - // filter apps regardless of the phone's locked/unlocked state. - // - GET_SERVICES: Return service - return createComponentName(pm.resolveService(intent, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.GET_SERVICES)); - } - - private static ComponentName resolveManifestDeclaredBroadcastReceiverIntent( - PackageManager pm, Intent intent) { - // Flag explanations. - // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE: - // filter apps regardless of the phone's locked/unlocked state. - List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(intent, - PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); - return (resolveInfos != null && !resolveInfos.isEmpty()) - ? createComponentName(resolveInfos.get(0)) : null; - } - - private static ComponentName resolveImplicitActivityIntent(PackageManager pm, Intent intent) { - // Flag explanations. - // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE: - // Filter apps regardless of the phone's locked/unlocked state. - // - MATCH_DEFAULT_ONLY: - // Implicit intent receiver should be set as default. Only needed for activity. - // - GET_ACTIVITIES: Return activity - return createComponentName(pm.resolveActivity(intent, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_DEFAULT_ONLY - | PackageManager.GET_ACTIVITIES)); + private static ComponentName getComponentName(PendingIntent pendingIntent, int componentType) { + List<ResolveInfo> resolveInfos = null; + switch (componentType) { + case COMPONENT_TYPE_ACTIVITY: + resolveInfos = pendingIntent.queryIntentComponents( + PACKAGE_MANAGER_COMMON_FLAGS + | PackageManager.MATCH_DEFAULT_ONLY /* Implicit intent receiver + should be set as default. Only needed for activity. */ + | PackageManager.GET_ACTIVITIES); + break; + case COMPONENT_TYPE_SERVICE: + resolveInfos = pendingIntent.queryIntentComponents( + PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_SERVICES); + break; + case COMPONENT_TYPE_BROADCAST: + resolveInfos = pendingIntent.queryIntentComponents( + PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_RECEIVERS); + break; + } + if (resolveInfos != null && !resolveInfos.isEmpty()) { + return createComponentName(resolveInfos.get(0)); + } + return null; } private static ComponentName createComponentName(ResolveInfo resolveInfo) { diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java index 63618eef250a..7ef4924879bf 100644 --- a/services/core/java/com/android/server/media/MediaKeyDispatcher.java +++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java @@ -100,9 +100,12 @@ public abstract class MediaKeyDispatcher { /** * Implement this to customize the logic for which MediaButtonReceiver should consume a * dispatched key event. - * - * Note: This pending intent will have lower priority over the {@link MediaSession.Token} + * <p> + * This pending intent will have lower priority over the {@link MediaSession.Token} * returned from {@link #getMediaSession(KeyEvent, int, boolean)}. + * <p> + * Use a pending intent with an explicit intent; setting a pending intent with an implicit + * intent that cannot be resolved to a certain component name will fail. * * @return a {@link PendingIntent} instance that should receive the dispatched key event. */ diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index ae58d4c40622..74111be419b5 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -843,7 +843,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } @Override - public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException { + public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName) + throws RemoteException { final long token = Binder.clearCallingIdentity(); try { if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) @@ -851,7 +852,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR return; } mMediaButtonReceiverHolder = - MediaButtonReceiverHolder.create(mContext, mUserId, pi); + MediaButtonReceiverHolder.create(mContext, mUserId, pi, sessionPackageName); mService.onMediaButtonReceiverChanged(MediaSessionRecord.this); } finally { Binder.restoreCallingIdentity(token); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index c0381e4b1715..6c1d3991c4e9 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -2124,7 +2124,8 @@ public class MediaSessionService extends SystemService implements Monitor { uid, asSystemService); if (pi != null) { mediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mContext, - mCurrentFullUserRecord.mFullUserId, pi); + mCurrentFullUserRecord.mFullUserId, pi, + /* sessionPackageName= */ ""); } } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index f92f3dcd77ef..39ed7e8b1e1a 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -16,8 +16,6 @@ package com.android.server.net; -import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal; - import android.annotation.NonNull; import android.net.Network; import android.net.NetworkTemplate; @@ -39,28 +37,6 @@ public abstract class NetworkPolicyManagerInternal { public abstract void resetUserState(int userId); /** - * Figure out if networking is blocked for a given set of conditions. - * - * This is used by ConnectivityService via passing stale copies of conditions, so it must not - * take any locks. - * - * @param uid The target uid. - * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService. - * @param isNetworkMetered True if the network is metered. - * @param isBackgroundRestricted True if data saver is enabled. - * - * @return true if networking is blocked for the UID under the specified conditions. - */ - public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered, - boolean isBackgroundRestricted) { - // Log of invoking internal function is disabled because it will be called very - // frequently. And metrics are unlikely needed on this method because the callers are - // external and this method doesn't take any locks or perform expensive operations. - return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered, - isBackgroundRestricted, null); - } - - /** * Informs that an appId has been added or removed from the temp-powersave-allowlist so that * that network rules for that appId can be updated. * diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index ca21640feb8f..b99a55271943 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -5402,6 +5402,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override + public boolean checkUidNetworkingBlocked(int uid, int uidRules, + boolean isNetworkMetered, boolean isBackgroundRestricted) { + mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG); + // Log of invoking this function is disabled because it will be called very frequently. And + // metrics are unlikely needed on this method because the callers are external and this + // method doesn't take any locks or perform expensive operations. + return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered, + isBackgroundRestricted, null); + } + + @Override public boolean isUidRestrictedOnMeteredNetworks(int uid) { mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG); final int uidRules; @@ -5410,9 +5421,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); isBackgroundRestricted = mRestrictBackground; } - //TODO(b/177490332): The logic here might not be correct because it doesn't consider - // RULE_REJECT_METERED condition. And it could be replaced by - // isUidNetworkingBlockedInternal(). + // TODO(b/177490332): The logic here might not be correct because it doesn't consider + // RULE_REJECT_METERED condition. And it could be replaced by + // isUidNetworkingBlockedInternal(). return isBackgroundRestricted && !hasRule(uidRules, RULE_ALLOW_METERED) && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6843733eea9f..f7f1865f757e 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3041,7 +3041,8 @@ public class NotificationManagerService extends SystemService { } Binder windowToken = new Binder(); - mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId); + mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId, + null /* options */); record = getToastRecord(callingUid, callingPid, pkg, isSystemToast, token, text, callback, duration, windowToken, displayId, textCallback); mToastQueue.add(record); @@ -10103,7 +10104,9 @@ public class NotificationManagerService extends SystemService { } BackgroundThread.getHandler().post(() -> { - if (info.isSystem || hasCompanionDevice(info)) { + if (info.isSystem + || hasCompanionDevice(info) + || mAssistants.isServiceTokenValidLocked(info.service)) { notifyNotificationChannelChanged( info, pkg, user, channel, modificationType); } diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java new file mode 100644 index 000000000000..a83edb75badb --- /dev/null +++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.os; + +import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; +import static android.os.Process.THREAD_PRIORITY_BACKGROUND; + +import android.annotation.AppIdInt; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.Context; +import android.os.FileObserver; +import android.os.Handler; +import android.os.ParcelFileDescriptor; +import android.os.UserHandle; +import android.util.Slog; +import android.util.SparseArray; +import android.util.proto.ProtoInputStream; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.BootReceiver; +import com.android.server.ServiceThread; +import com.android.server.os.TombstoneProtos.Tombstone; + +import libcore.io.IoUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Optional; + +/** + * A class to manage native tombstones. + */ +public final class NativeTombstoneManager { + private static final String TAG = NativeTombstoneManager.class.getSimpleName(); + + private static final File TOMBSTONE_DIR = new File("/data/tombstones"); + + private final Context mContext; + private final Handler mHandler; + private final TombstoneWatcher mWatcher; + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private final SparseArray<TombstoneFile> mTombstones; + + NativeTombstoneManager(Context context) { + mTombstones = new SparseArray<TombstoneFile>(); + mContext = context; + + final ServiceThread thread = new ServiceThread(TAG + ":tombstoneWatcher", + THREAD_PRIORITY_BACKGROUND, true /* allowIo */); + thread.start(); + mHandler = thread.getThreadHandler(); + + mWatcher = new TombstoneWatcher(); + mWatcher.startWatching(); + } + + void onSystemReady() { + // Scan existing tombstones. + mHandler.post(() -> { + final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles(); + for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) { + if (tombstoneFiles[i].isFile()) { + handleTombstone(tombstoneFiles[i]); + } + } + }); + } + + private void handleTombstone(File path) { + final String filename = path.getName(); + if (!filename.startsWith("tombstone_")) { + return; + } + + if (filename.endsWith(".pb")) { + handleProtoTombstone(path); + } else { + BootReceiver.addTombstoneToDropBox(mContext, path); + } + } + + private void handleProtoTombstone(File path) { + final String filename = path.getName(); + if (!filename.endsWith(".pb")) { + Slog.w(TAG, "unexpected tombstone name: " + path); + return; + } + + final String suffix = filename.substring("tombstone_".length()); + final String numberStr = suffix.substring(0, suffix.length() - 3); + + int number; + try { + number = Integer.parseInt(numberStr); + if (number < 0 || number > 99) { + Slog.w(TAG, "unexpected tombstone name: " + path); + return; + } + } catch (NumberFormatException ex) { + Slog.w(TAG, "unexpected tombstone name: " + path); + return; + } + + ParcelFileDescriptor pfd; + try { + pfd = ParcelFileDescriptor.open(path, MODE_READ_WRITE); + } catch (FileNotFoundException ex) { + Slog.w(TAG, "failed to open " + path, ex); + return; + } + + final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd); + if (!parsedTombstone.isPresent()) { + IoUtils.closeQuietly(pfd); + return; + } + + synchronized (mLock) { + TombstoneFile previous = mTombstones.get(number); + if (previous != null) { + previous.dispose(); + } + + mTombstones.put(number, parsedTombstone.get()); + } + } + + static class TombstoneFile { + final ParcelFileDescriptor mPfd; + + final @UserIdInt int mUserId; + final @AppIdInt int mAppId; + + boolean mPurged = false; + + TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) { + mPfd = pfd; + mUserId = userId; + mAppId = appId; + } + + public boolean matches(Optional<Integer> userId, Optional<Integer> appId) { + if (mPurged) { + return false; + } + + if (userId.isPresent() && userId.get() != mUserId) { + return false; + } + + if (appId.isPresent() && appId.get() != mAppId) { + return false; + } + + return true; + } + + public void dispose() { + IoUtils.closeQuietly(mPfd); + } + + static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) { + final FileInputStream is = new FileInputStream(pfd.getFileDescriptor()); + final ProtoInputStream stream = new ProtoInputStream(is); + + int uid = 0; + String selinuxLabel = ""; + + try { + while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (stream.getFieldNumber()) { + case (int) Tombstone.UID: + uid = stream.readInt(Tombstone.UID); + break; + + case (int) Tombstone.SELINUX_LABEL: + selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL); + break; + + default: + break; + } + } + } catch (IOException ex) { + Slog.e(TAG, "Failed to parse tombstone", ex); + return Optional.empty(); + } + + if (!UserHandle.isApp(uid)) { + Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring"); + return Optional.empty(); + } + + final int userId = UserHandle.getUserId(uid); + final int appId = UserHandle.getAppId(uid); + + if (!selinuxLabel.startsWith("u:r:untrusted_app")) { + Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring"); + return Optional.empty(); + } + + return Optional.of(new TombstoneFile(pfd, userId, appId)); + } + } + + class TombstoneWatcher extends FileObserver { + TombstoneWatcher() { + // Tombstones can be created either by linking an O_TMPFILE temporary file (CREATE), + // or by moving a named temporary file in the same directory on kernels where O_TMPFILE + // isn't supported (MOVED_TO). + super(TOMBSTONE_DIR, FileObserver.CREATE | FileObserver.MOVED_TO); + } + + @Override + public void onEvent(int event, @Nullable String path) { + mHandler.post(() -> { + handleTombstone(new File(TOMBSTONE_DIR, path)); + }); + } + } +} diff --git a/services/core/java/com/android/server/os/NativeTombstoneManagerService.java b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java new file mode 100644 index 000000000000..cb3c7ff0c07d --- /dev/null +++ b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.os; + +import android.content.Context; + +import com.android.server.LocalServices; +import com.android.server.SystemService; + +/** + * Service that tracks and manages native tombstones. + * + * @hide + */ +public class NativeTombstoneManagerService extends SystemService { + private static final String TAG = "NativeTombstoneManagerService"; + + private NativeTombstoneManager mManager; + + public NativeTombstoneManagerService(Context context) { + super(context); + } + + @Override + public void onStart() { + mManager = new NativeTombstoneManager(getContext()); + LocalServices.addService(NativeTombstoneManager.class, mManager); + } + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { + mManager.onSystemReady(); + } + } +} diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java index 5373f996d7f8..66ea55401cef 100644 --- a/services/core/java/com/android/server/pm/ApkChecksums.java +++ b/services/core/java/com/android/server/pm/ApkChecksums.java @@ -23,7 +23,6 @@ import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256; import static android.content.pm.Checksum.TYPE_WHOLE_SHA1; import static android.content.pm.Checksum.TYPE_WHOLE_SHA256; import static android.content.pm.Checksum.TYPE_WHOLE_SHA512; -import static android.content.pm.PackageManager.EXTRA_CHECKSUMS; import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512; @@ -32,15 +31,15 @@ import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKE import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.content.Intent; -import android.content.IntentSender; import android.content.pm.ApkChecksum; import android.content.pm.Checksum; +import android.content.pm.IOnChecksumsReadyListener; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.pm.Signature; import android.content.pm.parsing.ApkLiteParseUtils; import android.os.Handler; +import android.os.RemoteException; import android.os.SystemClock; import android.os.incremental.IncrementalManager; import android.os.incremental.IncrementalStorage; @@ -294,21 +293,21 @@ public class ApkChecksums { /** * Fetch or calculate checksums for the collection of files. * - * @param filesToChecksum split name, null for base and File to fetch checksums for - * @param optional mask to fetch readily available checksums - * @param required mask to forcefully calculate if not available - * @param installerPackageName package name of the installer of the packages - * @param trustedInstallers array of certificate to trust, two specific cases: - * null - trust anybody, - * [] - trust nobody. - * @param statusReceiver to receive the resulting checksums + * @param filesToChecksum split name, null for base and File to fetch checksums for + * @param optional mask to fetch readily available checksums + * @param required mask to forcefully calculate if not available + * @param installerPackageName package name of the installer of the packages + * @param trustedInstallers array of certificate to trust, two specific cases: + * null - trust anybody, + * [] - trust nobody. + * @param onChecksumsReadyListener to receive the resulting checksums */ public static void getChecksums(List<Pair<String, File>> filesToChecksum, @Checksum.Type int optional, @Checksum.Type int required, @Nullable String installerPackageName, @Nullable Certificate[] trustedInstallers, - @NonNull IntentSender statusReceiver, + @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, @NonNull Injector injector) { List<Map<Integer, ApkChecksum>> result = new ArrayList<>(filesToChecksum.size()); for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { @@ -326,14 +325,14 @@ public class ApkChecksums { } long startTime = SystemClock.uptimeMillis(); - processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector, - startTime); + processRequiredChecksums(filesToChecksum, result, required, onChecksumsReadyListener, + injector, startTime); } private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum, List<Map<Integer, ApkChecksum>> result, @Checksum.Type int required, - @NonNull IntentSender statusReceiver, + @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, @NonNull Injector injector, long startTime) { final boolean timeout = @@ -350,7 +349,7 @@ public class ApkChecksums { // Not ready, come back later. injector.getHandler().postDelayed(() -> { processRequiredChecksums(filesToChecksum, result, required, - statusReceiver, injector, startTime); + onChecksumsReadyListener, injector, startTime); }, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS); return; } @@ -363,13 +362,9 @@ public class ApkChecksums { } } - final Intent intent = new Intent(); - intent.putExtra(EXTRA_CHECKSUMS, - allChecksums.toArray(new ApkChecksum[allChecksums.size()])); - try { - statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null); - } catch (IntentSender.SendIntentException e) { + onChecksumsReadyListener.onChecksumsReady(allChecksums); + } catch (RemoteException e) { Slog.w(TAG, e); } } diff --git a/services/core/java/com/android/server/pm/DefaultAppProvider.java b/services/core/java/com/android/server/pm/DefaultAppProvider.java index a17967fcc76e..c18d0e9ef35e 100644 --- a/services/core/java/com/android/server/pm/DefaultAppProvider.java +++ b/services/core/java/com/android/server/pm/DefaultAppProvider.java @@ -41,14 +41,18 @@ import java.util.function.Supplier; public class DefaultAppProvider { @NonNull private final Supplier<RoleManager> mRoleManagerSupplier; + @NonNull + private final Supplier<UserManagerInternal> mUserManagerInternalSupplier; /** * Create a new instance of this class * * @param roleManagerSupplier the supplier for {@link RoleManager} */ - public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier) { + public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier, + @NonNull Supplier<UserManagerInternal> userManagerInternalSupplier) { mRoleManagerSupplier = roleManagerSupplier; + mUserManagerInternalSupplier = userManagerInternalSupplier; } /** @@ -132,7 +136,8 @@ public class DefaultAppProvider { */ @Nullable public String getDefaultHome(@NonNull int userId) { - return getRoleHolder(RoleManager.ROLE_HOME, userId); + return getRoleHolder(RoleManager.ROLE_HOME, + mUserManagerInternalSupplier.get().getProfileParentId(userId)); } /** diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java new file mode 100644 index 000000000000..a32e107d9e73 --- /dev/null +++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static java.util.Objects.requireNonNull; + +import android.annotation.IntDef; +import android.content.IntentFilter; + +import com.android.internal.annotations.Immutable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Representation of an immutable default cross-profile intent filter. + */ +@Immutable +final class DefaultCrossProfileIntentFilter { + + @IntDef({ + Direction.TO_PARENT, + Direction.TO_PROFILE + }) + @Retention(RetentionPolicy.SOURCE) + @interface Direction { + int TO_PARENT = 0; + int TO_PROFILE = 1; + } + + /** The intent filter that's used */ + public final IntentFilter filter; + + /** + * The flags related to the forwarding, e.g. + * {@link android.content.pm.PackageManager#SKIP_CURRENT_PROFILE} or + * {@link android.content.pm.PackageManager#ONLY_IF_NO_MATCH_FOUND}. + */ + public final int flags; + + /** + * The direction of forwarding, can be either {@link Direction#TO_PARENT} or + * {@link Direction#TO_PROFILE}. + */ + public final @Direction int direction; + + /** + * Whether this cross profile intent filter would allow personal data to be shared into + * the work profile. If this is {@code true}, this intent filter should be only added to + * the profile if the admin does not enable + * {@link android.os.UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE}. + */ + public final boolean letsPersonalDataIntoProfile; + + private DefaultCrossProfileIntentFilter(IntentFilter filter, int flags, + @Direction int direction, boolean letsPersonalDataIntoProfile) { + this.filter = requireNonNull(filter); + this.flags = flags; + this.direction = direction; + this.letsPersonalDataIntoProfile = letsPersonalDataIntoProfile; + } + + static final class Builder { + private IntentFilter mFilter = new IntentFilter(); + private int mFlags; + private @Direction int mDirection; + private boolean mLetsPersonalDataIntoProfile; + + Builder(@Direction int direction, int flags, boolean letsPersonalDataIntoProfile) { + mDirection = direction; + mFlags = flags; + mLetsPersonalDataIntoProfile = letsPersonalDataIntoProfile; + } + + Builder addAction(String action) { + mFilter.addAction(action); + return this; + } + + Builder addCategory(String category) { + mFilter.addCategory(category); + return this; + } + + Builder addDataType(String type) { + try { + mFilter.addDataType(type); + } catch (IntentFilter.MalformedMimeTypeException e) { + // ignore + } + return this; + } + + Builder addDataScheme(String scheme) { + mFilter.addDataScheme(scheme); + return this; + } + + DefaultCrossProfileIntentFilter build() { + return new DefaultCrossProfileIntentFilter(mFilter, mFlags, mDirection, + mLetsPersonalDataIntoProfile); + } + } +} diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java new file mode 100644 index 000000000000..3019439a430b --- /dev/null +++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.content.pm.PackageManager.ONLY_IF_NO_MATCH_FOUND; +import static android.content.pm.PackageManager.SKIP_CURRENT_PROFILE; +import static android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH; + +import android.content.Intent; +import android.hardware.usb.UsbManager; +import android.provider.AlarmClock; +import android.provider.MediaStore; + +import java.util.Arrays; +import java.util.List; + +/** + * Utility Class for {@link DefaultCrossProfileIntentFilter}. + */ +public class DefaultCrossProfileIntentFiltersUtils { + + private DefaultCrossProfileIntentFiltersUtils() { + } + + // Intents from profile to parent user + /** Emergency call intent with mime type is always resolved by primary user. */ + private static final DefaultCrossProfileIntentFilter + EMERGENCY_CALL_MIME = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + SKIP_CURRENT_PROFILE, + /* letsPersonalDataIntoProfile= */ false) + .addAction(Intent.ACTION_CALL_EMERGENCY) + .addAction(Intent.ACTION_CALL_PRIVILEGED) + .addCategory(Intent.CATEGORY_DEFAULT) + .addCategory(Intent.CATEGORY_BROWSABLE) + .addDataType("vnd.android.cursor.item/phone") + .addDataType("vnd.android.cursor.item/phone_v2") + .addDataType("vnd.android.cursor.item/person") + .addDataType("vnd.android.cursor.dir/calls") + .addDataType("vnd.android.cursor.item/calls") + .build(); + + /** Emergency call intent with data schemes is always resolved by primary user. */ + private static final DefaultCrossProfileIntentFilter + EMERGENCY_CALL_DATA = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + SKIP_CURRENT_PROFILE, + /* letsPersonalDataIntoProfile= */ false) + .addAction(Intent.ACTION_CALL_EMERGENCY) + .addAction(Intent.ACTION_CALL_PRIVILEGED) + .addCategory(Intent.CATEGORY_DEFAULT) + .addCategory(Intent.CATEGORY_BROWSABLE) + .addDataScheme("tel") + .addDataScheme("sip") + .addDataScheme("voicemail") + .build(); + + /** Dial intent with mime type can be handled by either managed profile or its parent user. */ + private static final DefaultCrossProfileIntentFilter DIAL_MIME = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + ONLY_IF_NO_MATCH_FOUND, + /* letsPersonalDataIntoProfile= */ false) + .addAction(Intent.ACTION_DIAL) + .addAction(Intent.ACTION_VIEW) + .addCategory(Intent.CATEGORY_DEFAULT) + .addCategory(Intent.CATEGORY_BROWSABLE) + .addDataType("vnd.android.cursor.item/phone") + .addDataType("vnd.android.cursor.item/phone_v2") + .addDataType("vnd.android.cursor.item/person") + .addDataType("vnd.android.cursor.dir/calls") + .addDataType("vnd.android.cursor.item/calls") + .build(); + + /** Dial intent with data scheme can be handled by either managed profile or its parent user. */ + private static final DefaultCrossProfileIntentFilter DIAL_DATA = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + ONLY_IF_NO_MATCH_FOUND, + /* letsPersonalDataIntoProfile= */ false) + .addAction(Intent.ACTION_DIAL) + .addAction(Intent.ACTION_VIEW) + .addCategory(Intent.CATEGORY_DEFAULT) + .addCategory(Intent.CATEGORY_BROWSABLE) + .addDataScheme("tel") + .addDataScheme("sip") + .addDataScheme("voicemail") + .build(); + + /** + * Dial intent with no data scheme or type can be handled by either managed profile or its + * parent user. + */ + private static final DefaultCrossProfileIntentFilter DIAL_RAW = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + ONLY_IF_NO_MATCH_FOUND, + /* letsPersonalDataIntoProfile= */ false) + .addAction(Intent.ACTION_DIAL) + .addCategory(Intent.CATEGORY_DEFAULT) + .addCategory(Intent.CATEGORY_BROWSABLE) + .build(); + + /** Pressing the call button can be handled by either managed profile or its parent user. */ + private static final DefaultCrossProfileIntentFilter CALL_BUTTON = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + ONLY_IF_NO_MATCH_FOUND, + /* letsPersonalDataIntoProfile= */ false) + .addAction(Intent.ACTION_CALL_BUTTON) + .addCategory(Intent.CATEGORY_DEFAULT) + .build(); + + /** SMS and MMS are exclusively handled by the primary user. */ + private static final DefaultCrossProfileIntentFilter SMS_MMS = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + SKIP_CURRENT_PROFILE, + /* letsPersonalDataIntoProfile= */ false) + .addAction(Intent.ACTION_VIEW) + .addAction(Intent.ACTION_SENDTO) + .addCategory(Intent.CATEGORY_DEFAULT) + .addCategory(Intent.CATEGORY_BROWSABLE) + .addDataScheme("sms") + .addDataScheme("smsto") + .addDataScheme("mms") + .addDataScheme("mmsto") + .build(); + + /** Mobile network settings is always shown in the primary user. */ + private static final DefaultCrossProfileIntentFilter + MOBILE_NETWORK_SETTINGS = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + SKIP_CURRENT_PROFILE, + /* letsPersonalDataIntoProfile= */ false) + .addAction(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS) + .addAction(android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS) + .addCategory(Intent.CATEGORY_DEFAULT) + .build(); + + /** HOME intent is always resolved by the primary user. */ + static final DefaultCrossProfileIntentFilter HOME = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + SKIP_CURRENT_PROFILE, + /* letsPersonalDataIntoProfile= */ false) + .addAction(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_DEFAULT) + .addCategory(Intent.CATEGORY_HOME) + .build(); + + /** Get content can be forwarded to parent user. */ + private static final DefaultCrossProfileIntentFilter GET_CONTENT = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + /* flags= */0, + /* letsPersonalDataIntoProfile= */ true) + .addAction(Intent.ACTION_GET_CONTENT) + .addCategory(Intent.CATEGORY_DEFAULT) + .addCategory(Intent.CATEGORY_OPENABLE) + .addDataType("*/*") + .build(); + + /** Open document intent can be forwarded to parent user. */ + private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + /* flags= */0, + /* letsPersonalDataIntoProfile= */ true) + .addAction(Intent.ACTION_OPEN_DOCUMENT) + .addCategory(Intent.CATEGORY_DEFAULT) + .addCategory(Intent.CATEGORY_OPENABLE) + .addDataType("*/*") + .build(); + + /** Pick for any data type can be forwarded to parent user. */ + private static final DefaultCrossProfileIntentFilter ACTION_PICK_DATA = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + /* flags= */0, + /* letsPersonalDataIntoProfile= */ true) + .addAction(Intent.ACTION_PICK) + .addCategory(Intent.CATEGORY_DEFAULT) + .addDataType("*/*") + .build(); + + /** Pick without data type can be forwarded to parent user. */ + private static final DefaultCrossProfileIntentFilter ACTION_PICK_RAW = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + /* flags= */0, + /* letsPersonalDataIntoProfile= */ true) + .addAction(Intent.ACTION_PICK) + .addCategory(Intent.CATEGORY_DEFAULT) + .build(); + + /** Speech recognition can be performed by primary user. */ + private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + /* flags= */0, + /* letsPersonalDataIntoProfile= */ false) + .addAction(ACTION_RECOGNIZE_SPEECH) + .addCategory(Intent.CATEGORY_DEFAULT) + .build(); + + /** Media capture can be performed by primary user. */ + private static final DefaultCrossProfileIntentFilter MEDIA_CAPTURE = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + /* flags= */0, + /* letsPersonalDataIntoProfile= */ true) + .addAction(MediaStore.ACTION_IMAGE_CAPTURE) + .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE) + .addAction(MediaStore.ACTION_VIDEO_CAPTURE) + .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION) + .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA) + .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE) + .addAction(MediaStore.INTENT_ACTION_VIDEO_CAMERA) + .addCategory(Intent.CATEGORY_DEFAULT) + .build(); + + /** Alarm setting can be performed by primary user. */ + private static final DefaultCrossProfileIntentFilter SET_ALARM = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + /* flags= */0, + /* letsPersonalDataIntoProfile= */ false) + .addAction(AlarmClock.ACTION_SET_ALARM) + .addAction(AlarmClock.ACTION_SHOW_ALARMS) + .addAction(AlarmClock.ACTION_SET_TIMER) + .addCategory(Intent.CATEGORY_DEFAULT) + .build(); + + // Intents from parent to profile user + + /** ACTION_SEND can be forwarded to the managed profile on user's choice. */ + private static final DefaultCrossProfileIntentFilter ACTION_SEND = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PROFILE, + /* flags= */0, + /* letsPersonalDataIntoProfile= */ true) + .addAction(Intent.ACTION_SEND) + .addAction(Intent.ACTION_SEND_MULTIPLE) + .addCategory(Intent.CATEGORY_DEFAULT) + .addDataType("*/*") + .build(); + + /** USB devices attached can get forwarded to the profile. */ + private static final DefaultCrossProfileIntentFilter + USB_DEVICE_ATTACHED = + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PROFILE, + /* flags= */0, + /* letsPersonalDataIntoProfile= */ false) + .addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED) + .addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED) + .addCategory(Intent.CATEGORY_DEFAULT) + .build(); + + public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileFilters() { + return Arrays.asList( + EMERGENCY_CALL_MIME, + EMERGENCY_CALL_DATA, + DIAL_MIME, + DIAL_DATA, + DIAL_RAW, + CALL_BUTTON, + SMS_MMS, + SET_ALARM, + MEDIA_CAPTURE, + RECOGNIZE_SPEECH, + ACTION_PICK_RAW, + ACTION_PICK_DATA, + OPEN_DOCUMENT, + GET_CONTENT, + USB_DEVICE_ATTACHED, + ACTION_SEND, + HOME, + MOBILE_NETWORK_SETTINGS); + } +} diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java index f5ec595cc45a..ecafdfdbd6f1 100644 --- a/services/core/java/com/android/server/pm/IncrementalStates.java +++ b/services/core/java/com/android/server/pm/IncrementalStates.java @@ -249,24 +249,6 @@ public final class IncrementalStates { } /** - * @return the current startable state. - */ - public boolean isStartable() { - synchronized (mLock) { - return mStartableState.isStartable(); - } - } - - /** - * @return Whether the package is still being loaded or has been fully loaded. - */ - public boolean isLoading() { - synchronized (mLock) { - return mLoadingState.isLoading(); - } - } - - /** * @return all current states in a Parcelable. */ public IncrementalStatesInfo getIncrementalStatesInfo() { diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index c4a23f961072..f240d85572c4 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -1316,10 +1316,6 @@ public class LauncherAppsService extends SystemService { } finally { mListeners.finishBroadcast(); } - PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); - pmi.registerInstalledLoadingProgressCallback(packageName, - new PackageLoadingProgressCallback(packageName, user), - user.getIdentifier()); super.onPackageAdded(packageName, uid); } @@ -1542,38 +1538,5 @@ public class LauncherAppsService extends SystemService { checkCallbackCount(); } } - - class PackageLoadingProgressCallback extends - PackageManagerInternal.InstalledLoadingProgressCallback { - private String mPackageName; - private UserHandle mUser; - - PackageLoadingProgressCallback(String packageName, UserHandle user) { - super(mCallbackHandler); - mPackageName = packageName; - mUser = user; - } - - @Override - public void onLoadingProgressChanged(float progress) { - final int n = mListeners.beginBroadcast(); - try { - for (int i = 0; i < n; i++) { - IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); - BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); - if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) { - continue; - } - try { - listener.onPackageLoadingProgressChanged(mUser, mPackageName, progress); - } catch (RemoteException re) { - Slog.d(TAG, "Callback failed ", re); - } - } - } finally { - mListeners.finishBroadcast(); - } - } - } } } diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java index 6485c0c42ab7..d851e6ced91b 100644 --- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java +++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java @@ -401,11 +401,16 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { String[] abiList = (cpuAbiOverride != null) ? new String[]{cpuAbiOverride} : Build.SUPPORTED_ABIS; - // Enable gross and lame hacks for apps that are built with old - // SDK tools. We must scan their APKs for renderscript bitcode and - // not launch them if it's present. Don't bother checking on devices - // that don't have 64 bit support. + // If an app that contains RenderScript has target API level < 21, it needs to run + // with 32-bit ABI, and its APK file will contain a ".bc" file. + // If an app that contains RenderScript has target API level >= 21, it can run with + // either 32-bit or 64-bit ABI, and its APK file will not contain a ".bc" file. + // Therefore, on a device that supports both 32-bit and 64-bit ABIs, we scan the app + // APK to see if it has a ".bc" file. If so, we will run it with 32-bit ABI. + // However, if the device only supports 64-bit ABI but does not support 32-bit ABI, + // we will fail the installation for such an app because it won't be able to run. boolean needsRenderScriptOverride = false; + // No need to check if the device only supports 32-bit if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null && NativeLibraryHelper.hasRenderscriptBitcode(handle)) { if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { @@ -414,7 +419,8 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { } else { throw new PackageManagerException( INSTALL_FAILED_CPU_ABI_INCOMPATIBLE, - "Apks with renderscript are not supported on 64-bit only devices"); + "Apps that contain RenderScript with target API level < 21 are not " + + "supported on 64-bit only platforms"); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f444ed6df2a3..965f68bdcaf3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -24,7 +24,6 @@ import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS; import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.MODE_IGNORED; import static android.content.Intent.ACTION_MAIN; -import static android.content.Intent.CATEGORY_BROWSABLE; import static android.content.Intent.CATEGORY_DEFAULT; import static android.content.Intent.CATEGORY_HOME; import static android.content.Intent.EXTRA_LONG_VERSION_CODE; @@ -68,11 +67,7 @@ import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE; import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP; import static android.content.pm.PackageManager.INSTALL_STAGED; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; import static android.content.pm.PackageManager.MATCH_ALL; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.content.pm.PackageManager.MATCH_APEX; @@ -177,6 +172,7 @@ import android.content.pm.DataLoaderType; import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureInfo; import android.content.pm.IDexModuleRegisterCallback; +import android.content.pm.IOnChecksumsReadyListener; import android.content.pm.IPackageChangeObserver; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; @@ -377,11 +373,6 @@ import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.dex.ViewCompiler; -import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; -import com.android.server.pm.verify.domain.DomainVerificationService; -import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; -import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1; -import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2; import com.android.server.pm.parsing.PackageCacher; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.PackageParser2; @@ -395,6 +386,11 @@ import com.android.server.pm.permission.LegacyPermissionManagerService; import com.android.server.pm.permission.Permission; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationService; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2; import com.android.server.rollback.RollbackManagerInternal; import com.android.server.security.VerityUtils; import com.android.server.storage.DeviceStorageMonitorInternal; @@ -404,7 +400,6 @@ import com.android.server.utils.Watchable; import com.android.server.utils.Watched; import com.android.server.utils.WatchedArrayMap; import com.android.server.utils.WatchedSparseBooleanArray; -import com.android.server.utils.WatchedSparseIntArray; import com.android.server.utils.Watcher; import com.android.server.wm.ActivityTaskManagerInternal; @@ -466,7 +461,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.function.Supplier; /** * Keep track of all those APKs everywhere. @@ -3622,8 +3616,6 @@ public class PackageManagerService extends IPackageManager.Stub final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); - enforceCrossUserPermission(callingUid, userId, - /* requireFullPermission */ false, /* checkShell */ false, "getPackagesForUid"); return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp); } @@ -5523,19 +5515,19 @@ public class PackageManagerService extends IPackageManager.Stub public void requestChecksums(@NonNull String packageName, boolean includeSplits, @Checksum.Type int optional, @Checksum.Type int required, @Nullable List trustedInstallers, - @NonNull IntentSender statusReceiver, int userId) { + @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) { requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers, - statusReceiver, userId, mInjector.getBackgroundExecutor(), + onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(), mInjector.getBackgroundHandler()); } private void requestChecksumsInternal(@NonNull String packageName, boolean includeSplits, - @Checksum.Type int optional, - @Checksum.Type int required, @Nullable List trustedInstallers, - @NonNull IntentSender statusReceiver, int userId, @NonNull Executor executor, - @NonNull Handler handler) { + @Checksum.Type int optional, @Checksum.Type int required, + @Nullable List trustedInstallers, + @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId, + @NonNull Executor executor, @NonNull Handler handler) { Objects.requireNonNull(packageName); - Objects.requireNonNull(statusReceiver); + Objects.requireNonNull(onChecksumsReadyListener); Objects.requireNonNull(executor); Objects.requireNonNull(handler); @@ -5571,7 +5563,7 @@ public class PackageManagerService extends IPackageManager.Stub () -> mInjector.getIncrementalManager(), () -> mPmInternal); ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName, - trustedCerts, statusReceiver, injector); + trustedCerts, onChecksumsReadyListener, injector); }); } @@ -5769,8 +5761,8 @@ public class PackageManagerService extends IPackageManager.Stub (i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()), (i, pm) -> (IncrementalManager) i.getContext().getSystemService(Context.INCREMENTAL_SERVICE), - (i, pm) -> new DefaultAppProvider(() -> context.getSystemService( - RoleManager.class)), + (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class), + () -> LocalServices.getService(UserManagerInternal.class)), (i, pm) -> new DisplayMetrics(), (i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore, i.getDisplayMetrics(), pm.mCacheDir, @@ -19168,6 +19160,8 @@ public class PackageManagerService extends IPackageManager.Stub extras, 0 /*flags*/, null /*targetPackage*/, null /*finishedReceiver*/, mInstalledUserIds, null /* instantUserIds */, newBroadcastAllowList, null); + // Unregister progress listener + mIncrementalManager.unregisterLoadingProgressCallbacks(codePath); // Unregister health listener as it will always be healthy from now mIncrementalManager.unregisterHealthListener(codePath); } @@ -23939,7 +23933,8 @@ public class PackageManagerService extends IPackageManager.Stub writer.println("Domain verification status:"); writer.increaseIndent(); try { - mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL); + mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL, + mSettings::getPackageLPr); } catch (PackageManager.NameNotFoundException e) { pw.println("Failure printing domain verification information"); Slog.e(TAG, "Failure printing domain verification information", e); @@ -27045,47 +27040,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public boolean registerInstalledLoadingProgressCallback(String packageName, - PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) { - final PackageSetting ps = getPackageSettingForUser(packageName, Binder.getCallingUid(), - userId); - if (ps == null) { - return false; - } - if (!ps.isPackageLoading()) { - Slog.w(TAG, - "Failed registering loading progress callback. Package is fully loaded."); - return false; - } - if (mIncrementalManager == null) { - Slog.w(TAG, - "Failed registering loading progress callback. Incremental is not enabled"); - return false; - } - return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(), - (IPackageLoadingProgressCallback) callback.getBinder()); - } - - @Override - public boolean unregisterInstalledLoadingProgressCallback(String packageName, - PackageManagerInternal.InstalledLoadingProgressCallback callback) { - final PackageSetting ps; - synchronized (mLock) { - ps = mSettings.getPackageLPr(packageName); - if (ps == null) { - Slog.w(TAG, "Failed unregistering loading progress callback. Package " - + packageName + " is not installed"); - return false; - } - } - if (mIncrementalManager == null) { - return false; - } - return mIncrementalManager.unregisterLoadingProgressCallback(ps.getPathString(), - (IPackageLoadingProgressCallback) callback.getBinder()); - } - - @Override public IncrementalStatesInfo getIncrementalStatesInfo( @NonNull String packageName, int filterCallingUid, int userId) { final PackageSetting ps = getPackageSettingForUser(packageName, filterCallingUid, @@ -27110,12 +27064,14 @@ public class PackageManagerService extends IPackageManager.Stub ps.setStatesOnCrashOrAnr(); } + @Override public void requestChecksums(@NonNull String packageName, boolean includeSplits, @Checksum.Type int optional, @Checksum.Type int required, - @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId, + @Nullable List trustedInstallers, + @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId, @NonNull Executor executor, @NonNull Handler handler) { requestChecksumsInternal(packageName, includeSplits, optional, required, - trustedInstallers, statusReceiver, userId, executor, handler); + trustedInstallers, onChecksumsReadyListener, userId, executor, handler); } @Override diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 69e84b536004..ca5d2b49b99d 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -344,8 +344,8 @@ public class PackageSetting extends PackageSettingBase { installSource.originatingPackageName); proto.end(sourceToken); } - proto.write(PackageProto.StatesProto.IS_STARTABLE, incrementalStates.isStartable()); - proto.write(PackageProto.StatesProto.IS_LOADING, incrementalStates.isLoading()); + proto.write(PackageProto.StatesProto.IS_STARTABLE, isPackageStartable()); + proto.write(PackageProto.StatesProto.IS_LOADING, isPackageLoading()); writeUsersInfoToProto(proto, PackageProto.USERS); writePackageUserPermissionsProto(proto, PackageProto.USER_PERMISSIONS, users, dataProvider); proto.end(packageToken); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 8aa553d68b98..3a142837e063 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -26,8 +26,6 @@ import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.IncrementalStatesInfo; -import android.content.pm.IntentFilterVerificationInfo; -import android.content.pm.PackageManager; import android.content.pm.PackageManager.UninstallReason; import android.content.pm.PackageParser; import android.content.pm.PackageUserState; @@ -42,8 +40,6 @@ import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; -import com.android.server.pm.verify.domain.DomainVerificationService; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.File; @@ -731,14 +727,14 @@ public abstract class PackageSettingBase extends SettingBase { * @return True if package is startable, false otherwise. */ public boolean isPackageStartable() { - return incrementalStates.isStartable(); + return getIncrementalStates().isStartable(); } /** * @return True if package is still being loaded, false if the package is fully loaded. */ public boolean isPackageLoading() { - return incrementalStates.isLoading(); + return getIncrementalStates().isLoading(); } /** diff --git a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java index ce77c9163843..8c5084afcdf9 100644 --- a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java +++ b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java @@ -16,22 +16,16 @@ package com.android.server.pm; -import static android.content.pm.PackageManager.EXTRA_CHECKSUMS; - import android.app.admin.SecurityLog; import android.content.Context; -import android.content.IIntentReceiver; -import android.content.IIntentSender; -import android.content.Intent; -import android.content.IntentSender; import android.content.pm.ApkChecksum; import android.content.pm.Checksum; +import android.content.pm.IOnChecksumsReadyListener; import android.content.pm.PackageManagerInternal; import android.os.Bundle; import android.os.Handler; import android.os.HandlerExecutor; -import android.os.IBinder; -import android.os.Parcelable; +import android.os.RemoteException; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Slog; @@ -39,7 +33,6 @@ import android.util.Slog; import com.android.internal.os.BackgroundThread; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; @@ -109,26 +102,23 @@ public final class ProcessLoggingHandler extends Handler { // Capturing local loggingInfo to still log even if hash was invalidated. try { pmi.requestChecksums(packageName, false, 0, CHECKSUM_TYPE, null, - new IntentSender((IIntentSender) new IIntentSender.Stub() { + new IOnChecksumsReadyListener.Stub() { @Override - public void send(int code, Intent intent, String resolvedType, - IBinder allowlistToken, IIntentReceiver finishedReceiver, - String requiredPermission, Bundle options) { - processChecksums(loggingInfo, intent); + public void onChecksumsReady(List<ApkChecksum> checksums) + throws RemoteException { + processChecksums(loggingInfo, checksums); } - }), context.getUserId(), mExecutor, this); + }, context.getUserId(), + mExecutor, this); } catch (Throwable t) { Slog.e(TAG, "requestChecksums() failed", t); enqueueProcessChecksum(loggingInfo, null); } } - void processChecksums(final LoggingInfo loggingInfo, Intent intent) { - Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_CHECKSUMS); - ApkChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length, - ApkChecksum[].class); - - for (ApkChecksum checksum : checksums) { + void processChecksums(final LoggingInfo loggingInfo, List<ApkChecksum> checksums) { + for (int i = 0, size = checksums.size(); i < size; ++i) { + ApkChecksum checksum = checksums.get(i); if (checksum.getType() == CHECKSUM_TYPE) { processChecksum(loggingInfo, checksum.getValue()); return; diff --git a/services/core/java/com/android/server/pm/RestrictionsSet.java b/services/core/java/com/android/server/pm/RestrictionsSet.java index ea61ca47deb0..e5a70c3a2365 100644 --- a/services/core/java/com/android/server/pm/RestrictionsSet.java +++ b/services/core/java/com/android/server/pm/RestrictionsSet.java @@ -26,6 +26,7 @@ import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.BundleUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -78,7 +79,7 @@ public class RestrictionsSet { if (!changed) { return false; } - if (!UserRestrictionsUtils.isEmpty(restrictions)) { + if (!BundleUtils.isEmpty(restrictions)) { mUserRestrictions.put(userId, restrictions); } else { mUserRestrictions.delete(userId); diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 89729b585b14..9b092c000172 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -1535,6 +1535,27 @@ class ShortcutPackage extends ShortcutPackageItem { pw.println(")"); } + public void dumpShortcuts(@NonNull PrintWriter pw, int matchFlags) { + final boolean matchDynamic = (matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC) != 0; + final boolean matchPinned = (matchFlags & ShortcutManager.FLAG_MATCH_PINNED) != 0; + final boolean matchManifest = (matchFlags & ShortcutManager.FLAG_MATCH_MANIFEST) != 0; + final boolean matchCached = (matchFlags & ShortcutManager.FLAG_MATCH_CACHED) != 0; + + final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0) + | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0) + | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0) + | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0); + + final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts; + final int size = shortcuts.size(); + for (int i = 0; i < size; i++) { + final ShortcutInfo si = shortcuts.valueAt(i); + if ((si.getFlags() & shortcutFlags) != 0) { + pw.println(si.toDumpString("")); + } + } + } + @Override public JSONObject dumpCheckin(boolean clear) throws JSONException { final JSONObject result = super.dumpCheckin(clear); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 3c4457db6cf0..863e3fe5c6a3 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -40,6 +40,7 @@ import android.content.IntentSender.SendIntentException; import android.content.LocusId; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ComponentInfo; import android.content.pm.IPackageManager; import android.content.pm.IShortcutService; import android.content.pm.LauncherApps; @@ -4556,6 +4557,10 @@ public class ShortcutService extends IShortcutService.Stub { private int mUserId = UserHandle.USER_SYSTEM; + private int mShortcutMatchFlags = ShortcutManager.FLAG_MATCH_CACHED + | ShortcutManager.FLAG_MATCH_DYNAMIC | ShortcutManager.FLAG_MATCH_MANIFEST + | ShortcutManager.FLAG_MATCH_PINNED; + private void parseOptionsLocked(boolean takeUser) throws CommandException { String opt; @@ -4571,6 +4576,9 @@ public class ShortcutService extends IShortcutService.Stub { break; } // fallthrough + case "--flags": + mShortcutMatchFlags = Integer.parseInt(getNextArgRequired()); + break; default: throw new CommandException("Unknown option: " + opt); } @@ -4606,9 +4614,15 @@ public class ShortcutService extends IShortcutService.Stub { case "clear-shortcuts": handleClearShortcuts(); break; + case "get-shortcuts": + handleGetShortcuts(); + break; case "verify-states": // hidden command to verify various internal states. handleVerifyStates(); break; + case "has-shortcut-access": + handleHasShortcutAccess(); + break; default: return handleDefaultCommands(cmd); } @@ -4640,7 +4654,7 @@ public class ShortcutService extends IShortcutService.Stub { pw.println("[Deprecated] cmd shortcut get-default-launcher [--user USER_ID]"); pw.println(" Show the default launcher"); pw.println(" Note: This command is deprecated. Callers should query the default" - + " launcher directly from RoleManager instead."); + + " launcher from RoleManager instead."); pw.println(); pw.println("cmd shortcut unload-user [--user USER_ID]"); pw.println(" Unload a user from the memory"); @@ -4649,6 +4663,13 @@ public class ShortcutService extends IShortcutService.Stub { pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE"); pw.println(" Remove all shortcuts from a package, including pinned shortcuts"); pw.println(); + pw.println("cmd shortcut get-shortcuts [--user USER_ID] [--flags FLAGS] PACKAGE"); + pw.println(" Show the shortcuts for a package that match the given flags"); + pw.println(); + pw.println("cmd shortcut has-shortcut-access [--user USER_ID] PACKAGE"); + pw.println(" Prints \"true\" if the package can access shortcuts," + + " \"false\" otherwise"); + pw.println(); } private void handleResetThrottling() throws CommandException { @@ -4693,11 +4714,24 @@ public class ShortcutService extends IShortcutService.Stub { private void handleGetDefaultLauncher() throws CommandException { synchronized (mLock) { parseOptionsLocked(/* takeUser =*/ true); + + final String defaultLauncher = getDefaultLauncher(mUserId); + if (defaultLauncher == null) { + throw new CommandException( + "Failed to get the default launcher for user " + mUserId); + } + + // Get the class name of the component from PM to keep the old behaviour. final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); - // Default launcher from package manager. - final ComponentName defaultLauncher = mPackageManagerInternal - .getHomeActivitiesAsUser(allHomeCandidates, getParentOrSelfUserId(mUserId)); - getOutPrintWriter().println("Launcher: " + defaultLauncher); + mPackageManagerInternal.getHomeActivitiesAsUser(allHomeCandidates, + getParentOrSelfUserId(mUserId)); + for (ResolveInfo ri : allHomeCandidates) { + final ComponentInfo ci = ri.getComponentInfo(); + if (ci.packageName.equals(defaultLauncher)) { + getOutPrintWriter().println("Launcher: " + ci.getComponentName()); + break; + } + } } } @@ -4723,6 +4757,24 @@ public class ShortcutService extends IShortcutService.Stub { } } + private void handleGetShortcuts() throws CommandException { + synchronized (mLock) { + parseOptionsLocked(/* takeUser =*/ true); + final String packageName = getNextArgRequired(); + + Slog.i(TAG, "cmd: handleGetShortcuts: user=" + mUserId + ", flags=" + + mShortcutMatchFlags + ", package=" + packageName); + + final ShortcutUser user = ShortcutService.this.getUserShortcutsLocked(mUserId); + final ShortcutPackage p = user.getPackageShortcutsIfExists(packageName); + if (p == null) { + return; + } + + p.dumpShortcuts(getOutPrintWriter(), mShortcutMatchFlags); + } + } + private void handleVerifyStates() throws CommandException { try { verifyStatesForce(); // This will throw when there's an issue. @@ -4730,6 +4782,16 @@ public class ShortcutService extends IShortcutService.Stub { throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th)); } } + + private void handleHasShortcutAccess() throws CommandException { + synchronized (mLock) { + parseOptionsLocked(/* takeUser =*/ true); + final String packageName = getNextArgRequired(); + + boolean shortcutAccess = hasShortcutHostPermissionInner(packageName, mUserId); + getOutPrintWriter().println(Boolean.toString(shortcutAccess)); + } + } } // === Unit test support === diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java index c182d685c247..c17d2e4b0c62 100644 --- a/services/core/java/com/android/server/pm/UserManagerInternal.java +++ b/services/core/java/com/android/server/pm/UserManagerInternal.java @@ -298,4 +298,11 @@ public abstract class UserManagerInternal { * Gets all {@link UserInfo UserInfos}. */ public abstract @NonNull UserInfo[] getUserInfos(); + + /** + * Sets all default cross profile intent filters between {@code parentUserId} and + * {@code profileUserId}. + */ + public abstract void setDefaultCrossProfileIntentFilters( + @UserIdInt int parentUserId, @UserIdInt int profileUserId); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index a8077774d62a..753f22da2b9d 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -80,6 +80,7 @@ import android.os.UserManager; import android.os.UserManager.EnforcingUser; import android.os.UserManager.QuietModeFlag; import android.os.storage.StorageManager; +import android.provider.Settings; import android.security.GateKeeper; import android.service.gatekeeper.IGateKeeperService; import android.stats.devicepolicy.DevicePolicyEnums; @@ -108,6 +109,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; +import com.android.server.BundleUtils; import com.android.server.LocalServices; import com.android.server.LockGuard; import com.android.server.SystemService; @@ -1960,11 +1962,11 @@ public class UserManagerService extends IUserManager.Stub { final Bundle global = mDevicePolicyGlobalUserRestrictions.mergeAll(); final RestrictionsSet local = getDevicePolicyLocalRestrictionsForTargetUserLR(userId); - if (UserRestrictionsUtils.isEmpty(global) && local.isEmpty()) { + if (BundleUtils.isEmpty(global) && local.isEmpty()) { // Common case first. return baseRestrictions; } - final Bundle effective = UserRestrictionsUtils.clone(baseRestrictions); + final Bundle effective = BundleUtils.clone(baseRestrictions); UserRestrictionsUtils.merge(effective, global); UserRestrictionsUtils.merge(effective, local.mergeAll()); @@ -2105,7 +2107,7 @@ public class UserManagerService extends IUserManager.Stub { @Override public Bundle getUserRestrictions(@UserIdInt int userId) { checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserRestrictions"); - return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId)); + return BundleUtils.clone(getEffectiveUserRestrictions(userId)); } @Override @@ -2129,7 +2131,7 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mRestrictionsLock) { // Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create // a copy. - final Bundle newRestrictions = UserRestrictionsUtils.clone( + final Bundle newRestrictions = BundleUtils.clone( mBaseUserRestrictions.getRestrictions(userId)); newRestrictions.putBoolean(key, value); @@ -2727,7 +2729,7 @@ public class UserManagerService extends IUserManager.Stub { if (userVersion < 7) { // Previously only one user could enforce global restrictions, now it is per-user. synchronized (mRestrictionsLock) { - if (!UserRestrictionsUtils.isEmpty(oldGlobalUserRestrictions) + if (!BundleUtils.isEmpty(oldGlobalUserRestrictions) && mDeviceOwnerUserId != UserHandle.USER_NULL) { mDevicePolicyGlobalUserRestrictions.updateRestrictions( mDeviceOwnerUserId, oldGlobalUserRestrictions); @@ -3562,6 +3564,9 @@ public class UserManagerService extends IUserManager.Stub { t.traceBegin("PM.onNewUserCreated-" + userId); mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false); t.traceEnd(); + applyDefaultUserSettings(userTypeDetails, userId); + setDefaultCrossProfileIntentFilters(userId, userTypeDetails, restrictions, parentId); + if (preCreate) { // Must start user (which will be stopped right away, through // UserController.finishUserUnlockedCompleted) so services can properly @@ -3597,6 +3602,78 @@ public class UserManagerService extends IUserManager.Stub { return userInfo; } + private void applyDefaultUserSettings(UserTypeDetails userTypeDetails, @UserIdInt int userId) { + final Bundle systemSettings = userTypeDetails.getDefaultSystemSettings(); + final Bundle secureSettings = userTypeDetails.getDefaultSecureSettings(); + if (systemSettings.isEmpty() && secureSettings.isEmpty()) { + return; + } + + final int systemSettingsSize = systemSettings.size(); + final String[] systemSettingsArray = systemSettings.keySet().toArray( + new String[systemSettingsSize]); + for (int i = 0; i < systemSettingsSize; i++) { + final String setting = systemSettingsArray[i]; + if (!Settings.System.putStringForUser( + mContext.getContentResolver(), setting, systemSettings.getString(setting), + userId)) { + Slog.e(LOG_TAG, "Failed to insert default system setting: " + setting); + } + } + + final int secureSettingsSize = secureSettings.size(); + final String[] secureSettingsArray = secureSettings.keySet().toArray( + new String[secureSettingsSize]); + for (int i = 0; i < secureSettingsSize; i++) { + final String setting = secureSettingsArray[i]; + if (!Settings.Secure.putStringForUser( + mContext.getContentResolver(), setting, secureSettings.getString(setting), + userId)) { + Slog.e(LOG_TAG, "Failed to insert default secure setting: " + setting); + } + } + } + + /** + * Sets all default cross profile intent filters between {@code parentUserId} and + * {@code profileUserId}, does nothing if {@code userType} is not a profile. + */ + private void setDefaultCrossProfileIntentFilters( + @UserIdInt int profileUserId, UserTypeDetails profileDetails, + Bundle profileRestrictions, @UserIdInt int parentUserId) { + if (profileDetails == null || !profileDetails.isProfile()) { + return; + } + final List<DefaultCrossProfileIntentFilter> filters = + profileDetails.getDefaultCrossProfileIntentFilters(); + if (filters.isEmpty()) { + return; + } + + // Skip filters that allow data to be shared into the profile, if admin has disabled it. + final boolean disallowSharingIntoProfile = + profileRestrictions.getBoolean( + UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, + /* defaultValue = */ false); + final int size = profileDetails.getDefaultCrossProfileIntentFilters().size(); + for (int i = 0; i < size; i++) { + final DefaultCrossProfileIntentFilter filter = + profileDetails.getDefaultCrossProfileIntentFilters().get(i); + if (disallowSharingIntoProfile && filter.letsPersonalDataIntoProfile) { + continue; + } + if (filter.direction == DefaultCrossProfileIntentFilter.Direction.TO_PARENT) { + mPm.addCrossProfileIntentFilter( + filter.filter, mContext.getOpPackageName(), profileUserId, parentUserId, + filter.flags); + } else { + mPm.addCrossProfileIntentFilter( + filter.filter, mContext.getOpPackageName(), parentUserId, profileUserId, + filter.flags); + } + } + } + /** * Finds and converts a previously pre-created user into a regular user, if possible. * @@ -5462,6 +5539,15 @@ public class UserManagerService extends IUserManager.Stub { return allInfos; } } + + @Override + public void setDefaultCrossProfileIntentFilters( + @UserIdInt int parentUserId, @UserIdInt int profileUserId) { + final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(profileUserId); + final Bundle restrictions = getEffectiveUserRestrictions(profileUserId); + UserManagerService.this.setDefaultCrossProfileIntentFilters( + profileUserId, userTypeDetails, restrictions, parentUserId); + } } /** @@ -5726,8 +5812,8 @@ public class UserManagerService extends IUserManager.Stub { // merge existing base restrictions with the new type's default restrictions synchronized (mRestrictionsLock) { - if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) { - final Bundle newRestrictions = UserRestrictionsUtils.clone( + if (!BundleUtils.isEmpty(newUserType.getDefaultRestrictions())) { + final Bundle newRestrictions = BundleUtils.clone( mBaseUserRestrictions.getRestrictions(userInfo.id)); UserRestrictionsUtils.merge(newRestrictions, newUserType.getDefaultRestrictions()); diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 51dff4063850..ff90043bec94 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -44,6 +44,7 @@ import android.util.TypedXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; +import com.android.server.BundleUtils; import com.android.server.LocalServices; import com.google.android.collect.Sets; @@ -384,10 +385,6 @@ public class UserRestrictionsUtils { return in != null ? in : new Bundle(); } - public static boolean isEmpty(@Nullable Bundle in) { - return (in == null) || (in.size() == 0); - } - /** * Returns {@code true} if given bundle is not null and contains {@code true} for a given * restriction. @@ -396,17 +393,6 @@ public class UserRestrictionsUtils { return in != null && in.getBoolean(restriction); } - /** - * Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty - * bundle. - * - * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return - * {@link Bundle#EMPTY}) - */ - public static @NonNull Bundle clone(@Nullable Bundle in) { - return (in != null) ? new Bundle(in) : new Bundle(); - } - public static void merge(@NonNull Bundle dest, @Nullable Bundle in) { Objects.requireNonNull(dest); Preconditions.checkArgument(dest != in); @@ -483,10 +469,10 @@ public class UserRestrictionsUtils { if (a == b) { return true; } - if (isEmpty(a)) { - return isEmpty(b); + if (BundleUtils.isEmpty(a)) { + return BundleUtils.isEmpty(b); } - if (isEmpty(b)) { + if (BundleUtils.isEmpty(b)) { return false; } for (String key : a.keySet()) { diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java index 5fa46b9e4635..17ce386e3770 100644 --- a/services/core/java/com/android/server/pm/UserTypeDetails.java +++ b/services/core/java/com/android/server/pm/UserTypeDetails.java @@ -28,8 +28,12 @@ import android.os.Bundle; import android.os.UserManager; import com.android.internal.util.Preconditions; +import com.android.server.BundleUtils; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * Contains the details about a multiuser "user type", such as a @@ -82,6 +86,24 @@ public final class UserTypeDetails { */ private final @Nullable Bundle mDefaultRestrictions; + /** + * List of {@link android.provider.Settings.System} to apply by default to newly created users + * of this type. + */ + private final @Nullable Bundle mDefaultSystemSettings; + + /** + * List of {@link android.provider.Settings.Secure} to apply by default to newly created users + * of this type. + */ + private final @Nullable Bundle mDefaultSecureSettings; + + /** + * List of {@link DefaultCrossProfileIntentFilter} to allow by default for newly created + * profiles. + */ + private final @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters; + // Fields for profiles only, controlling the nature of their badges. // All badge information should be set if {@link #hasBadge()} is true. @@ -133,7 +155,10 @@ public final class UserTypeDetails { int iconBadge, int badgePlain, int badgeNoBackground, @Nullable int[] badgeLabels, @Nullable int[] badgeColors, @Nullable int[] darkThemeBadgeColors, - @Nullable Bundle defaultRestrictions) { + @Nullable Bundle defaultRestrictions, + @Nullable Bundle defaultSystemSettings, + @Nullable Bundle defaultSecureSettings, + @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters) { this.mName = name; this.mEnabled = enabled; this.mMaxAllowed = maxAllowed; @@ -141,6 +166,9 @@ public final class UserTypeDetails { this.mBaseType = baseType; this.mDefaultUserInfoPropertyFlags = defaultUserInfoPropertyFlags; this.mDefaultRestrictions = defaultRestrictions; + this.mDefaultSystemSettings = defaultSystemSettings; + this.mDefaultSecureSettings = defaultSecureSettings; + this.mDefaultCrossProfileIntentFilters = defaultCrossProfileIntentFilters; this.mIconBadge = iconBadge; this.mBadgePlain = badgePlain; @@ -263,9 +291,9 @@ public final class UserTypeDetails { return (mBaseType & UserInfo.FLAG_SYSTEM) != 0; } - /** Returns a Bundle representing the default user restrictions. */ + /** Returns a {@link Bundle} representing the default user restrictions. */ @NonNull Bundle getDefaultRestrictions() { - return UserRestrictionsUtils.clone(mDefaultRestrictions); + return BundleUtils.clone(mDefaultRestrictions); } /** Adds the default user restrictions to the given bundle of restrictions. */ @@ -273,6 +301,24 @@ public final class UserTypeDetails { UserRestrictionsUtils.merge(currentRestrictions, mDefaultRestrictions); } + /** Returns a {@link Bundle} representing the default system settings. */ + @NonNull Bundle getDefaultSystemSettings() { + return BundleUtils.clone(mDefaultSystemSettings); + } + + /** Returns a {@link Bundle} representing the default secure settings. */ + @NonNull Bundle getDefaultSecureSettings() { + return BundleUtils.clone(mDefaultSecureSettings); + } + + /** Returns a list of default cross profile intent filters. */ + @NonNull List<DefaultCrossProfileIntentFilter> getDefaultCrossProfileIntentFilters() { + return mDefaultCrossProfileIntentFilters != null + ? new ArrayList<>(mDefaultCrossProfileIntentFilters) + : Collections.emptyList(); + } + + /** Dumps details of the UserTypeDetails. Do not parse this. */ public void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mName: "); pw.println(mName); @@ -325,6 +371,10 @@ public final class UserTypeDetails { private int mMaxAllowedPerParent = UNLIMITED_NUMBER_OF_USERS; private int mDefaultUserInfoPropertyFlags = 0; private @Nullable Bundle mDefaultRestrictions = null; + private @Nullable Bundle mDefaultSystemSettings = null; + private @Nullable Bundle mDefaultSecureSettings = null; + private @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters = + null; private boolean mEnabled = true; private int mLabel = Resources.ID_NULL; private @Nullable int[] mBadgeLabels = null; @@ -407,6 +457,22 @@ public final class UserTypeDetails { return this; } + public Builder setDefaultSystemSettings(@Nullable Bundle settings) { + mDefaultSystemSettings = settings; + return this; + } + + public Builder setDefaultSecureSettings(@Nullable Bundle settings) { + mDefaultSecureSettings = settings; + return this; + } + + public Builder setDefaultCrossProfileIntentFilters( + @Nullable List<DefaultCrossProfileIntentFilter> intentFilters) { + mDefaultCrossProfileIntentFilters = intentFilters; + return this; + } + @UserInfoFlag int getBaseType() { return mBaseType; } @@ -425,17 +491,28 @@ public final class UserTypeDetails { Preconditions.checkArgument(mBadgeColors != null && mBadgeColors.length != 0, "UserTypeDetails " + mName + " has badge but no badgeColors."); } + if (!isProfile()) { + Preconditions.checkArgument(mDefaultCrossProfileIntentFilters == null + || mDefaultCrossProfileIntentFilters.isEmpty(), + "UserTypeDetails %s has a non empty " + + "defaultCrossProfileIntentFilters", mName); + } return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType, mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent, mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors, mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors, - mDefaultRestrictions); + mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings, + mDefaultCrossProfileIntentFilters); } private boolean hasBadge() { return mIconBadge != Resources.ID_NULL; } + private boolean isProfile() { + return (mBaseType & UserInfo.FLAG_PROFILE) != 0; + } + // TODO(b/143784345): Refactor this when we clean up UserInfo. private boolean hasValidBaseType() { return mBaseType == UserInfo.FLAG_FULL diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java index 1ffbf608c6d7..6aac0b2f3d0f 100644 --- a/services/core/java/com/android/server/pm/UserTypeFactory.java +++ b/services/core/java/com/android/server/pm/UserTypeFactory.java @@ -133,7 +133,9 @@ public final class UserTypeFactory { com.android.internal.R.color.profile_badge_1_dark, com.android.internal.R.color.profile_badge_2_dark, com.android.internal.R.color.profile_badge_3_dark) - .setDefaultRestrictions(null); + .setDefaultRestrictions(getDefaultManagedProfileRestrictions()) + .setDefaultSecureSettings(getDefaultManagedProfileSecureSettings()) + .setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter()); } /** @@ -256,12 +258,35 @@ public final class UserTypeFactory { return restrictions; } + private static Bundle getDefaultManagedProfileRestrictions() { + final Bundle restrictions = new Bundle(); + restrictions.putBoolean(UserManager.DISALLOW_WALLPAPER, true); + return restrictions; + } + + private static Bundle getDefaultManagedProfileSecureSettings() { + // Only add String values to the bundle, settings are written as Strings eventually + final Bundle settings = new Bundle(); + settings.putString( + android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, "1"); + settings.putString( + android.provider.Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, "1"); + return settings; + } + + private static List<DefaultCrossProfileIntentFilter> + getDefaultManagedCrossProfileIntentFilter() { + return DefaultCrossProfileIntentFiltersUtils.getDefaultManagedProfileFilters(); + } + /** * Reads the given xml parser to obtain device user-type customization, and updates the given * map of {@link UserTypeDetails.Builder}s accordingly. * <p> * The xml file can specify the attributes according to the set... methods below. */ + // TODO(b/176973369): Add parsing logic to support custom settings/filters + // in config_user_types.xml @VisibleForTesting static void customizeBuilders(ArrayMap<String, UserTypeDetails.Builder> builders, XmlResourceParser parser) { diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java index af9978b91e48..1925590112f8 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java @@ -32,15 +32,30 @@ import android.util.SparseArray; import com.android.internal.util.CollectionUtils; import com.android.server.pm.PackageSetting; +import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.verify.domain.models.DomainVerificationPkgState; import com.android.server.pm.verify.domain.models.DomainVerificationStateMap; import com.android.server.pm.verify.domain.models.DomainVerificationUserState; -import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.Arrays; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +@SuppressWarnings("PointlessBooleanExpression") public class DomainVerificationDebug { + // Disable to turn off all logging. This is used to allow a "basic" set of debug flags to be + // enabled and checked in, without having everything be on or off. + public static final boolean DEBUG_ANY = false; + + // Enable to turn on all logging. Requires enabling DEBUG_ANY. + public static final boolean DEBUG_ALL = false; + + public static final boolean DEBUG_APPROVAL = DEBUG_ANY && (DEBUG_ALL || true); + public static final boolean DEBUG_BROADCASTS = DEBUG_ANY && (DEBUG_ALL || false); + public static final boolean DEBUG_PROXIES = DEBUG_ANY && (DEBUG_ALL || false); + @NonNull private final DomainVerificationCollector mCollector; @@ -50,7 +65,7 @@ public class DomainVerificationDebug { public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName, @Nullable @UserIdInt Integer userId, - @NonNull DomainVerificationService.Connection connection, + @NonNull Function<String, PackageSetting> pkgSettingFunction, @NonNull DomainVerificationStateMap<DomainVerificationPkgState> stateMap) throws NameNotFoundException { ArrayMap<String, Integer> reusedMap = new ArrayMap<>(); @@ -61,7 +76,7 @@ public class DomainVerificationDebug { for (int index = 0; index < size; index++) { DomainVerificationPkgState pkgState = stateMap.valueAt(index); String pkgName = pkgState.getPackageName(); - PackageSetting pkgSetting = connection.getPackageSettingLocked(pkgName); + PackageSetting pkgSetting = pkgSettingFunction.apply(pkgName); if (pkgSetting == null || pkgSetting.getPkg() == null) { continue; } @@ -77,7 +92,7 @@ public class DomainVerificationDebug { throw DomainVerificationUtils.throwPackageUnavailable(packageName); } - PackageSetting pkgSetting = connection.getPackageSettingLocked(packageName); + PackageSetting pkgSetting = pkgSettingFunction.apply(packageName); if (pkgSetting == null || pkgSetting.getPkg() == null) { throw DomainVerificationUtils.throwPackageUnavailable(packageName); } diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java index 7ad275a6f351..0474d78a3e53 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java @@ -32,15 +32,16 @@ import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import com.android.server.pm.PackageSetting; +import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.verify.domain.models.DomainVerificationPkgState; import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; -import com.android.server.pm.parsing.pkg.AndroidPackage; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.Set; import java.util.UUID; +import java.util.function.Function; public interface DomainVerificationManagerInternal extends DomainVerificationManager { @@ -191,12 +192,21 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan /** * Print the verification state and user selection state of a package. * - * @param packageName the package whose state to change, or all packages if none is specified - * @param userId the specific user to print, or null to skip printing user selection - * states, supports {@link android.os.UserHandle#USER_ALL} + * @param packageName the package whose state to change, or all packages if none is + * specified + * @param userId the specific user to print, or null to skip printing user selection + * states, supports {@link android.os.UserHandle#USER_ALL} + * @param pkgSettingFunction the method by which to retrieve package data; if this is called + * from {@link com.android.server.pm.PackageManagerService}, it is + * expected to pass in the snapshot of {@link PackageSetting} objects, + * or if null is passed, the manager may decide to lock {@link + * com.android.server.pm.PackageManagerService} through {@link + * Connection#getPackageSettingLocked(String)} */ void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName, - @Nullable @UserIdInt Integer userId) throws NameNotFoundException; + @Nullable @UserIdInt Integer userId, + @Nullable Function<String, PackageSetting> pkgSettingFunction) + throws NameNotFoundException; @NonNull DomainVerificationShell getShell(); @@ -225,7 +235,7 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan throws IllegalArgumentException, NameNotFoundException; - interface Connection { + interface Connection extends Function<String, PackageSetting> { /** * Notify that a settings change has been made and that eventually @@ -249,10 +259,19 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan */ void schedule(int code, @Nullable Object object); + // TODO(b/178733426): Make DomainVerificationService PMS snapshot aware so it can avoid + // locking package state at all. This can be as simple as removing this method in favor of + // accepting a PackageSetting function in at every method call, although should probably + // be abstracted to a wrapper class. @Nullable PackageSetting getPackageSettingLocked(@NonNull String pkgName); @Nullable AndroidPackage getPackageLocked(@NonNull String pkgName); + + @Override + default PackageSetting apply(@NonNull String pkgName) { + return getPackageSettingLocked(pkgName); + } } } diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java index 53540c8e0d4f..e24e5bbfa4f6 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java @@ -64,13 +64,14 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.function.Function; public class DomainVerificationService extends SystemService implements DomainVerificationManagerInternal, DomainVerificationShell.Callback { private static final String TAG = "DomainVerificationService"; - public static final boolean DEBUG_APPROVAL = true; + public static final boolean DEBUG_APPROVAL = DomainVerificationDebug.DEBUG_APPROVAL; /** * The new user preference API for verifying domains marked autoVerify=true in @@ -541,8 +542,6 @@ public class DomainVerificationService extends SystemService userState.removeHosts(domains); } } - - mConnection.scheduleWriteSettings(); } @Nullable @@ -848,6 +847,7 @@ public class DomainVerificationService extends SystemService public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) { mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid()); mLegacySettings.add(packageName, userId, state); + mConnection.scheduleWriteSettings(); } @Override @@ -890,9 +890,24 @@ public class DomainVerificationService extends SystemService @Override public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName, - @Nullable @UserIdInt Integer userId) throws NameNotFoundException { + @Nullable Integer userId) throws NameNotFoundException { + // This method is only used by DomainVerificationShell, which doesn't lock PMS, so it's + // safe to pass mConnection directly here and lock PMS. This method is not exposed + // to the general system server/PMS. + printState(writer, packageName, userId, mConnection); + } + + @Override + public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName, + @Nullable @UserIdInt Integer userId, + @Nullable Function<String, PackageSetting> pkgSettingFunction) + throws NameNotFoundException { + if (pkgSettingFunction == null) { + pkgSettingFunction = mConnection; + } + synchronized (mLock) { - mDebug.printState(writer, packageName, userId, mConnection, mAttachedPkgStates); + mDebug.printState(writer, packageName, userId, pkgSettingFunction, mAttachedPkgStates); } } @@ -1051,6 +1066,8 @@ public class DomainVerificationService extends SystemService } } } + + mConnection.scheduleWriteSettings(); } /** @@ -1108,6 +1125,8 @@ public class DomainVerificationService extends SystemService } } } + + mConnection.scheduleWriteSettings(); } @Override diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java index 715d8fb0fc2d..09abdd092648 100644 --- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java +++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java @@ -23,9 +23,10 @@ import android.content.Context; import android.util.Slog; import com.android.server.DeviceIdleInternal; -import com.android.server.pm.verify.domain.DomainVerificationMessageCodes; import com.android.server.pm.verify.domain.DomainVerificationCollector; +import com.android.server.pm.verify.domain.DomainVerificationDebug; import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationMessageCodes; import java.util.Objects; import java.util.Set; @@ -35,7 +36,7 @@ public interface DomainVerificationProxy { String TAG = "DomainVerificationProxy"; - boolean DEBUG_PROXIES = false; + boolean DEBUG_PROXIES = DomainVerificationDebug.DEBUG_PROXIES; static <ConnectionType extends DomainVerificationProxyV1.Connection & DomainVerificationProxyV2.Connection> DomainVerificationProxy makeProxy( diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java index eab89e987885..9389e63404f4 100644 --- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java +++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java @@ -37,10 +37,11 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.verify.domain.DomainVerificationCollector; +import com.android.server.pm.verify.domain.DomainVerificationDebug; import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; import com.android.server.pm.verify.domain.DomainVerificationMessageCodes; -import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.Collections; import java.util.List; @@ -52,7 +53,7 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy { private static final String TAG = "DomainVerificationProxyV1"; - private static final boolean DEBUG_BROADCASTS = false; + private static final boolean DEBUG_BROADCASTS = DomainVerificationDebug.DEBUG_BROADCASTS; @NonNull private final Context mContext; @@ -152,18 +153,22 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy { UUID domainSetId = pair.first; String packageName = pair.second; - DomainVerificationInfo set; + DomainVerificationInfo info; try { - set = mManager.getDomainVerificationInfo(packageName); + info = mManager.getDomainVerificationInfo(packageName); } catch (PackageManager.NameNotFoundException ignored) { return true; } - if (!Objects.equals(domainSetId, set.getIdentifier())) { + if (info == null) { + return true; + } + + if (!Objects.equals(domainSetId, info.getIdentifier())) { return true; } - Set<String> successfulDomains = new ArraySet<>(set.getHostToStateMap().keySet()); + Set<String> successfulDomains = new ArraySet<>(info.getHostToStateMap().keySet()); successfulDomains.removeAll(response.failedDomains); int callingUid = response.callingUid; diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java index 9fcbce2ad055..1ef06036021e 100644 --- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java +++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java @@ -28,6 +28,7 @@ import android.os.Process; import android.os.UserHandle; import android.util.Slog; +import com.android.server.pm.verify.domain.DomainVerificationDebug; import com.android.server.pm.verify.domain.DomainVerificationMessageCodes; import java.util.Set; @@ -36,7 +37,7 @@ public class DomainVerificationProxyV2 implements DomainVerificationProxy { private static final String TAG = "DomainVerificationProxyV2"; - private static final boolean DEBUG_BROADCASTS = true; + private static final boolean DEBUG_BROADCASTS = DomainVerificationDebug.DEBUG_BROADCASTS; @NonNull private final Context mContext; diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java index c10e828d8c3d..82fc22c51afc 100644 --- a/services/core/java/com/android/server/policy/DisplayFoldController.java +++ b/services/core/java/com/android/server/policy/DisplayFoldController.java @@ -74,8 +74,8 @@ class DisplayFoldController { mHandler = handler; DeviceStateManager deviceStateManager = context.getSystemService(DeviceStateManager.class); - deviceStateManager.registerDeviceStateListener(new DeviceStateListener(context), - new HandlerExecutor(handler)); + deviceStateManager.addDeviceStateListener(new HandlerExecutor(handler), + new DeviceStateListener(context)); } void finishedGoingToSleep() { diff --git a/services/core/java/com/android/server/policy/IconUtilities.java b/services/core/java/com/android/server/policy/IconUtilities.java deleted file mode 100644 index 884d7d497a8f..000000000000 --- a/services/core/java/com/android/server/policy/IconUtilities.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.policy; - -import android.graphics.ColorFilter; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.PaintDrawable; -import android.graphics.drawable.StateListDrawable; -import android.graphics.Bitmap; -import android.graphics.BlurMaskFilter; -import android.graphics.Canvas; -import android.graphics.ColorMatrix; -import android.graphics.Paint; -import android.graphics.PaintFlagsDrawFilter; -import android.graphics.PorterDuff; -import android.graphics.Rect; -import android.graphics.TableMaskFilter; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.content.res.Resources; -import android.content.Context; - -/** - * Various utilities shared amongst the Launcher's classes. - */ -public final class IconUtilities { - - private int mIconWidth = -1; - private int mIconHeight = -1; - private int mIconTextureWidth = -1; - private int mIconTextureHeight = -1; - - private final Rect mOldBounds = new Rect(); - private final Canvas mCanvas = new Canvas(); - private final DisplayMetrics mDisplayMetrics; - - private ColorFilter mDisabledColorFilter; - - public IconUtilities(Context context) { - final Resources resources = context.getResources(); - DisplayMetrics metrics = mDisplayMetrics = resources.getDisplayMetrics(); - final float density = metrics.density; - final float blurPx = 5 * density; - - mIconWidth = mIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size); - mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2); - mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, - Paint.FILTER_BITMAP_FLAG)); - } - - /** - * Returns a bitmap suitable for the all apps view. The bitmap will be a power - * of two sized ARGB_8888 bitmap that can be used as a gl texture. - */ - public Bitmap createIconBitmap(Drawable icon) { - int width = mIconWidth; - int height = mIconHeight; - - if (icon instanceof PaintDrawable) { - PaintDrawable painter = (PaintDrawable) icon; - painter.setIntrinsicWidth(width); - painter.setIntrinsicHeight(height); - } else if (icon instanceof BitmapDrawable) { - // Ensure the bitmap has a density. - BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; - Bitmap bitmap = bitmapDrawable.getBitmap(); - if (bitmap.getDensity() == Bitmap.DENSITY_NONE) { - bitmapDrawable.setTargetDensity(mDisplayMetrics); - } - } - int sourceWidth = icon.getIntrinsicWidth(); - int sourceHeight = icon.getIntrinsicHeight(); - - if (sourceWidth > 0 && sourceHeight > 0) { - // There are intrinsic sizes. - if (width < sourceWidth || height < sourceHeight) { - // It's too big, scale it down. - final float ratio = (float) sourceWidth / sourceHeight; - if (sourceWidth > sourceHeight) { - height = (int) (width / ratio); - } else if (sourceHeight > sourceWidth) { - width = (int) (height * ratio); - } - } else if (sourceWidth < width && sourceHeight < height) { - // It's small, use the size they gave us. - width = sourceWidth; - height = sourceHeight; - } - } - - // no intrinsic size --> use default size - int textureWidth = mIconTextureWidth; - int textureHeight = mIconTextureHeight; - - final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight, - Bitmap.Config.ARGB_8888); - final Canvas canvas = mCanvas; - canvas.setBitmap(bitmap); - - final int left = (textureWidth-width) / 2; - final int top = (textureHeight-height) / 2; - - mOldBounds.set(icon.getBounds()); - icon.setBounds(left, top, left+width, top+height); - icon.draw(canvas); - icon.setBounds(mOldBounds); - - return bitmap; - } - - public ColorFilter getDisabledColorFilter() { - if (mDisabledColorFilter != null) { - return mDisabledColorFilter; - } - ColorMatrix brightnessMatrix = new ColorMatrix(); - float brightnessF = 0.5f; - int brightnessI = (int) (255 * brightnessF); - // Brightness: C-new = C-old*(1-amount) + amount - float scale = 1f - brightnessF; - float[] mat = brightnessMatrix.getArray(); - mat[0] = scale; - mat[6] = scale; - mat[12] = scale; - mat[4] = brightnessI; - mat[9] = brightnessI; - mat[14] = brightnessI; - - ColorMatrix filterMatrix = new ColorMatrix(); - filterMatrix.setSaturation(0); - filterMatrix.preConcat(brightnessMatrix); - - mDisabledColorFilter = new ColorMatrixColorFilter(filterMatrix); - return mDisabledColorFilter; - } -} diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index a407e8e1b7df..bc819617332d 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -222,6 +222,7 @@ import com.android.server.wm.DisplayPolicy; import com.android.server.wm.DisplayRotation; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.AppTransitionListener; +import com.android.server.wm.WindowManagerService; import java.io.File; import java.io.FileNotFoundException; @@ -1913,6 +1914,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { handleStartTransitionForKeyguardLw(keyguardGoingAway, 0 /* duration */); } }); + mKeyguardDelegate = new KeyguardServiceDelegate(mContext, new StateCallback() { @Override @@ -3136,7 +3138,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) { final int res = applyKeyguardOcclusionChange(); if (res != 0) return res; - if (keyguardGoingAway) { + if (!WindowManagerService.sEnableRemoteKeyguardAnimation && keyguardGoingAway) { if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation"); startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration); } diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index e9d64406432a..db33e750d803 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -417,8 +417,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { WindowManagerFuncs windowManagerFuncs); /** - * Check permissions when adding a window or a window token from - * {@link android.app.WindowContext}. + * Check permissions when adding a window. * * @param type The window type * @param isRoundedCornerOverlay {@code true} to indicate the adding window is @@ -431,7 +430,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * else an error code, usually * {@link WindowManagerGlobal#ADD_PERMISSION_DENIED}, to abort the add. * - * @see IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle, String) * @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY */ int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName, @@ -1158,7 +1156,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * @param startTime the start time of the animation in uptime milliseconds * @param fadeoutDuration the duration of the exit animation, in milliseconds */ - public void startKeyguardExitAnimation(long startTime, long fadeoutDuration); + void startKeyguardExitAnimation(long startTime, long fadeoutDuration); /** * Called when System UI has been started. diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index c2a1c7930b89..a95628f633ad 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -29,6 +29,7 @@ import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardService; import com.android.server.UiThread; import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult; +import com.android.server.wm.WindowManagerService; import java.io.PrintWriter; @@ -398,7 +399,7 @@ public class KeyguardServiceDelegate { } public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { - if (mKeyguardService != null) { + if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) { mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration); } } diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java index 57e39b6c6829..b995b19c5841 100644 --- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java +++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java @@ -45,6 +45,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.infra.ServiceConnector; +import java.lang.ref.WeakReference; + /** Manages the connection to the remote rotation resolver service. */ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResolverService> { @@ -128,13 +130,20 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol mProposedRotation = proposedRotation; mCurrentRotation = currentRotation; mPackageName = packageName; - mIRotationResolverCallback = new RotationResolverCallback(); + mIRotationResolverCallback = new RotationResolverCallback(this); mCancellationSignalInternal = cancellationSignal; mRequestStartTimeMillis = SystemClock.elapsedRealtime(); } void cancelInternal() { + synchronized (mLock) { + if (mIsFulfilled) { + Slog.v(TAG, "Trying to cancel the request that has been already fulfilled."); + return; + } + mIsFulfilled = true; + } Handler.getMain().post(() -> { synchronized (mLock) { try { @@ -147,9 +156,6 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol } } }); - synchronized (mLock) { - mIsFulfilled = true; - } mCallbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED); } @@ -160,44 +166,53 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol ipw.decreaseIndent(); } - private class RotationResolverCallback extends IRotationResolverCallback.Stub { + private static class RotationResolverCallback extends IRotationResolverCallback.Stub { + private WeakReference<RotationRequest> mRequestWeakReference; + + RotationResolverCallback(RotationRequest request) { + this.mRequestWeakReference = new WeakReference<>(request); + } + @Override public void onSuccess(int rotation) { - synchronized (mLock) { - if (mIsFulfilled) { + final RotationRequest request = mRequestWeakReference.get(); + synchronized (request.mLock) { + if (request.mIsFulfilled) { Slog.w(TAG, "Callback received after the rotation request is fulfilled."); return; } - mIsFulfilled = true; - mCallbackInternal.onSuccess(rotation); + request.mIsFulfilled = true; + request.mCallbackInternal.onSuccess(rotation); final long timeToCalculate = - SystemClock.elapsedRealtime() - mRequestStartTimeMillis; - logRotationStats(mProposedRotation, mCurrentRotation, rotation, + SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis; + logRotationStats(request.mProposedRotation, request.mCurrentRotation, rotation, timeToCalculate); } } @Override public void onFailure(int error) { - synchronized (mLock) { - if (mIsFulfilled) { + final RotationRequest request = mRequestWeakReference.get(); + synchronized (request.mLock) { + if (request.mIsFulfilled) { Slog.w(TAG, "Callback received after the rotation request is fulfilled."); return; } - mIsFulfilled = true; - mCallbackInternal.onFailure(error); + request.mIsFulfilled = true; + request.mCallbackInternal.onFailure(error); final long timeToCalculate = - SystemClock.elapsedRealtime() - mRequestStartTimeMillis; - logRotationStats(mProposedRotation, mCurrentRotation, RESOLUTION_FAILURE, - timeToCalculate); + SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis; + logRotationStats(request.mProposedRotation, request.mCurrentRotation, + RESOLUTION_FAILURE, timeToCalculate); } } @Override public void onCancellable(@NonNull ICancellationSignal cancellation) { - synchronized (mLock) { - mCancellation = cancellation; - if (mCancellationSignalInternal.isCanceled()) { + final RotationRequest request = mRequestWeakReference.get(); + synchronized (request.mLock) { + request.mCancellation = cancellation; + if (request.mCancellationSignalInternal.isCanceled()) { // Dispatch the cancellation signal if the client has cancelled the request. try { cancellation.cancel(); diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java index 3dbc32ad54d8..6f7c016cb3f6 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java @@ -122,14 +122,9 @@ final class RotationResolverManagerPerUserService extends } }); - if (mRemoteService != null) { - mRemoteService.resolveRotationLocked(mCurrentRequest); - mCurrentRequest.mIsDispatched = true; - } else { - Slog.w(TAG, "Remote service is not available at this moment."); - callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED); - cancelLocked(); - } + + mRemoteService.resolveRotationLocked(mCurrentRequest); + mCurrentRequest.mIsDispatched = true; } @GuardedBy("mLock") @@ -198,15 +193,6 @@ final class RotationResolverManagerPerUserService extends if (mCurrentRequest == null) { return; } - - if (mCurrentRequest.mIsFulfilled) { - if (isVerbose()) { - Slog.d(TAG, "Trying to cancel the request that has been already fulfilled."); - } - mCurrentRequest = null; - return; - } - mCurrentRequest.cancelInternal(); mCurrentRequest = null; } diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java index 4a37e7960912..57cf986842da 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java @@ -150,7 +150,7 @@ public class RotationResolverManagerService extends @Override public void resolveRotation( @NonNull RotationResolverCallbackInternal callbackInternal, int proposedRotation, - int currentRotation, String packageName, long timeout, + int currentRotation, long timeout, @NonNull CancellationSignal cancellationSignalInternal) { Objects.requireNonNull(callbackInternal); Objects.requireNonNull(cancellationSignalInternal); @@ -159,7 +159,8 @@ public class RotationResolverManagerService extends final RotationResolverManagerPerUserService service = getServiceForUserLocked( UserHandle.getCallingUserId()); service.resolveRotationLocked(callbackInternal, proposedRotation, - currentRotation, packageName, timeout, cancellationSignalInternal); + currentRotation, /* packageName */ "", timeout, + cancellationSignalInternal); } else { Slog.w(TAG, "Rotation Resolver service is disabled."); callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED); @@ -191,7 +192,7 @@ public class RotationResolverManagerService extends TAG); final RotationResolverManagerPerUserService service = getServiceForUserLocked( UserHandle.getCallingUserId()); - new RotationResolverShellCommend(service).exec(this, in, out, err, args, callback, + new RotationResolverShellCommand(service).exec(this, in, out, err, args, callback, resultReceiver); } } diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java index 0a873892b5bf..54a9edba4e03 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java @@ -26,13 +26,13 @@ import android.view.Surface; import java.io.PrintWriter; -final class RotationResolverShellCommend extends ShellCommand { +final class RotationResolverShellCommand extends ShellCommand { private static final int INITIAL_RESULT_CODE = -1; @NonNull private final RotationResolverManagerPerUserService mService; - RotationResolverShellCommend(@NonNull RotationResolverManagerPerUserService service) { + RotationResolverShellCommand(@NonNull RotationResolverManagerPerUserService service) { mService = service; } 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 263776c63db6..5e681c674d8b 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -2509,7 +2509,6 @@ public class StatsPullAtomService extends SystemService { try { // force procstats to flush & combine old files into one store long lastHighWaterMark = readProcStatsHighWaterMark(section); - List<ParcelFileDescriptor> statsFiles = new ArrayList<>(); ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS]; for (int i = 0; i < protoStreams.length; i++) { @@ -2519,7 +2518,7 @@ public class StatsPullAtomService extends SystemService { ProcessStats procStats = new ProcessStats(false); // Force processStatsService to aggregate all in-storage and in-memory data. long highWaterMark = processStatsService.getCommittedStatsMerged( - lastHighWaterMark, section, true, statsFiles, procStats); + lastHighWaterMark, section, true, null, procStats); procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE); for (int i = 0; i < protoStreams.length; i++) { diff --git a/services/core/java/com/android/server/tracing/OWNERS b/services/core/java/com/android/server/tracing/OWNERS new file mode 100644 index 000000000000..f5de4eb05c54 --- /dev/null +++ b/services/core/java/com/android/server/tracing/OWNERS @@ -0,0 +1,2 @@ +cfijalkovich@google.com +carmenjackson@google.com diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java new file mode 100644 index 000000000000..8f227489740f --- /dev/null +++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java @@ -0,0 +1,99 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.tracing; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.UserHandle; +import android.tracing.ITracingServiceProxy; +import android.util.Log; + +import com.android.server.SystemService; + +/** + * TracingServiceProxy is the system_server intermediary between the Perfetto tracing daemon and the + * system tracing app Traceur. + * + * @hide + */ +public class TracingServiceProxy extends SystemService { + private static final String TAG = "TracingServiceProxy"; + + public static final String TRACING_SERVICE_PROXY_BINDER_NAME = "tracing.proxy"; + + private static final String TRACING_APP_PACKAGE_NAME = "com.android.traceur"; + private static final String TRACING_APP_ACTIVITY = "com.android.traceur.StopTraceService"; + + // Keep this in sync with the definitions in TraceService + private static final String INTENT_ACTION_NOTIFY_SESSION_STOPPED = + "com.android.traceur.NOTIFY_SESSION_STOPPED"; + private static final String INTENT_ACTION_NOTIFY_SESSION_STOLEN = + "com.android.traceur.NOTIFY_SESSION_STOLEN"; + + private final Context mContext; + private final PackageManager mPackageManager; + + private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() { + /** + * Notifies system tracing app that a tracing session has ended. If a session is repurposed + * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but + * there is no buffer available to dump. + */ + @Override + public void notifyTraceSessionEnded(boolean sessionStolen) { + notifyTraceur(sessionStolen); + } + }; + + public TracingServiceProxy(Context context) { + super(context); + mContext = context; + mPackageManager = context.getPackageManager(); + } + + @Override + public void onStart() { + publishBinderService(TRACING_SERVICE_PROXY_BINDER_NAME, mTracingServiceProxy); + } + + private void notifyTraceur(boolean sessionStolen) { + final Intent intent = new Intent(); + + try { + // Validate that Traceur is a system app. + PackageInfo info = mPackageManager.getPackageInfo(TRACING_APP_PACKAGE_NAME, + PackageManager.MATCH_SYSTEM_ONLY); + + intent.setClassName(info.packageName, TRACING_APP_ACTIVITY); + if (sessionStolen) { + intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOLEN); + } else { + intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED); + } + + try { + mContext.startForegroundServiceAsUser(intent, UserHandle.SYSTEM); + } catch (RuntimeException e) { + Log.e(TAG, "Failed to notifyTraceSessionEnded", e); + } + } catch (NameNotFoundException e) { + Log.e(TAG, "Failed to locate Traceur", e); + } + } +} diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index a82f239948ff..3726407211d5 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -16,6 +16,7 @@ package com.android.server.vcn; +import static com.android.server.VcnManagementService.VDBG; import android.annotation.NonNull; import android.net.NetworkCapabilities; @@ -29,6 +30,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.server.VcnManagementService.VcnSafemodeCallback; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import java.util.Collections; @@ -37,6 +39,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; /** * Represents an single instance of a VCN. @@ -82,10 +85,19 @@ public class Vcn extends Handler { /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */ private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE; + /** + * Causes this VCN to immediately enter Safemode. + * + * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its + * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode. + */ + private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1; + @NonNull private final VcnContext mVcnContext; @NonNull private final ParcelUuid mSubscriptionGroup; @NonNull private final Dependencies mDeps; @NonNull private final VcnNetworkRequestListener mRequestListener; + @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback; @NonNull private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections = @@ -94,14 +106,33 @@ public class Vcn extends Handler { @NonNull private VcnConfig mConfig; @NonNull private TelephonySubscriptionSnapshot mLastSnapshot; - private boolean mIsRunning = true; + /** + * Whether this Vcn instance is active and running. + * + * <p>The value will be {@code true} while running. It will be {@code false} if the VCN has been + * shut down or has entered safe mode. + * + * <p>This AtomicBoolean is required in order to ensure consistency and correctness across + * multiple threads. Unlike the rest of the Vcn, this is queried synchronously on Binder threads + * from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN + * Looper. + */ + // TODO(b/179429339): update when exiting safemode (when a new VcnConfig is provided) + private final AtomicBoolean mIsActive = new AtomicBoolean(true); public Vcn( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, - @NonNull TelephonySubscriptionSnapshot snapshot) { - this(vcnContext, subscriptionGroup, config, snapshot, new Dependencies()); + @NonNull TelephonySubscriptionSnapshot snapshot, + @NonNull VcnSafemodeCallback vcnSafemodeCallback) { + this( + vcnContext, + subscriptionGroup, + config, + snapshot, + vcnSafemodeCallback, + new Dependencies()); } @VisibleForTesting(visibility = Visibility.PRIVATE) @@ -110,10 +141,13 @@ public class Vcn extends Handler { @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, + @NonNull VcnSafemodeCallback vcnSafemodeCallback, @NonNull Dependencies deps) { super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); mVcnContext = vcnContext; mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); + mVcnSafemodeCallback = + Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback"); mDeps = Objects.requireNonNull(deps, "Missing deps"); mRequestListener = new VcnNetworkRequestListener(); @@ -143,6 +177,11 @@ public class Vcn extends Handler { sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN)); } + /** Synchronously checks whether this Vcn is active. */ + public boolean isActive() { + return mIsActive.get(); + } + /** Get current Gateways for testing purposes */ @VisibleForTesting(visibility = Visibility.PRIVATE) public Set<VcnGatewayConnection> getVcnGatewayConnections() { @@ -160,7 +199,7 @@ public class Vcn extends Handler { @Override public void handleMessage(@NonNull Message msg) { - if (!mIsRunning) { + if (!isActive()) { return; } @@ -177,6 +216,9 @@ public class Vcn extends Handler { case MSG_CMD_TEARDOWN: handleTeardown(); break; + case MSG_CMD_ENTER_SAFEMODE: + handleEnterSafemode(); + break; default: Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what); } @@ -184,7 +226,7 @@ public class Vcn extends Handler { private void handleConfigUpdated(@NonNull VcnConfig config) { // TODO: Add a dump function in VcnConfig that omits PII. Until then, use hashCode() - Slog.v(getLogTag(), String.format("Config updated: config = %s", config.hashCode())); + Slog.v(getLogTag(), "Config updated: config = " + config.hashCode()); mConfig = config; @@ -198,23 +240,41 @@ public class Vcn extends Handler { gatewayConnection.teardownAsynchronously(); } - mIsRunning = false; + mIsActive.set(false); + } + + private void handleEnterSafemode() { + handleTeardown(); + + mVcnSafemodeCallback.onEnteredSafemode(); } private void handleNetworkRequested( @NonNull NetworkRequest request, int score, int providerId) { if (score > getNetworkScore()) { - Slog.v(getLogTag(), - "Request already satisfied by higher-scoring (" + score + ") network from " - + "provider " + providerId + ": " + request); + if (VDBG) { + Slog.v( + getLogTag(), + "Request already satisfied by higher-scoring (" + + score + + ") network from " + + "provider " + + providerId + + ": " + + request); + } return; } // If preexisting VcnGatewayConnection(s) satisfy request, return for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) { if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { - Slog.v(getLogTag(), - "Request already satisfied by existing VcnGatewayConnection: " + request); + if (VDBG) { + Slog.v( + getLogTag(), + "Request already satisfied by existing VcnGatewayConnection: " + + request); + } return; } } @@ -233,7 +293,8 @@ public class Vcn extends Handler { mVcnContext, mSubscriptionGroup, mLastSnapshot, - gatewayConnectionConfig); + gatewayConnectionConfig, + new VcnGatewayStatusCallbackImpl()); mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection); } } @@ -242,7 +303,7 @@ public class Vcn extends Handler { private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) { mLastSnapshot = snapshot; - if (mIsRunning) { + if (isActive()) { for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) { gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot); } @@ -260,7 +321,7 @@ public class Vcn extends Handler { } private String getLogTag() { - return String.format("%s [%d]", TAG, mSubscriptionGroup.hashCode()); + return TAG + " [" + mSubscriptionGroup.hashCode() + "]"; } /** Retrieves the network score for a VCN Network */ @@ -271,6 +332,20 @@ public class Vcn extends Handler { return 52; } + /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */ + @VisibleForTesting(visibility = Visibility.PACKAGE) + public interface VcnGatewayStatusCallback { + /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */ + void onEnteredSafemode(); + } + + private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback { + @Override + public void onEnteredSafemode() { + sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE)); + } + } + /** External dependencies used by Vcn, for injection in tests */ @VisibleForTesting(visibility = Visibility.PRIVATE) public static class Dependencies { @@ -279,9 +354,14 @@ public class Vcn extends Handler { VcnContext vcnContext, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, - VcnGatewayConnectionConfig connectionConfig) { + VcnGatewayConnectionConfig connectionConfig, + VcnGatewayStatusCallback gatewayStatusCallback) { return new VcnGatewayConnection( - vcnContext, subscriptionGroup, snapshot, connectionConfig); + vcnContext, + subscriptionGroup, + snapshot, + connectionConfig, + gatewayStatusCallback); } } } diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 853bb4324f90..2503e812f9e1 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -61,7 +61,6 @@ import android.os.ParcelUuid; import android.util.ArraySet; import android.util.Slog; -import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.State; @@ -69,12 +68,14 @@ import com.android.internal.util.StateMachine; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback; +import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -297,9 +298,9 @@ public class VcnGatewayConnection extends StateMachine { private static final int EVENT_SETUP_COMPLETED = 6; private static class EventSetupCompletedInfo implements EventInfo { - @NonNull public final ChildSessionConfiguration childSessionConfig; + @NonNull public final VcnChildSessionConfiguration childSessionConfig; - EventSetupCompletedInfo(@NonNull ChildSessionConfiguration childSessionConfig) { + EventSetupCompletedInfo(@NonNull VcnChildSessionConfiguration childSessionConfig) { this.childSessionConfig = Objects.requireNonNull(childSessionConfig); } @@ -403,15 +404,13 @@ public class VcnGatewayConnection extends StateMachine { @NonNull final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState(); - @NonNull private final Object mLock = new Object(); - - @GuardedBy("mLock") @NonNull private TelephonySubscriptionSnapshot mLastSnapshot; @NonNull private final VcnContext mVcnContext; @NonNull private final ParcelUuid mSubscriptionGroup; @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker; @NonNull private final VcnGatewayConnectionConfig mConnectionConfig; + @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback; @NonNull private final Dependencies mDeps; @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback; @@ -473,7 +472,7 @@ public class VcnGatewayConnection extends StateMachine { * <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating * states, @Nullable otherwise. */ - private ChildSessionConfiguration mChildConfig; + private VcnChildSessionConfiguration mChildConfig; /** * The active network agent. @@ -487,8 +486,15 @@ public class VcnGatewayConnection extends StateMachine { @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, - @NonNull VcnGatewayConnectionConfig connectionConfig) { - this(vcnContext, subscriptionGroup, snapshot, connectionConfig, new Dependencies()); + @NonNull VcnGatewayConnectionConfig connectionConfig, + @NonNull VcnGatewayStatusCallback gatewayStatusCallback) { + this( + vcnContext, + subscriptionGroup, + snapshot, + connectionConfig, + gatewayStatusCallback, + new Dependencies()); } @VisibleForTesting(visibility = Visibility.PRIVATE) @@ -497,16 +503,17 @@ public class VcnGatewayConnection extends StateMachine { @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnGatewayConnectionConfig connectionConfig, + @NonNull VcnGatewayStatusCallback gatewayStatusCallback, @NonNull Dependencies deps) { super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); mVcnContext = vcnContext; mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig"); + mGatewayStatusCallback = + Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback"); mDeps = Objects.requireNonNull(deps, "Missing deps"); - synchronized (mLock) { - mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); - } + mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback(); @@ -577,13 +584,10 @@ public class VcnGatewayConnection extends StateMachine { */ public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) { Objects.requireNonNull(snapshot, "Missing snapshot"); + mVcnContext.ensureRunningOnLooperThread(); - // Vcn is the only user of this method and runs on the same Thread, but lock around - // mLastSnapshot to be technically correct. - synchronized (mLock) { - mLastSnapshot = snapshot; - mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot); - } + mLastSnapshot = snapshot; + mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot); sendMessage(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL); } @@ -656,7 +660,7 @@ public class VcnGatewayConnection extends StateMachine { new EventTransformCreatedInfo(direction, transform)); } - private void childOpened(int token, @NonNull ChildSessionConfiguration childConfig) { + private void childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig) { sendMessage(EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig)); } @@ -1005,7 +1009,7 @@ public class VcnGatewayConnection extends StateMachine { protected void updateNetworkAgent( @NonNull IpSecTunnelInterface tunnelIface, @NonNull NetworkAgent agent, - @NonNull ChildSessionConfiguration childConfig) { + @NonNull VcnChildSessionConfiguration childConfig) { final NetworkCapabilities caps = buildNetworkCapabilities(mConnectionConfig, mUnderlying); final LinkProperties lp = @@ -1017,7 +1021,7 @@ public class VcnGatewayConnection extends StateMachine { protected NetworkAgent buildNetworkAgent( @NonNull IpSecTunnelInterface tunnelIface, - @NonNull ChildSessionConfiguration childConfig) { + @NonNull VcnChildSessionConfiguration childConfig) { final NetworkCapabilities caps = buildNetworkCapabilities(mConnectionConfig, mUnderlying); final LinkProperties lp = @@ -1065,15 +1069,15 @@ public class VcnGatewayConnection extends StateMachine { protected void setupInterface( int token, @NonNull IpSecTunnelInterface tunnelIface, - @NonNull ChildSessionConfiguration childConfig) { + @NonNull VcnChildSessionConfiguration childConfig) { setupInterface(token, tunnelIface, childConfig, null); } protected void setupInterface( int token, @NonNull IpSecTunnelInterface tunnelIface, - @NonNull ChildSessionConfiguration childConfig, - @Nullable ChildSessionConfiguration oldChildConfig) { + @NonNull VcnChildSessionConfiguration childConfig, + @Nullable VcnChildSessionConfiguration oldChildConfig) { try { final Set<LinkAddress> newAddrs = new ArraySet<>(childConfig.getInternalAddresses()); @@ -1186,7 +1190,7 @@ public class VcnGatewayConnection extends StateMachine { protected void setupInterfaceAndNetworkAgent( int token, @NonNull IpSecTunnelInterface tunnelIface, - @NonNull ChildSessionConfiguration childConfig) { + @NonNull VcnChildSessionConfiguration childConfig) { setupInterface(token, tunnelIface, childConfig); if (mNetworkAgent == null) { @@ -1204,7 +1208,64 @@ public class VcnGatewayConnection extends StateMachine { */ class RetryTimeoutState extends ActiveBaseState { @Override - protected void processStateMsg(Message msg) {} + protected void enterState() throws Exception { + // Reset upon entry to ConnectedState + mFailedAttempts++; + + if (mUnderlying == null) { + Slog.wtf(TAG, "Underlying network was null in retry state"); + transitionTo(mDisconnectedState); + } else { + sendMessageDelayed( + EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken, getNextRetryIntervalsMs()); + } + } + + @Override + protected void processStateMsg(Message msg) { + switch (msg.what) { + case EVENT_UNDERLYING_NETWORK_CHANGED: + final UnderlyingNetworkRecord oldUnderlying = mUnderlying; + mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; + + // If new underlying is null, all networks were lost; go back to disconnected. + if (mUnderlying == null) { + removeMessages(EVENT_RETRY_TIMEOUT_EXPIRED); + + transitionTo(mDisconnectedState); + return; + } else if (oldUnderlying != null + && mUnderlying.network.equals(oldUnderlying.network)) { + // If the network has not changed, do nothing. + return; + } + + // Fallthrough + case EVENT_RETRY_TIMEOUT_EXPIRED: + removeMessages(EVENT_RETRY_TIMEOUT_EXPIRED); + + transitionTo(mConnectingState); + break; + case EVENT_DISCONNECT_REQUESTED: + handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); + break; + default: + logUnhandledMessage(msg); + break; + } + } + + private long getNextRetryIntervalsMs() { + final int retryDelayIndex = mFailedAttempts - 1; + final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs(); + + // Repeatedly use last item in retry timeout list. + if (retryDelayIndex >= retryIntervalsMs.length) { + return retryIntervalsMs[retryIntervalsMs.length - 1]; + } + + return retryIntervalsMs[retryDelayIndex]; + } } @VisibleForTesting(visibility = Visibility.PRIVATE) @@ -1274,7 +1335,7 @@ public class VcnGatewayConnection extends StateMachine { private static LinkProperties buildConnectedLinkProperties( @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig, @NonNull IpSecTunnelInterface tunnelIface, - @NonNull ChildSessionConfiguration childConfig) { + @NonNull VcnChildSessionConfiguration childConfig) { final LinkProperties lp = new LinkProperties(); lp.setInterfaceName(tunnelIface.getInterfaceName()); @@ -1325,20 +1386,28 @@ public class VcnGatewayConnection extends StateMachine { } } - private class ChildSessionCallbackImpl implements ChildSessionCallback { + /** Implementation of ChildSessionCallback, exposed for testing. */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public class VcnChildSessionCallback implements ChildSessionCallback { private final int mToken; - ChildSessionCallbackImpl(int token) { + VcnChildSessionCallback(int token) { mToken = token; } - @Override - public void onOpened(@NonNull ChildSessionConfiguration childConfig) { + /** Internal proxy method for injecting of mocked ChildSessionConfiguration */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + void onOpened(@NonNull VcnChildSessionConfiguration childConfig) { Slog.v(TAG, "ChildOpened for token " + mToken); childOpened(mToken, childConfig); } @Override + public void onOpened(@NonNull ChildSessionConfiguration childConfig) { + onOpened(new VcnChildSessionConfiguration(childConfig)); + } + + @Override public void onClosed() { Slog.v(TAG, "ChildClosed for token " + mToken); sessionLost(mToken, null); @@ -1418,7 +1487,7 @@ public class VcnGatewayConnection extends StateMachine { buildIkeParams(), buildChildParams(), new IkeSessionCallbackImpl(token), - new ChildSessionCallbackImpl(token)); + new VcnChildSessionCallback(token)); } /** External dependencies used by VcnGatewayConnection, for injection in tests */ @@ -1455,6 +1524,35 @@ public class VcnGatewayConnection extends StateMachine { } } + /** + * Proxy implementation of Child Session Configuration, used for testing. + * + * <p>This wrapper allows mocking of the final, parcelable ChildSessionConfiguration object for + * testing purposes. This is the unfortunate result of mockito-inline (for mocking final + * classes) not working properly with system services & associated classes. + * + * <p>This class MUST EXCLUSIVELY be a passthrough, proxying calls directly to the actual + * ChildSessionConfiguration. + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class VcnChildSessionConfiguration { + private final ChildSessionConfiguration mChildConfig; + + public VcnChildSessionConfiguration(ChildSessionConfiguration childConfig) { + mChildConfig = childConfig; + } + + /** Retrieves the addresses to be used inside the tunnel. */ + public List<LinkAddress> getInternalAddresses() { + return mChildConfig.getInternalAddresses(); + } + + /** Retrieves the DNS servers to be used inside the tunnel. */ + public List<InetAddress> getInternalDnsServers() { + return mChildConfig.getInternalDnsServers(); + } + } + /** Proxy implementation of IKE session, used for testing. */ @VisibleForTesting(visibility = Visibility.PRIVATE) public static class VcnIkeSession { diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java index fe4ea303610f..bfeec011a2c9 100644 --- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java +++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java @@ -16,6 +16,8 @@ package com.android.server.vcn; +import static com.android.server.VcnManagementService.VDBG; + import android.annotation.NonNull; import android.content.Context; import android.net.NetworkProvider; @@ -83,11 +85,16 @@ public class VcnNetworkProvider extends NetworkProvider { @Override public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) { - Slog.v( - TAG, - String.format( - "Network requested: Request = %s, score = %d, providerId = %d", - request, score, providerId)); + if (VDBG) { + Slog.v( + TAG, + "Network requested: Request = " + + request + + ", score = " + + score + + ", providerId = " + + providerId); + } final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId); diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 31984531d31f..5697564ce93f 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1094,7 +1094,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return; } if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken); - mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId); + mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId, + null /* options */); final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId); try { connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false, diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 2ecefeafcf5c..3bbc81a696e6 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -16,12 +16,27 @@ package com.android.server.wm; +import static android.os.Build.IS_USER; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; +import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY; +import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER; +import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_H; +import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_L; +import static com.android.server.accessibility.AccessibilityTraceProto.ACCESSIBILITY_SERVICE; +import static com.android.server.accessibility.AccessibilityTraceProto.CALENDAR_TIME; +import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PARAMS; +import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PKG; +import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_STACKS; +import static com.android.server.accessibility.AccessibilityTraceProto.ELAPSED_REALTIME_NANOS; +import static com.android.server.accessibility.AccessibilityTraceProto.PROCESS_NAME; +import static com.android.server.accessibility.AccessibilityTraceProto.THREAD_ID_NAME; +import static com.android.server.accessibility.AccessibilityTraceProto.WHERE; +import static com.android.server.accessibility.AccessibilityTraceProto.WINDOW_MANAGER_SERVICE; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.utils.RegionUtils.forEachRect; @@ -29,7 +44,9 @@ import static com.android.server.wm.utils.RegionUtils.forEachRect; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.NonNull; +import android.app.Application; import android.content.Context; +import android.content.pm.PackageManagerInternal; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; @@ -41,15 +58,20 @@ import android.graphics.PorterDuff.Mode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; +import android.os.Binder; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Process; +import android.os.SystemClock; import android.util.ArraySet; import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; import android.util.TypedValue; +import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.InsetsSource; import android.view.MagnificationSpec; @@ -64,13 +86,22 @@ import android.view.animation.Interpolator; import com.android.internal.R; import com.android.internal.os.SomeArgs; +import com.android.internal.util.TraceBuffer; +import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; +import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; +import java.io.File; +import java.io.IOException; import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -79,26 +110,37 @@ import java.util.Set; * This class contains the accessibility related logic of the window manager. */ final class AccessibilityController { + private static final String TAG = AccessibilityController.class.getSimpleName(); - private final WindowManagerService mService; + private static final Object STATIC_LOCK = new Object(); + static AccessibilityControllerInternal + getAccessibilityControllerInternal(WindowManagerService service) { + return AccessibilityControllerInternalImpl.getInstance(service); + } + private final AccessibilityTracing mAccessibilityTracing; + private final WindowManagerService mService; private static final Rect EMPTY_RECT = new Rect(); private static final float[] sTempFloats = new float[9]; - public AccessibilityController(WindowManagerService service) { + AccessibilityController(WindowManagerService service) { mService = service; + mAccessibilityTracing = AccessibilityTracing.getInstance(service); } private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>(); - private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver = new SparseArray<>(); // Set to true if initializing window population complete. private boolean mAllObserversInitialized = true; - public boolean setMagnificationCallbacksLocked(int displayId, - MagnificationCallbacks callbacks) { + boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".setMagnificationCallbacks", + "displayId=" + displayId + "; callbacks={" + callbacks + "}"); + } boolean result = false; if (callbacks != null) { if (mDisplayMagnifiers.get(displayId) != null) { @@ -118,7 +160,7 @@ final class AccessibilityController { if (displayMagnifier == null) { throw new IllegalStateException("Magnification callbacks already cleared!"); } - displayMagnifier.destroyLocked(); + displayMagnifier.destroy(); mDisplayMagnifiers.remove(displayId); result = true; } @@ -133,8 +175,13 @@ final class AccessibilityController { * @param callback The callback. * @return {@code false} if display id is not valid or an embedded display. */ - public boolean setWindowsForAccessibilityCallbackLocked(int displayId, + boolean setWindowsForAccessibilityCallback(int displayId, WindowsForAccessibilityCallback callback) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".setWindowsForAccessibilityCallback", + "displayId=" + displayId + "; callback={" + callback + "}"); + } final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId); if (dc == null) { return false; @@ -147,7 +194,7 @@ final class AccessibilityController { // empty, that means this mapping didn't be set, and needs to do this again. // This happened when accessibility window observer is disabled and enabled again. if (mWindowsForAccessibilityObserver.get(displayId) == null) { - handleWindowObserverOfEmbeddedDisplayLocked(displayId, dc.getParentWindow()); + handleWindowObserverOfEmbeddedDisplay(displayId, dc.getParentWindow()); } return false; } else if (mWindowsForAccessibilityObserver.get(displayId) != null) { @@ -181,7 +228,12 @@ final class AccessibilityController { return true; } - public void performComputeChangedWindowsNotLocked(int displayId, boolean forceSend) { + void performComputeChangedWindowsNot(int displayId, boolean forceSend) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".performComputeChangedWindowsNot", + "displayId=" + displayId + "; forceSend=" + forceSend); + } WindowsForAccessibilityObserver observer = null; synchronized (mService.mGlobalLock) { final WindowsForAccessibilityObserver windowsForA11yObserver = @@ -191,86 +243,119 @@ final class AccessibilityController { } } if (observer != null) { - observer.performComputeChangedWindowsNotLocked(forceSend); + observer.performComputeChangedWindows(forceSend); } } - public void setMagnificationSpecLocked(int displayId, MagnificationSpec spec) { + void setMagnificationSpec(int displayId, MagnificationSpec spec) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".setMagnificationSpec", + "displayId=" + displayId + "; spec={" + spec + "}"); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.setMagnificationSpecLocked(spec); + displayMagnifier.setMagnificationSpec(spec); } final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayId); if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); + windowsForA11yObserver.scheduleComputeChangedWindows(); } } - public void getMagnificationRegionLocked(int displayId, Region outMagnificationRegion) { + void getMagnificationRegion(int displayId, Region outMagnificationRegion) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".getMagnificationRegion", + "displayId=" + displayId + "; outMagnificationRegion={" + outMagnificationRegion + + "}"); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.getMagnificationRegionLocked(outMagnificationRegion); + displayMagnifier.getMagnificationRegion(outMagnificationRegion); } } - public void onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle) { + void onRectangleOnScreenRequested(int displayId, Rect rectangle) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".onRectangleOnScreenRequested", + "displayId=" + displayId + "; rectangle={" + rectangle + "}"); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.onRectangleOnScreenRequestedLocked(rectangle); + displayMagnifier.onRectangleOnScreenRequested(rectangle); } // Not relevant for the window observer. } - public void onWindowLayersChangedLocked(int displayId) { + void onWindowLayersChanged(int displayId) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".onWindowLayersChanged", "displayId=" + displayId); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.onWindowLayersChangedLocked(); + displayMagnifier.onWindowLayersChanged(); } final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayId); if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); + windowsForA11yObserver.scheduleComputeChangedWindows(); } } - public void onRotationChangedLocked(DisplayContent displayContent) { + void onRotationChanged(DisplayContent displayContent) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".onRotationChanged", + "displayContent={" + displayContent + "}"); + } final int displayId = displayContent.getDisplayId(); final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.onRotationChangedLocked(displayContent); + displayMagnifier.onRotationChanged(displayContent); } final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayId); if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); + windowsForA11yObserver.scheduleComputeChangedWindows(); } } - public void onAppWindowTransitionLocked(int displayId, int transition) { + void onAppWindowTransition(int displayId, int transition) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".onAppWindowTransition", + "displayId=" + displayId + "; transition=" + transition); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.onAppWindowTransitionLocked(displayId, transition); + displayMagnifier.onAppWindowTransition(displayId, transition); } // Not relevant for the window observer. } - public void onWindowTransitionLocked(WindowState windowState, int transition) { + void onWindowTransition(WindowState windowState, int transition) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".onWindowTransition", + "windowState={" + windowState + "}; transition=" + transition); + } final int displayId = windowState.getDisplayId(); final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.onWindowTransitionLocked(windowState, transition); + displayMagnifier.onWindowTransition(windowState, transition); } final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayId); if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); + windowsForA11yObserver.scheduleComputeChangedWindows(); } } - public void onWindowFocusChangedNotLocked(int displayId) { + void onWindowFocusChangedNot(int displayId) { // Not relevant for the display magnifier. - + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".onWindowFocusChangedNot", "displayId=" + displayId); + } WindowsForAccessibilityObserver observer = null; synchronized (mService.mGlobalLock) { final WindowsForAccessibilityObserver windowsForA11yObserver = @@ -280,7 +365,7 @@ final class AccessibilityController { } } if (observer != null) { - observer.performComputeChangedWindowsNotLocked(false); + observer.performComputeChangedWindows(false); } // Since we abandon initializing observers if no window has focus, make sure all observers // are initialized. @@ -311,7 +396,7 @@ final class AccessibilityController { boolean areAllObserversInitialized = true; for (int i = unInitializedObservers.size() - 1; i >= 0; --i) { final WindowsForAccessibilityObserver observer = unInitializedObservers.get(i); - observer.performComputeChangedWindowsNotLocked(true); + observer.performComputeChangedWindows(true); areAllObserversInitialized &= observer.mInitialized; } synchronized (mService.mGlobalLock) { @@ -324,50 +409,89 @@ final class AccessibilityController { * another display is also taken into consideration. * @param displayIds the display ids of displays when the situation happens. */ - public void onSomeWindowResizedOrMovedLocked(int... displayIds) { + void onSomeWindowResizedOrMoved(int... displayIds) { + onSomeWindowResizedOrMovedWithCallingUid(Binder.getCallingUid(), displayIds); + } + + void onSomeWindowResizedOrMovedWithCallingUid(int callingUid, int... displayIds) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".onSomeWindowResizedOrMoved", + "displayIds={" + displayIds.toString() + "}", + "".getBytes(), + callingUid); + } // Not relevant for the display magnifier. for (int i = 0; i < displayIds.length; i++) { final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayIds[i]); if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); + windowsForA11yObserver.scheduleComputeChangedWindows(); } } } - public void drawMagnifiedRegionBorderIfNeededLocked(int displayId, - SurfaceControl.Transaction t) { + void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".drawMagnifiedRegionBorderIfNeeded", + "displayId=" + displayId + "; transaction={" + t + "}"); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(t); + displayMagnifier.drawMagnifiedRegionBorderIfNeeded(t); } // Not relevant for the window observer. } - public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { + MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".getMagnificationSpecForWindow", + "windowState={" + windowState + "}"); + } final int displayId = windowState.getDisplayId(); final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - return displayMagnifier.getMagnificationSpecForWindowLocked(windowState); + return displayMagnifier.getMagnificationSpecForWindow(windowState); } return null; } - public boolean hasCallbacksLocked() { + boolean hasCallbacks() { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".hasCallbacks"); + } return (mDisplayMagnifiers.size() > 0 || mWindowsForAccessibilityObserver.size() > 0); } - public void setForceShowMagnifiableBoundsLocked(int displayId, boolean show) { + void setForceShowMagnifiableBounds(int displayId, boolean show) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".setForceShowMagnifiableBounds", + "displayId=" + displayId + "; show=" + show); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.setForceShowMagnifiableBoundsLocked(show); + displayMagnifier.setForceShowMagnifiableBounds(show); displayMagnifier.showMagnificationBoundsIfNeeded(); } } - public void handleWindowObserverOfEmbeddedDisplayLocked(int embeddedDisplayId, + void handleWindowObserverOfEmbeddedDisplay(int embeddedDisplayId, WindowState parentWindow) { + handleWindowObserverOfEmbeddedDisplay( + embeddedDisplayId, parentWindow, Binder.getCallingUid()); + } + + void handleWindowObserverOfEmbeddedDisplay( + int embeddedDisplayId, WindowState parentWindow, int callingUid) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".handleWindowObserverOfEmbeddedDisplay", + "embeddedDisplayId=" + embeddedDisplayId + "; parentWindowState={" + + parentWindow + "}", + "".getBytes(), + callingUid); + } if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) { return; } @@ -390,7 +514,7 @@ final class AccessibilityController { } } - private static void populateTransformationMatrixLocked(WindowState windowState, + private static void populateTransformationMatrix(WindowState windowState, Matrix outMatrix) { windowState.getTransformationMatrix(sTempFloats, outMatrix); } @@ -451,6 +575,7 @@ final class AccessibilityController { private final Handler mHandler; private final DisplayContent mDisplayContent; private final Display mDisplay; + private final AccessibilityTracing mAccessibilityTracing; private final MagnificationCallbacks mCallbacks; @@ -458,7 +583,7 @@ final class AccessibilityController { private boolean mForceShowMagnifiableBounds = false; - public DisplayMagnifier(WindowManagerService windowManagerService, + DisplayMagnifier(WindowManagerService windowManagerService, DisplayContent displayContent, Display display, MagnificationCallbacks callbacks) { @@ -469,36 +594,58 @@ final class AccessibilityController { mDisplay = display; mHandler = new MyHandler(mService.mH.getLooper()); mMagnifedViewport = new MagnifiedViewport(); + mAccessibilityTracing = AccessibilityTracing.getInstance(mService); mLongAnimationDuration = mDisplayContext.getResources().getInteger( com.android.internal.R.integer.config_longAnimTime); + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".DisplayMagnifier.constructor", + "windowManagerService={" + windowManagerService + "}; displayContent={" + + displayContent + "}; display={" + display + "}; callbacks={" + + callbacks + "}"); + } } - public void setMagnificationSpecLocked(MagnificationSpec spec) { - mMagnifedViewport.updateMagnificationSpecLocked(spec); - mMagnifedViewport.recomputeBoundsLocked(); + void setMagnificationSpec(MagnificationSpec spec) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + LOG_TAG + ".setMagnificationSpec", "spec={" + spec + "}"); + } + mMagnifedViewport.updateMagnificationSpec(spec); + mMagnifedViewport.recomputeBounds(); mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec); mService.scheduleAnimationLocked(); } - public void setForceShowMagnifiableBoundsLocked(boolean show) { + void setForceShowMagnifiableBounds(boolean show) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + LOG_TAG + ".setForceShowMagnifiableBounds", "show=" + show); + } mForceShowMagnifiableBounds = show; - mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true); + mMagnifedViewport.setMagnifiedRegionBorderShown(show, true); } - public boolean isForceShowingMagnifiableBoundsLocked() { + boolean isForceShowingMagnifiableBounds() { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".isForceShowingMagnifiableBounds"); + } return mForceShowMagnifiableBounds; } - public void onRectangleOnScreenRequestedLocked(Rect rectangle) { + void onRectangleOnScreenRequested(Rect rectangle) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + LOG_TAG + ".onRectangleOnScreenRequested", "rectangle={" + rectangle + "}"); + } if (DEBUG_RECTANGLE_REQUESTED) { Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle); } - if (!mMagnifedViewport.isMagnifyingLocked()) { + if (!mMagnifedViewport.isMagnifying()) { return; } Rect magnifiedRegionBounds = mTempRect2; - mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds); + mMagnifedViewport.getMagnifiedFrameInContentCoords(magnifiedRegionBounds); if (magnifiedRegionBounds.contains(rectangle)) { return; } @@ -511,31 +658,42 @@ final class AccessibilityController { args).sendToTarget(); } - public void onWindowLayersChangedLocked() { + void onWindowLayersChanged() { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".onWindowLayersChanged"); + } if (DEBUG_LAYERS) { Slog.i(LOG_TAG, "Layers changed."); } - mMagnifedViewport.recomputeBoundsLocked(); + mMagnifedViewport.recomputeBounds(); mService.scheduleAnimationLocked(); } - public void onRotationChangedLocked(DisplayContent displayContent) { + void onRotationChanged(DisplayContent displayContent) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + LOG_TAG + ".onRotationChanged", "displayContent={" + displayContent + "}"); + } if (DEBUG_ROTATION) { final int rotation = displayContent.getRotation(); Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation) + " displayId: " + displayContent.getDisplayId()); } - mMagnifedViewport.onRotationChangedLocked(displayContent.getPendingTransaction()); + mMagnifedViewport.onRotationChanged(displayContent.getPendingTransaction()); mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED); } - public void onAppWindowTransitionLocked(int displayId, int transition) { + void onAppWindowTransition(int displayId, int transition) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".onAppWindowTransition", + "displayId=" + displayId + "; transition=" + transition); + } if (DEBUG_WINDOW_TRANSITIONS) { Slog.i(LOG_TAG, "Window transition: " + AppTransition.appTransitionOldToString(transition) + " displayId: " + displayId); } - final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); + final boolean magnifying = mMagnifedViewport.isMagnifying(); if (magnifying) { switch (transition) { case WindowManager.TRANSIT_OLD_ACTIVITY_OPEN: @@ -550,13 +708,17 @@ final class AccessibilityController { } } - public void onWindowTransitionLocked(WindowState windowState, int transition) { + void onWindowTransition(WindowState windowState, int transition) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".onWindowTransition", + "windowState={" + windowState + "}; transition=" + transition); + } if (DEBUG_WINDOW_TRANSITIONS) { Slog.i(LOG_TAG, "Window transition: " + AppTransition.appTransitionOldToString(transition) + " displayId: " + windowState.getDisplayId()); } - final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); + final boolean magnifying = mMagnifedViewport.isMagnifying(); final int type = windowState.mAttrs.type; switch (transition) { case WindowManagerPolicy.TRANSIT_ENTER: @@ -586,7 +748,7 @@ final class AccessibilityController { case WindowManager.LayoutParams.TYPE_QS_DIALOG: case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: { Rect magnifiedRegionBounds = mTempRect2; - mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked( + mMagnifedViewport.getMagnifiedFrameInContentCoords( magnifiedRegionBounds); Rect touchableRegionBounds = mTempRect1; windowState.getTouchableRegion(mTempRegion1); @@ -604,8 +766,12 @@ final class AccessibilityController { } } - public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { - MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked(); + MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow", + "windowState={" + windowState + "}"); + } + MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec(); if (spec != null && !spec.isNop()) { if (!windowState.shouldMagnify()) { return null; @@ -614,24 +780,38 @@ final class AccessibilityController { return spec; } - public void getMagnificationRegionLocked(Region outMagnificationRegion) { + void getMagnificationRegion(Region outMagnificationRegion) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationRegion", + "outMagnificationRegion={" + outMagnificationRegion + "}"); + } // Make sure we're working with the most current bounds - mMagnifedViewport.recomputeBoundsLocked(); - mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion); + mMagnifedViewport.recomputeBounds(); + mMagnifedViewport.getMagnificationRegion(outMagnificationRegion); } - public void destroyLocked() { + void destroy() { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".destroy"); + } mMagnifedViewport.destroyWindow(); } // Can be called outside of a surface transaction - public void showMagnificationBoundsIfNeeded() { + void showMagnificationBoundsIfNeeded() { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".showMagnificationBoundsIfNeeded"); + } mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED) .sendToTarget(); } - public void drawMagnifiedRegionBorderIfNeededLocked(SurfaceControl.Transaction t) { - mMagnifedViewport.drawWindowIfNeededLocked(t); + void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded", + "transition={" + t + "}"); + } + mMagnifedViewport.drawWindowIfNeeded(t); } void dump(PrintWriter pw, String prefix) { @@ -665,7 +845,7 @@ final class AccessibilityController { private boolean mFullRedrawNeeded; private int mTempLayer = 0; - public MagnifiedViewport() { + MagnifiedViewport() { mBorderWidth = mDisplayContext.getResources().getDimension( com.android.internal.R.dimen.accessibility_magnification_indicator_width); mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2); @@ -681,14 +861,14 @@ final class AccessibilityController { mCircularPath = null; } - recomputeBoundsLocked(); + recomputeBounds(); } - public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) { + void getMagnificationRegion(@NonNull Region outMagnificationRegion) { outMagnificationRegion.set(mMagnificationRegion); } - public void updateMagnificationSpecLocked(MagnificationSpec spec) { + void updateMagnificationSpec(MagnificationSpec spec) { if (spec != null) { mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY); } else { @@ -698,12 +878,12 @@ final class AccessibilityController { // to show the border. We will do so when the pending message is handled. if (!mHandler.hasMessages( MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) { - setMagnifiedRegionBorderShownLocked( - isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true); + setMagnifiedRegionBorderShown( + isMagnifying() || isForceShowingMagnifiableBounds(), true); } } - public void recomputeBoundsLocked() { + void recomputeBounds() { mDisplay.getRealSize(mTempPoint); final int screenWidth = mTempPoint.x; final int screenHeight = mTempPoint.y; @@ -721,7 +901,7 @@ final class AccessibilityController { SparseArray<WindowState> visibleWindows = mTempWindowStates; visibleWindows.clear(); - populateWindowsOnScreenLocked(visibleWindows); + populateWindowsOnScreen(visibleWindows); final int visibleWindowCount = visibleWindows.size(); for (int i = visibleWindowCount - 1; i >= 0; i--) { @@ -736,7 +916,7 @@ final class AccessibilityController { // Consider the touchable portion of the window Matrix matrix = mTempMatrix; - populateTransformationMatrixLocked(windowState, matrix); + populateTransformationMatrix(windowState, matrix); Region touchableRegion = mTempRegion3; windowState.getTouchableRegion(touchableRegion); Rect touchableFrame = mTempRect1; @@ -848,24 +1028,24 @@ final class AccessibilityController { return letterboxBounds; } - public void onRotationChangedLocked(SurfaceControl.Transaction t) { + void onRotationChanged(SurfaceControl.Transaction t) { // If we are showing the magnification border, hide it immediately so // the user does not see strange artifacts during rotation. The screenshot // used for rotation already has the border. After the rotation is complete // we will show the border. - if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) { - setMagnifiedRegionBorderShownLocked(false, false); + if (isMagnifying() || isForceShowingMagnifiableBounds()) { + setMagnifiedRegionBorderShown(false, false); final long delay = (long) (mLongAnimationDuration * mService.getWindowAnimationScaleLocked()); Message message = mHandler.obtainMessage( MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED); mHandler.sendMessageDelayed(message, delay); } - recomputeBoundsLocked(); + recomputeBounds(); mWindow.updateSize(t); } - public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) { + void setMagnifiedRegionBorderShown(boolean shown, boolean animate) { if (shown) { mFullRedrawNeeded = true; mOldMagnificationRegion.set(0, 0, 0, 0); @@ -873,31 +1053,31 @@ final class AccessibilityController { mWindow.setShown(shown, animate); } - public void getMagnifiedFrameInContentCoordsLocked(Rect rect) { + void getMagnifiedFrameInContentCoords(Rect rect) { MagnificationSpec spec = mMagnificationSpec; mMagnificationRegion.getBounds(rect); rect.offset((int) -spec.offsetX, (int) -spec.offsetY); rect.scale(1.0f / spec.scale); } - public boolean isMagnifyingLocked() { + boolean isMagnifying() { return mMagnificationSpec.scale > 1.0f; } - public MagnificationSpec getMagnificationSpecLocked() { + MagnificationSpec getMagnificationSpec() { return mMagnificationSpec; } - public void drawWindowIfNeededLocked(SurfaceControl.Transaction t) { - recomputeBoundsLocked(); + void drawWindowIfNeeded(SurfaceControl.Transaction t) { + recomputeBounds(); mWindow.drawIfNeeded(t); } - public void destroyWindow() { + void destroyWindow() { mWindow.releaseSurface(); } - private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) { + private void populateWindowsOnScreen(SparseArray<WindowState> outWindows) { mTempLayer = 0; mDisplayContent.forAllWindows((w) -> { if (w.isOnScreen() && w.isVisible() @@ -929,7 +1109,7 @@ final class AccessibilityController { private boolean mInvalidated; - public ViewportWindow(Context context) { + ViewportWindow(Context context) { SurfaceControl surfaceControl = null; try { mDisplay.getRealSize(mTempPoint); @@ -971,7 +1151,7 @@ final class AccessibilityController { mInvalidated = true; } - public void setShown(boolean shown, boolean animate) { + void setShown(boolean shown, boolean animate) { synchronized (mService.mGlobalLock) { if (mShown == shown) { return; @@ -986,13 +1166,13 @@ final class AccessibilityController { @SuppressWarnings("unused") // Called reflectively from an animator. - public int getAlpha() { + int getAlpha() { synchronized (mService.mGlobalLock) { return mAlpha; } } - public void setAlpha(int alpha) { + void setAlpha(int alpha) { synchronized (mService.mGlobalLock) { if (mAlpha == alpha) { return; @@ -1005,7 +1185,7 @@ final class AccessibilityController { } } - public void setBounds(Region bounds) { + void setBounds(Region bounds) { synchronized (mService.mGlobalLock) { if (mBounds.equals(bounds)) { return; @@ -1018,7 +1198,7 @@ final class AccessibilityController { } } - public void updateSize(SurfaceControl.Transaction t) { + void updateSize(SurfaceControl.Transaction t) { synchronized (mService.mGlobalLock) { mDisplay.getRealSize(mTempPoint); t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y); @@ -1026,7 +1206,7 @@ final class AccessibilityController { } } - public void invalidate(Rect dirtyRect) { + void invalidate(Rect dirtyRect) { if (dirtyRect != null) { mDirtyRect.set(dirtyRect); } else { @@ -1036,7 +1216,7 @@ final class AccessibilityController { mService.scheduleAnimationLocked(); } - public void drawIfNeeded(SurfaceControl.Transaction t) { + void drawIfNeeded(SurfaceControl.Transaction t) { synchronized (mService.mGlobalLock) { if (!mInvalidated) { return; @@ -1078,7 +1258,7 @@ final class AccessibilityController { } } - public void releaseSurface() { + void releaseSurface() { mService.mTransactionFactory.get().remove(mSurfaceControl).apply(); mSurface.release(); } @@ -1101,7 +1281,7 @@ final class AccessibilityController { private final ValueAnimator mShowHideFrameAnimator; - public AnimationController(Context context, Looper looper) { + AnimationController(Context context, Looper looper) { super(looper); mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this, PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA); @@ -1114,7 +1294,7 @@ final class AccessibilityController { mShowHideFrameAnimator.setDuration(longAnimationDuration); } - public void onFrameShownStateChanged(boolean shown, boolean animate) { + void onFrameShownStateChanged(boolean shown, boolean animate) { obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED, shown ? 1 : 0, animate ? 1 : 0).sendToTarget(); } @@ -1158,7 +1338,7 @@ final class AccessibilityController { public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4; public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5; - public MyHandler(Looper looper) { + MyHandler(Looper looper) { super(looper); } @@ -1193,9 +1373,9 @@ final class AccessibilityController { case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : { synchronized (mService.mGlobalLock) { - if (mMagnifedViewport.isMagnifyingLocked() - || isForceShowingMagnifiableBoundsLocked()) { - mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true); + if (mMagnifedViewport.isMagnifying() + || isForceShowingMagnifiableBounds()) { + mMagnifedViewport.setMagnifiedRegionBorderShown(true, true); mService.scheduleAnimationLocked(); } } @@ -1252,6 +1432,8 @@ final class AccessibilityController { private final Handler mHandler; + private final AccessibilityTracing mAccessibilityTracing; + private final WindowsForAccessibilityCallback mCallback; private final int mDisplayId; @@ -1263,24 +1445,32 @@ final class AccessibilityController { // Set to true if initializing window population complete. private boolean mInitialized; - public WindowsForAccessibilityObserver(WindowManagerService windowManagerService, + WindowsForAccessibilityObserver(WindowManagerService windowManagerService, int displayId, WindowsForAccessibilityCallback callback) { mService = windowManagerService; mCallback = callback; mDisplayId = displayId; mHandler = new MyHandler(mService.mH.getLooper()); + mAccessibilityTracing = AccessibilityTracing.getInstance(mService); mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration .getSendRecurringAccessibilityEventsInterval(); computeChangedWindows(true); } - public void performComputeChangedWindowsNotLocked(boolean forceSend) { + void performComputeChangedWindows(boolean forceSend) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".performComputeChangedWindows", + "forceSend=" + forceSend); + } mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS); computeChangedWindows(forceSend); } - public void scheduleComputeChangedWindowsLocked() { + void scheduleComputeChangedWindows() { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".scheduleComputeChangedWindows"); + } if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) { mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS, mRecurringAccessibilityEventsIntervalMillis); @@ -1307,7 +1497,11 @@ final class AccessibilityController { * * @param forceSend Send the windows the accessibility even if they haven't changed. */ - public void computeChangedWindows(boolean forceSend) { + void computeChangedWindows(boolean forceSend) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + LOG_TAG + ".computeChangedWindows", "forceSend=" + forceSend); + } if (DEBUG) { Slog.i(LOG_TAG, "computeChangedWindows()"); } @@ -1343,7 +1537,7 @@ final class AccessibilityController { unaccountedSpace.set(0, 0, screenWidth, screenHeight); final SparseArray<WindowState> visibleWindows = mTempWindowStates; - populateVisibleWindowsOnScreenLocked(visibleWindows); + populateVisibleWindowsOnScreen(visibleWindows); Set<IBinder> addedWindows = mTempBinderSet; addedWindows.clear(); @@ -1518,7 +1712,7 @@ final class AccessibilityController { // Map the frame to get what appears on the screen. Matrix matrix = mTempMatrix; - populateTransformationMatrixLocked(windowState, matrix); + populateTransformationMatrix(windowState, matrix); forEachRect(touchableRegion, rect -> { // Move to origin as all transforms are captured by the matrix. @@ -1563,7 +1757,7 @@ final class AccessibilityController { && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION); } - private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) { + private void populateVisibleWindowsOnScreen(SparseArray<WindowState> outWindows) { final List<WindowState> tempWindowStatesList = new ArrayList<>(); final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); if (dc == null) { @@ -1637,4 +1831,292 @@ final class AccessibilityController { } } } + + private static final class AccessibilityControllerInternalImpl + implements AccessibilityControllerInternal { + + private static AccessibilityControllerInternal sInstance; + static AccessibilityControllerInternal getInstance(WindowManagerService service) { + synchronized (STATIC_LOCK) { + if (sInstance == null) { + sInstance = new AccessibilityControllerInternalImpl(service); + } + return sInstance; + } + } + + private final AccessibilityTracing mTracing; + private AccessibilityControllerInternalImpl(WindowManagerService service) { + mTracing = AccessibilityTracing.getInstance(service); + } + + @Override + public void startTrace() { + mTracing.startTrace(); + } + + @Override + public void stopTrace() { + mTracing.stopTrace(); + } + + @Override + public boolean isEnabled() { + return mTracing.isEnabled(); + } + + @Override + public void logTrace( + String where, String callingParams, byte[] a11yDump, int callingUid, + StackTraceElement[] stackTrace) { + mTracing.logState(where, callingParams, a11yDump, callingUid, stackTrace); + } + } + + private static final class AccessibilityTracing { + private static AccessibilityTracing sInstance; + static AccessibilityTracing getInstance(WindowManagerService service) { + synchronized (STATIC_LOCK) { + if (sInstance == null) { + sInstance = new AccessibilityTracing(service); + } + return sInstance; + } + } + + private static final int BUFFER_CAPACITY = 4096 * 1024; + private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb"; + private static final String TRACE_DIRECTORY = "/data/misc/a11ytrace/"; + private static final String TAG = "AccessibilityTracing"; + private static final long MAGIC_NUMBER_VALUE = + ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; + + private final Object mLock = new Object(); + private final WindowManagerService mService; + private final File mTraceFile; + private final TraceBuffer mBuffer; + private final LogHandler mHandler; + private volatile boolean mEnabled; + + AccessibilityTracing(WindowManagerService service) { + mService = service; + mTraceFile = new File(TRACE_FILENAME); + mBuffer = new TraceBuffer(BUFFER_CAPACITY); + HandlerThread workThread = new HandlerThread(TAG); + workThread.start(); + mHandler = new LogHandler(workThread.getLooper()); + } + + /** + * Start the trace. + */ + void startTrace() { + if (IS_USER) { + Slog.e(TAG, "Error: Tracing is not supported on user builds."); + return; + } + synchronized (mLock) { + try { + Files.createDirectories(Paths.get(TRACE_DIRECTORY)); + mTraceFile.createNewFile(); + } catch (Exception e) { + Slog.e(TAG, "Error: Failed to create trace file."); + return; + } + mEnabled = true; + mBuffer.resetBuffer(); + } + } + + /** + * Stops the trace and write the current buffer to disk + */ + void stopTrace() { + if (IS_USER) { + Slog.e(TAG, "Error: Tracing is not supported on user builds."); + return; + } + synchronized (mLock) { + mEnabled = false; + if (mEnabled) { + Slog.e(TAG, "Error: tracing enabled while waiting for flush."); + return; + } + writeTraceToFile(); + } + } + + boolean isEnabled() { + return mEnabled; + } + + /** + * Write an accessibility trace log entry. + */ + void logState(String where) { + if (!mEnabled) { + return; + } + logState(where, ""); + } + + /** + * Write an accessibility trace log entry. + */ + void logState(String where, String callingParams) { + if (!mEnabled) { + return; + } + logState(where, callingParams, "".getBytes()); + } + + /** + * Write an accessibility trace log entry. + */ + void logState(String where, String callingParams, byte[] a11yDump) { + if (!mEnabled) { + return; + } + logState(where, callingParams, a11yDump, Binder.getCallingUid()); + } + + /** + * Write an accessibility trace log entry. + */ + void logState( + String where, String callingParams, byte[] a11yDump, int callingUid) { + if (!mEnabled) { + return; + } + StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); + + logState(where, callingParams, a11yDump, callingUid, stackTraceElements); + } + + /** + * Write an accessibility trace log entry. + */ + void logState(String where, String callingParams, byte[] a11yDump, int callingUid, + StackTraceElement[] stackTrace) { + if (!mEnabled) { + return; + } + + log(where, callingParams, a11yDump, callingUid, stackTrace); + } + + private String toStackTraceString(StackTraceElement[] stackTraceElements) { + if (stackTraceElements == null) { + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + boolean skip = true; + for (int i = 0; i < stackTraceElements.length; i++) { + if (stackTraceElements[i].toString().contains( + AccessibilityTracing.class.getSimpleName())) { + skip = false; + } else if (!skip) { + stringBuilder.append(stackTraceElements[i].toString()).append("\n"); + } + } + return stringBuilder.toString(); + } + + /** + * Write the current state to the buffer + */ + private void log(String where, String callingParams, byte[] a11yDump, int callingUid, + StackTraceElement[] stackTrace) { + SimpleDateFormat fm = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); + SomeArgs args = SomeArgs.obtain(); + args.arg1 = SystemClock.elapsedRealtimeNanos(); + args.arg2 = fm.format(new Date()).toString(); + args.arg3 = where; + args.arg4 = Process.myPid() + ":" + Application.getProcessName(); + args.arg5 = Thread.currentThread().getId() + ":" + Thread.currentThread().getName(); + args.arg6 = callingUid; + args.arg7 = callingParams; + args.arg8 = stackTrace; + args.arg9 = a11yDump; + mHandler.obtainMessage(LogHandler.MESSAGE_LOG_TRACE_ENTRY, args).sendToTarget(); + } + + /** + * Writes the trace buffer to new file for the bugreport. + */ + void writeTraceToFile() { + mHandler.sendEmptyMessage(LogHandler.MESSAGE_WRITE_FILE); + } + + private class LogHandler extends Handler { + public static final int MESSAGE_LOG_TRACE_ENTRY = 1; + public static final int MESSAGE_WRITE_FILE = 2; + + LogHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MESSAGE_LOG_TRACE_ENTRY: { + final SomeArgs args = (SomeArgs) message.obj; + try { + ProtoOutputStream os = new ProtoOutputStream(); + PackageManagerInternal pmInternal = + LocalServices.getService(PackageManagerInternal.class); + + long tokenOuter = os.start(ENTRY); + String callingStack = + toStackTraceString((StackTraceElement[]) args.arg8); + + os.write(ELAPSED_REALTIME_NANOS, (long) args.arg1); + os.write(CALENDAR_TIME, (String) args.arg2); + os.write(WHERE, (String) args.arg3); + os.write(PROCESS_NAME, (String) args.arg4); + os.write(THREAD_ID_NAME, (String) args.arg5); + os.write(CALLING_PKG, pmInternal.getNameForUid((int) args.arg6)); + os.write(CALLING_PARAMS, (String) args.arg7); + os.write(CALLING_STACKS, callingStack); + os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg9); + + long tokenInner = os.start(WINDOW_MANAGER_SERVICE); + synchronized (mService.mGlobalLock) { + mService.dumpDebugLocked(os, WindowTraceLogLevel.ALL); + } + os.end(tokenInner); + + os.end(tokenOuter); + synchronized (mLock) { + mBuffer.add(os); + } + } catch (Exception e) { + Slog.e(TAG, "Exception while tracing state", e); + } + break; + } + case MESSAGE_WRITE_FILE: { + synchronized (mLock) { + writeTraceToFileInternal(); + } + break; + } + } + } + } + + /** + * Writes the trace buffer to disk. + */ + private void writeTraceToFileInternal() { + try { + ProtoOutputStream proto = new ProtoOutputStream(); + proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); + mBuffer.writeTraceToFile(mTraceFile, proto); + } catch (IOException e) { + Slog.e(TAG, "Unable to write buffer to file", e); + } + } + } + } diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index ce951b85ffe6..771b712cf480 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -975,7 +975,8 @@ class ActivityClientController extends IActivityClientController.Stub { final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) { r.mDisplayContent.mAppTransition.overridePendingAppTransition( - packageName, enterAnim, exitAnim, null, null); + packageName, enterAnim, exitAnim, null, null, + r.mOverrideTaskTransition); } } Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 6bca4843e009..3a0eb397d210 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -978,6 +978,7 @@ class ActivityMetricsLogger { final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info, r, (int) startupTimeMs); BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot)); + mLastTransitionInfo.remove(r); if (!info.isInterestingToLoggerAndObserver()) { return infoSnapshot; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 36c503703b9c..ccb349b9c5ab 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; @@ -82,7 +81,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.isFixedOrientationLandscape; import static android.content.pm.ActivityInfo.isFixedOrientationPortrait; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.res.Configuration.EMPTY; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; @@ -203,7 +201,6 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainerChildProto.ACTIVITY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -273,7 +270,6 @@ import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; import android.os.storage.StorageManager; -import android.permission.PermissionManager; import android.service.dreams.DreamActivity; import android.service.dreams.DreamManagerInternal; import android.service.voice.IVoiceInteractionSession; @@ -682,6 +678,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean mRequestForceTransition; boolean mEnteringAnimation; + boolean mOverrideTaskTransition; boolean mAppStopped; // A hint to override the window specified rotation animation, or -1 to use the window specified @@ -1365,7 +1362,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } final boolean surfaceReady = w.isDrawn() // Regular case - || w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready. || w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface. final boolean needsLetterbox = surfaceReady && isLetterboxed(w); updateRoundedCorners(w); @@ -1627,6 +1623,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (rotationAnimation >= 0) { mRotationAnimationHint = rotationAnimation; } + + mOverrideTaskTransition = options.getOverrideTaskTransition(); } ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService( @@ -3997,7 +3995,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(), pendingOptions.getAnimationStartedListener(), - pendingOptions.getAnimationFinishedListener()); + pendingOptions.getAnimationFinishedListener(), + pendingOptions.getOverrideTaskTransition()); break; case ANIM_CLIP_REVEAL: displayContent.mAppTransition.overridePendingAppTransitionClipReveal( @@ -6529,7 +6528,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Allowing closing {@link ActivityRecord} to participate can lead to an Activity in another // task being started in the wrong orientation during the transition. if (!getDisplayContent().mClosingApps.contains(this) - && (isVisible() || getDisplayContent().mOpeningApps.contains(this))) { + && (isVisibleRequested() || getDisplayContent().mOpeningApps.contains(this))) { return mOrientation; } @@ -6775,20 +6774,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // layout traversals. mConfigurationSeq = Math.max(++mConfigurationSeq, 1); getResolvedOverrideConfiguration().seq = mConfigurationSeq; - - // Sandbox max bounds by setting it to the app bounds, if activity is letterboxed or in - // size compat mode. - if (providesMaxBounds()) { - if (DEBUG_CONFIGURATION) { - ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds for uid %s to bounds %s " - + "due to letterboxing? %s mismatch with parent bounds? %s size compat " - + "mode %s", getUid(), - resolvedConfig.windowConfiguration.getBounds(), mLetterbox != null, - !matchParentBounds(), inSizeCompatMode()); - } - resolvedConfig.windowConfiguration - .setMaxBounds(resolvedConfig.windowConfiguration.getBounds()); - } } /** @@ -6972,19 +6957,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return super.getBounds(); } - @Override - public boolean providesMaxBounds() { - // System and SystemUI should always be able to access the physical display bounds, - // so do not provide it with the overridden maximum bounds. - // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead - if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW, - getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) { - return false; - } - // Max bounds should be sandboxed when this is letterboxed or in size compat mode. - return mLetterbox != null || !matchParentBounds() || inSizeCompatMode(); - } - @VisibleForTesting @Override Rect getAnimationBounds(int appRootTaskClipMode) { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index d846c3a6d36c..3456e51d028a 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -772,6 +772,11 @@ class ActivityStarter { newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true); } newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT, new IntentSender(target)); + ActivityOptions options = mRequest.activityOptions.getOptions(mRequest.intent, + mRequest.activityInfo, + mService.getProcessController(mRequest.caller), + mSupervisor); + newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_ACTIVITY_OPTIONS, options.toBundle()); heavy.updateIntentForHeavyWeightActivity(newIntent); newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP, mRequest.activityInfo.packageName); @@ -1814,8 +1819,7 @@ class ActivityStarter { } private Task computeTargetTask() { - if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask - && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { + if (mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { // A new task should be created instead of using existing one. return null; } else if (mSourceRecord != null) { @@ -2505,7 +2509,9 @@ class ActivityStarter { // If bring to front is requested, and no result is requested and we have not been given // an explicit task to launch in to, and we can find a task that was started with this // same component, then instead of launching bring that one to the front. - putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null; + putIntoExistingTask &= !isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK) + ? (mInTask == null && mStartActivity.resultTo == null) + : (mInTask == null); ActivityRecord intentActivity = null; if (putIntoExistingTask) { if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 2d6e9b2c68c5..4bcef408d9a0 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -130,6 +130,7 @@ import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.ActivityThread; import android.app.AlertDialog; +import android.app.AnrController; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.Dialog; @@ -494,6 +495,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { */ private volatile long mLastStopAppSwitchesTime; + private final List<AnrController> mAnrController = new ArrayList<>(); IActivityController mController = null; boolean mControllerIsAMonkey = false; @@ -2058,6 +2060,40 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mAppSwitchesAllowed; } + /** Register an {@link AnrController} to control the ANR dialog behavior */ + public void registerAnrController(AnrController controller) { + synchronized (mGlobalLock) { + mAnrController.add(controller); + } + } + + /** Unregister an {@link AnrController} */ + public void unregisterAnrController(AnrController controller) { + synchronized (mGlobalLock) { + mAnrController.remove(controller); + } + } + + /** @return the max ANR delay from all registered {@link AnrController} instances */ + public long getMaxAnrDelayMillis(ApplicationInfo info) { + if (info == null || info.packageName == null) { + return 0; + } + + final ArrayList<AnrController> controllers; + synchronized (mGlobalLock) { + controllers = new ArrayList<>(mAnrController); + } + + final String packageName = info.packageName; + long maxDelayMs = 0; + for (AnrController controller : controllers) { + maxDelayMs = Math.max(maxDelayMs, controller.getAnrDelayMillis(packageName, info.uid)); + } + maxDelayMs = Math.max(maxDelayMs, 0); + return maxDelayMs; + } + @Override public void setActivityController(IActivityController controller, boolean imAMonkey) { mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java index fde036950245..c589feae56ca 100644 --- a/services/core/java/com/android/server/wm/AlertWindowNotification.java +++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java @@ -39,7 +39,7 @@ import android.net.Uri; import android.os.Bundle; import com.android.internal.R; -import com.android.server.policy.IconUtilities; +import com.android.internal.util.ImageUtils; /** Displays an ongoing notification for a process displaying an alert window */ class AlertWindowNotification { @@ -54,7 +54,6 @@ class AlertWindowNotification { private final NotificationManager mNotificationManager; private final String mPackageName; private boolean mPosted; - private IconUtilities mIconUtilities; AlertWindowNotification(WindowManagerService service, String packageName) { mService = service; @@ -63,7 +62,6 @@ class AlertWindowNotification { (NotificationManager) mService.mContext.getSystemService(NOTIFICATION_SERVICE); mNotificationTag = CHANNEL_PREFIX + mPackageName; mRequestCode = sNextRequestCode++; - mIconUtilities = new IconUtilities(mService.mContext); } void post() { @@ -126,8 +124,9 @@ class AlertWindowNotification { if (aInfo != null) { final Drawable drawable = pm.getApplicationIcon(aInfo); - if (drawable != null) { - final Bitmap bitmap = mIconUtilities.createIconBitmap(drawable); + int size = context.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size); + final Bitmap bitmap = ImageUtils.buildScaledBitmap(drawable, size, size); + if (bitmap != null) { builder.setLargeIcon(bitmap); } } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 90070c8f5068..5b685b4a0499 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -90,6 +90,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; +import static com.android.server.wm.WindowManagerInternal.KeyguardExitAnimationStartListener; import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM; import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE; @@ -257,6 +258,7 @@ public class AppTransition implements Dump { private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION; private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>(); + private KeyguardExitAnimationStartListener mKeyguardExitAnimationStartListener; private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor(); private int mLastClipRevealMaxTranslation; @@ -266,6 +268,7 @@ public class AppTransition implements Dump { private final boolean mLowRamRecentsEnabled; private final int mDefaultWindowAnimationStyleResId; + private boolean mOverrideTaskTransition; private RemoteAnimationController mRemoteAnimationController; @@ -445,7 +448,7 @@ public class AppTransition implements Dump { AnimationAdapter.STATUS_BAR_TRANSITION_DURATION); if (mRemoteAnimationController != null) { - mRemoteAnimationController.goodToGo(); + mRemoteAnimationController.goodToGo(transit); } return redoLayout; } @@ -508,6 +511,11 @@ public class AppTransition implements Dump { mListeners.remove(listener); } + void registerKeygaurdExitAnimationStartListener( + KeyguardExitAnimationStartListener listener) { + mKeyguardExitAnimationStartListener = listener; + } + public void notifyAppTransitionFinishedLocked(IBinder token) { for (int i = 0; i < mListeners.size(); i++) { mListeners.get(i).onAppTransitionFinishedLocked(token); @@ -971,7 +979,8 @@ public class AppTransition implements Dump { @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction, boolean freeform, WindowContainer container) { - if (mNextAppTransitionOverrideRequested && container.canCustomizeAppTransition()) { + if (mNextAppTransitionOverrideRequested + && (container.canCustomizeAppTransition() || mOverrideTaskTransition)) { mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM; } @@ -1175,7 +1184,8 @@ public class AppTransition implements Dump { } void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim, - IRemoteCallback startedCallback, IRemoteCallback endedCallback) { + IRemoteCallback startedCallback, IRemoteCallback endedCallback, + boolean overrideTaskTransaction) { if (canOverridePendingAppTransition()) { clear(); mNextAppTransitionOverrideRequested = true; @@ -1185,6 +1195,7 @@ public class AppTransition implements Dump { postAnimationCallback(); mNextAppTransitionCallback = startedCallback; mAnimationFinishedCallback = endedCallback; + mOverrideTaskTransition = overrideTaskTransaction; } } diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 582aeb36b00b..9ca7eca07dc6 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -102,6 +102,7 @@ public class AppTransitionController { private final DisplayContent mDisplayContent; private final WallpaperController mWallpaperControllerLocked; private RemoteAnimationDefinition mRemoteAnimationDefinition = null; + private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400; private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>(); @@ -437,10 +438,14 @@ public class AppTransitionController { return adapter; } } - if (mRemoteAnimationDefinition == null) { - return null; + if (mRemoteAnimationDefinition != null) { + final RemoteAnimationAdapter adapter = mRemoteAnimationDefinition.getAdapter( + transit, activityTypes); + if (adapter != null) { + return adapter; + } } - return mRemoteAnimationDefinition.getAdapter(transit, activityTypes); + return null; } /** @@ -709,11 +714,17 @@ public class AppTransitionController { applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp, voiceInteraction); + for (int i = 0; i < openingApps.size(); ++i) { + openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false; + } + for (int i = 0; i < closingApps.size(); ++i) { + closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false; + } + final AccessibilityController accessibilityController = mDisplayContent.mWmService.mAccessibilityController; if (accessibilityController != null) { - accessibilityController.onAppWindowTransitionLocked( - mDisplayContent.getDisplayId(), transit); + accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 3ab79525de97..112fe526f0a8 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -685,6 +685,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ private boolean mInEnsureActivitiesVisible = false; + // Used to indicate that the movement of child tasks to top will not move the display to top as + // well and thus won't change the top resumed / focused record + boolean mDontMoveToTop; + private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> { WindowStateAnimator winAnimator = w.mWinAnimator; final ActivityRecord activity = w.mActivityRecord; @@ -948,7 +952,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } final ActivityRecord activity = w.mActivityRecord; - if (activity != null) { + if (activity != null && activity.isVisibleRequested()) { activity.updateLetterboxSurface(w); final boolean updateAllDrawn = activity.updateDrawnWindowStates(w); if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) { @@ -1226,7 +1230,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (mWmService.mAccessibilityController != null) { final int prevDisplayId = prevDc != null ? prevDc.getDisplayId() : INVALID_DISPLAY; - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(prevDisplayId, + mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(prevDisplayId, getDisplayId()); } } @@ -1447,7 +1451,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } config = new Configuration(); computeScreenConfiguration(config); - } else if (currentConfig != null) { + } else if (currentConfig != null + // If waiting for a remote rotation, don't prematurely update configuration. + && !mDisplayRotation.isWaitingForRemoteRotation()) { // No obvious action we need to take, but if our current state mismatches the // activity manager's, update it, disregarding font scale, which should remain set // to the value of the previous configuration. @@ -1850,7 +1856,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onRotationChangedLocked(this); + mWmService.mAccessibilityController.onRotationChanged(this); } } @@ -3400,7 +3406,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } void updateAccessibilityOnWindowFocusChanged(AccessibilityController accessibilityController) { - accessibilityController.onWindowFocusChangedNotLocked(getDisplayId()); + accessibilityController.onWindowFocusChangedNot(getDisplayId()); } private static void onWindowFocusChanged(WindowState oldFocus, WindowState newFocus) { @@ -4963,7 +4969,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (!mLocationInParentWindow.equals(x, y)) { mLocationInParentWindow.set(x, y); if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(mDisplayId); + mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(mDisplayId); } notifyLocationInParentDisplayChanged(); } @@ -5960,36 +5966,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } - /** - * Returns the number of window tokens without surface on this display. A {@link WindowToken} - * won't have its {@link SurfaceControl} until a window is added to a {@link WindowToken}. - * The purpose of this method is to accumulate non-window containing {@link WindowToken}s and - * limit the usage if the count exceeds a number. - * - * @param callingUid app calling uid - * @return the number of window tokens without surface on this display - * @see WindowToken#addWindow(WindowState) - */ - int getWindowTokensWithoutSurfaceCount(int callingUid) { - List<WindowToken> tokens = new ArrayList<>(mTokenMap.values()); - int count = 0; - for (int i = tokens.size() - 1; i >= 0; i--) { - final WindowToken token = tokens.get(i); - if (callingUid != token.getOwnerUid()) { - continue; - } - // Skip if token is an Activity - if (token.asActivityRecord() != null) { - continue; - } - if (token.mSurfaceControl != null) { - continue; - } - count++; - } - return count; - } - MagnificationSpec getMagnificationSpec() { return mMagnificationSpec; } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 02e281f512a2..61fe023ae1b9 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -25,7 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.view.Display.TYPE_INTERNAL; -import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES; +import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; import static android.view.InsetsState.ITYPE_CAPTION_BAR; import static android.view.InsetsState.ITYPE_CLIMATE_BAR; @@ -35,7 +35,7 @@ import static android.view.InsetsState.ITYPE_LEFT_GESTURES; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_RIGHT_GESTURES; import static android.view.InsetsState.ITYPE_STATUS_BAR; -import static android.view.InsetsState.ITYPE_TOP_GESTURES; +import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES; import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT; import static android.view.ViewRootImpl.computeWindowBounds; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; @@ -1074,7 +1074,7 @@ public class DisplayPolicy { rect.bottom = rect.top + getStatusBarHeight(displayFrames); }; mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider); - mDisplayContent.setInsetProvider(ITYPE_TOP_GESTURES, win, frameProvider); + mDisplayContent.setInsetProvider(ITYPE_TOP_MANDATORY_GESTURES, win, frameProvider); mDisplayContent.setInsetProvider(ITYPE_TOP_TAPPABLE_ELEMENT, win, frameProvider); break; case TYPE_NAVIGATION_BAR: @@ -1101,7 +1101,7 @@ public class DisplayPolicy { (displayFrames, windowState, inOutFrame) -> inOutFrame.set(windowState.getFrame())); - mDisplayContent.setInsetProvider(ITYPE_BOTTOM_GESTURES, win, + mDisplayContent.setInsetProvider(ITYPE_BOTTOM_MANDATORY_GESTURES, win, (displayFrames, windowState, inOutFrame) -> { inOutFrame.top -= mBottomGestureAdditionalInset; }); diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 48e4df7a57ce..b106657dee99 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -481,12 +481,12 @@ public class DisplayRotation { mRotation = rotation; + mDisplayContent.setLayoutNeeded(); + mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT, mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION); - mDisplayContent.setLayoutNeeded(); - if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) { // The screen rotation animation uses a screenshot to freeze the screen while windows // resize underneath. When we are rotating seamlessly, we allow the elements to @@ -1499,6 +1499,21 @@ public class DisplayRotation { } @Override + public boolean canUseRotationResolver() { + if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) return false; + + switch (mCurrentAppOrientation) { + case ActivityInfo.SCREEN_ORIENTATION_FULL_USER: + case ActivityInfo.SCREEN_ORIENTATION_USER: + case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: + case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: + case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: + return true; + } + return false; + } + + @Override public void onProposedRotationChanged(int rotation) { ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation); Runnable r = mRunnableCache.get(rotation, null); diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index b82fdd237f2b..6d5abe1e2f31 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -16,11 +16,11 @@ package com.android.server.wm; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY; import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; -import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; -import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO; import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED; @@ -196,6 +196,14 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } + void setDontMoveToTop(DisplayContent dc, boolean dontMoveToTop) { + DisplayInfo displayInfo = dc.getDisplayInfo(); + SettingsProvider.SettingsEntry overrideSettings = + mSettingsProvider.getSettings(displayInfo); + overrideSettings.mDontMoveToTop = dontMoveToTop; + mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); + } + boolean shouldShowSystemDecorsLocked(DisplayContent dc) { if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) { // Default display should show system decors. @@ -274,6 +282,10 @@ class DisplayWindowSettings { final int forcedScalingMode = settings.mForcedScalingMode != null ? settings.mForcedScalingMode : FORCE_SCALING_MODE_AUTO; dc.mDisplayScalingDisabled = forcedScalingMode == FORCE_SCALING_MODE_DISABLED; + + boolean dontMoveToTop = settings.mDontMoveToTop != null + ? settings.mDontMoveToTop : false; + dc.mDontMoveToTop = dontMoveToTop; } /** @@ -358,6 +370,8 @@ class DisplayWindowSettings { Boolean mIgnoreOrientationRequest; @Nullable Boolean mIgnoreDisplayCutout; + @Nullable + Boolean mDontMoveToTop; SettingsEntry() {} @@ -432,6 +446,10 @@ class DisplayWindowSettings { mIgnoreDisplayCutout = other.mIgnoreDisplayCutout; changed = true; } + if (other.mDontMoveToTop != mDontMoveToTop) { + mDontMoveToTop = other.mDontMoveToTop; + changed = true; + } return changed; } @@ -515,6 +533,11 @@ class DisplayWindowSettings { mIgnoreDisplayCutout = delta.mIgnoreDisplayCutout; changed = true; } + if (delta.mDontMoveToTop != null + && delta.mDontMoveToTop != mDontMoveToTop) { + mDontMoveToTop = delta.mDontMoveToTop; + changed = true; + } return changed; } @@ -531,7 +554,8 @@ class DisplayWindowSettings { && mImePolicy == null && mFixedToUserRotation == null && mIgnoreOrientationRequest == null - && mIgnoreDisplayCutout == null; + && mIgnoreDisplayCutout == null + && mDontMoveToTop == null; } @Override @@ -553,7 +577,8 @@ class DisplayWindowSettings { && Objects.equals(mImePolicy, that.mImePolicy) && Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation) && Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest) - && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout); + && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout) + && Objects.equals(mDontMoveToTop, that.mDontMoveToTop); } @Override @@ -561,7 +586,8 @@ class DisplayWindowSettings { return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth, mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode, mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mImePolicy, - mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout); + mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout, + mDontMoveToTop); } @Override @@ -581,6 +607,7 @@ class DisplayWindowSettings { + ", mFixedToUserRotation=" + mFixedToUserRotation + ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest + ", mIgnoreDisplayCutout=" + mIgnoreDisplayCutout + + ", mDontMoveToTop=" + mDontMoveToTop + '}'; } } diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index 5f3ab43eca7c..737f8107f83f 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -405,6 +405,9 @@ class DisplayWindowSettingsProvider implements SettingsProvider { "ignoreOrientationRequest", null /* defaultValue */); settingsEntry.mIgnoreDisplayCutout = getBooleanAttribute(parser, "ignoreDisplayCutout", null /* defaultValue */); + settingsEntry.mDontMoveToTop = getBooleanAttribute(parser, + "dontMoveToTop", null /* defaultValue */); + fileData.mSettings.put(name, settingsEntry); } XmlUtils.skipCurrentTag(parser); @@ -496,6 +499,10 @@ class DisplayWindowSettingsProvider implements SettingsProvider { out.attributeBoolean(null, "ignoreDisplayCutout", settingsEntry.mIgnoreDisplayCutout); } + if (settingsEntry.mDontMoveToTop != null) { + out.attributeBoolean(null, "dontMoveToTop", + settingsEntry.mDontMoveToTop); + } out.endTag(null, "display"); } diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 803bec8941a8..de4bdaa57efa 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -47,7 +47,7 @@ public class DockedStackDividerController { mTouchRegion.set(touchRegion); // We need to report touchable region changes to accessibility. if (mDisplayContent.mWmService.mAccessibilityController != null) { - mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked( + mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved( mDisplayContent.getDisplayId()); } } diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index 2274a4a91595..99c9e798c605 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -340,7 +340,7 @@ public class Letterbox { } public void applySurfaceChanges(SurfaceControl.Transaction t) { - if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) { + if (!needsApplySurfaceChanges()) { // Nothing changed. return; } diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index a1d2072c0de7..392f27ead2bb 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -36,6 +36,7 @@ import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; +import android.view.WindowManager; import com.android.internal.protolog.ProtoLogImpl; import com.android.internal.protolog.common.ProtoLog; @@ -98,7 +99,7 @@ class RemoteAnimationController implements DeathRecipient { /** * Called when the transition is ready to be started, and all leashes have been set up. */ - void goodToGo() { + void goodToGo(@WindowManager.TransitionOldType int transit) { ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()"); if (mPendingAnimations.isEmpty() || mCanceled) { ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, @@ -123,11 +124,15 @@ class RemoteAnimationController implements DeathRecipient { // Create the remote wallpaper animation targets (if any) final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations(); + + // TODO(bc-unlock): Create the remote non app animation targets (if any) + final RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0]; + mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { try { linkToDeathOfRunner(); - mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets, - mFinishedCallback); + mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets, + wallpaperTargets, nonAppTargets, mFinishedCallback); } catch (RemoteException e) { Slog.e(TAG, "Failed to start remote animation", e); onAnimationFinished(); @@ -274,6 +279,7 @@ class RemoteAnimationController implements DeathRecipient { private void setRunningRemoteAnimation(boolean running) { final int pid = mRemoteAnimationAdapter.getCallingPid(); final int uid = mRemoteAnimationAdapter.getCallingUid(); + if (pid == 0) { throw new RuntimeException("Calling pid of remote animation was null"); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 61fec0d0ead9..2c97f783e14d 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -847,8 +847,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> Slog.i(TAG, ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces"); } - // Send any pending task-info changes that were queued-up during a layout deferment - mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges"); mWmService.openSurfaceTransaction(); try { @@ -865,6 +863,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } + // Send any pending task-info changes that were queued-up during a layout deferment + mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); mWmService.mAnimator.executeAfterPrepareSurfacesRunnables(); checkAppTransitionReady(surfacePlacer); @@ -933,7 +933,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } win.destroySurfaceUnchecked(); - win.mWinAnimator.destroyPreservedSurfaceLocked(win.getSyncTransaction()); } while (i > 0); mWmService.mDestroySurface.clear(); } diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java index 6df453684734..65671959e884 100644 --- a/services/core/java/com/android/server/wm/SafeActivityOptions.java +++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java @@ -253,6 +253,20 @@ public class SafeActivityOptions { throw new SecurityException(msg); } + // Check if the caller is allowed to override any app transition animation. + final boolean overrideTaskTransition = options.getOverrideTaskTransition(); + if (aInfo != null && overrideTaskTransition) { + final int startTasksFromRecentsPerm = ActivityTaskManagerService.checkPermission( + START_TASKS_FROM_RECENTS, callingPid, callingUid); + if (startTasksFromRecentsPerm != PERMISSION_GRANTED) { + final String msg = "Permission Denial: starting " + getIntentString(intent) + + " from " + callerApp + " (pid=" + callingPid + + ", uid=" + callingUid + ") with overrideTaskTransition=true"; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + } + // Check permission for remote animations final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter(); if (adapter != null && supervisor.mService.checkPermission( diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java index 62c0527dfe1b..0902948bf559 100644 --- a/services/core/java/com/android/server/wm/ShellRoot.java +++ b/services/core/java/com/android/server/wm/ShellRoot.java @@ -191,7 +191,7 @@ public class ShellRoot { } } if (mDisplayContent.mWmService.mAccessibilityController != null) { - mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked( + mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved( mDisplayContent.getDisplayId()); } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 30af0eda17fc..e44a028c897f 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -77,7 +77,6 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP; import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; -import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; @@ -145,7 +144,6 @@ import static com.android.server.wm.TaskProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainerChildProto.TASK; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -921,10 +919,8 @@ class Task extends WindowContainer<WindowContainer> { return; } - if (isLeafTask()) { - // This task is going away, so save the last state if necessary. - saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent()); - } + // This task is going away, so save the last state if necessary. + saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent()); // TODO: VI what about activity? final boolean isVoiceSession = voiceSession != null; @@ -2459,14 +2455,16 @@ class Task extends WindowContainer<WindowContainer> { /** * Saves launching state if necessary so that we can launch the activity to its latest state. - * It only saves state if this task has been shown to user and it's in fullscreen or freeform - * mode on freeform displays. */ private void saveLaunchingStateIfNeeded() { saveLaunchingStateIfNeeded(getDisplayContent()); } private void saveLaunchingStateIfNeeded(DisplayContent display) { + if (!isLeafTask()) { + return; + } + if (!getHasBeenVisible()) { // Not ever visible to user. return; @@ -2867,16 +2865,6 @@ class Task extends WindowContainer<WindowContainer> { // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent". outBounds.setEmpty(); computeLetterboxBounds(outBounds, newParentConfig); - // Since the task is letterboxed due to mismatched orientation against its parent, - // sandbox max bounds to the app bounds. - if (!outBounds.isEmpty()) { - if (DEBUG_CONFIGURATION) { - ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds due to mismatched " - + "orientation with parent, to %s vs DisplayArea %s", outBounds, - getDisplayArea() != null ? getDisplayArea().getBounds() : "null"); - } - getResolvedOverrideConfiguration().windowConfiguration.setMaxBounds(outBounds); - } } /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */ diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index e18219ef4f46..622fe05b6170 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -497,7 +497,7 @@ class TaskChangeNotificationController { void notifyTaskDisplayChanged(int taskId, int newDisplayId) { final Message msg = mHandler.obtainMessage(NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG, taskId, newDisplayId); - forAllLocalListeners(mNotifyTaskStackChanged, msg); + forAllLocalListeners(mNotifyTaskDisplayChanged, msg); msg.sendToTarget(); } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 41854072985c..40248c43fe5d 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -47,6 +47,7 @@ import android.content.Intent; import android.os.UserHandle; import android.util.IntArray; import android.util.Slog; +import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.window.WindowContainerTransaction; @@ -403,7 +404,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } // We don't allow untrusted display to top when root task moves to top, // until user tapping this display to change display position as top intentionally. - if (!mDisplayContent.isTrusted() && !getParent().isOnTop()) { + // + // Displays with {@code mDontMoveToTop} property set to {@code true} won't be + // allowed to top neither. + if ((!mDisplayContent.isTrusted() || mDisplayContent.mDontMoveToTop) + && !getParent().isOnTop()) { includingParents = false; } final int targetPosition = findPositionForRootTask(position, child, false /* adding */); @@ -905,6 +910,13 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } } + @Override + RemoteAnimationTarget createRemoteAnimationTarget( + RemoteAnimationController.RemoteAnimationRecord record) { + final ActivityRecord activity = getTopMostActivity(); + return activity != null ? activity.createRemoteAnimationTarget(record) : null; + } + SurfaceControl getSplitScreenDividerAnchor() { return mSplitScreenDividerAnchor; } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 46aea23beaf6..98eb11f8a970 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -372,8 +372,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe dc.mWallpaperController.startWallpaperAnimation(anim); } } - } - if (transit == TRANSIT_KEYGUARD_GOING_AWAY) { dc.startKeyguardExitOnNonAppWindows( (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0, (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0, diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index f572e8e2f0aa..43303d4a5d7e 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -17,7 +17,6 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; @@ -51,7 +50,7 @@ class WallpaperWindowToken extends WindowToken { WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit, DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options) { - super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, INVALID_UID, + super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, false /* roundedCornerOverlay */, false /* fromClientToken */, options); dc.mWallpaperController.addWallpaperToken(this); setWindowingMode(WINDOWING_MODE_FULLSCREEN); diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 52ed2788d795..91a6664e0b36 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -164,7 +164,7 @@ public class WindowAnimator { dc.checkAppWindowsReadyToShow(); if (accessibilityController != null) { - accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId, + accessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction); } } @@ -220,8 +220,6 @@ public class WindowAnimator { mRemoveReplacedWindows = false; } - mService.destroyPreservedSurfaceLocked(); - mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); executeAfterPrepareSurfacesRunnables(); diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 1f438183754f..e183ea0d81ab 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -23,12 +23,15 @@ import android.content.Context; import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManagerInternal; +import android.os.Bundle; import android.os.IBinder; import android.view.Display; import android.view.IInputFilter; +import android.view.IRemoteAnimationFinishedCallback; import android.view.IWindow; import android.view.InputChannel; import android.view.MagnificationSpec; +import android.view.RemoteAnimationTarget; import android.view.WindowInfo; import android.view.WindowManager.DisplayImePolicy; @@ -46,6 +49,41 @@ import java.util.List; public abstract class WindowManagerInternal { /** + * Interface for accessibility features implemented by AccessibilityController inside + * WindowManager. + */ + public interface AccessibilityControllerInternal { + /** + * Enable the accessibility trace logging. + */ + void startTrace(); + + /** + * Disable the accessibility trace logging. + */ + void stopTrace(); + + /** + * Is trace enabled or not. + */ + boolean isEnabled(); + + /** + * Add an accessibility trace entry. + * + * @param where A string to identify this log entry, which can be used to filter/search + * through the tracing file. + * @param callingParams The parameters for the method to be logged. + * @param a11yDump The proto byte array for a11y state when the entry is generated. + * @param callingUid The calling uid. + * @param stackTrace The stack trace, null if not needed. + */ + void logTrace( + String where, String callingParams, byte[] a11yDump, int callingUid, + StackTraceElement[] stackTrace); + } + + /** * Interface to receive a callback when the windows reported for * accessibility changed. */ @@ -154,6 +192,21 @@ public abstract class WindowManagerInternal { } /** + * An interface to be notified when keyguard exit animation should start. + */ + public interface KeyguardExitAnimationStartListener { + /** + * Called when keyguard exit animation should start. + * @param apps The list of apps to animate. + * @param wallpapers The list of wallpapers to animate. + * @param finishedCallback The callback to invoke when the animation is finished. + */ + void onAnimationStart(RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + IRemoteAnimationFinishedCallback finishedCallback); + } + + /** * An interface to be notified about hardware keyboard status. */ public interface OnHardKeyboardStatusChangeListener { @@ -207,6 +260,11 @@ public abstract class WindowManagerInternal { } /** + * Request the interface to access features implemented by AccessibilityController. + */ + public abstract AccessibilityControllerInternal getAccessibilityController(); + + /** * Request that the window manager call * {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager} * within a surface transaction at a later time. @@ -351,8 +409,10 @@ public abstract class WindowManagerInternal { * @param token The token to add. * @param type The window type. * @param displayId The display to add the token to. + * @param options A bundle used to pass window-related options. */ - public abstract void addWindowToken(android.os.IBinder token, int type, int displayId); + public abstract void addWindowToken(@NonNull android.os.IBinder token, int type, int displayId, + @Nullable Bundle options); /** * Removes a window token. @@ -372,6 +432,14 @@ public abstract class WindowManagerInternal { public abstract void registerAppTransitionListener(AppTransitionListener listener); /** + * Registers a listener to be notified to start the keyguard exit animation. + * + * @param listener The listener to register. + */ + public abstract void registerKeyguardExitAnimationStartListener( + KeyguardExitAnimationStartListener listener); + + /** * Reports that the password for the given user has changed. */ public abstract void reportPasswordChanged(int userId); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 844edd08df17..e456a4efce06 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -23,7 +23,6 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.Manifest.permission.RESTRICTED_VR_ACCESS; -import static android.Manifest.permission.STATUS_BAR_SERVICE; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; @@ -35,7 +34,6 @@ import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEME import static android.content.pm.PackageManager.FEATURE_PC; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; -import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; import static android.os.Process.myPid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; @@ -82,8 +80,6 @@ import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_RELAUNCH; import static android.view.WindowManagerGlobal.ADD_OKAY; -import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS; -import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY; import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; @@ -405,6 +401,8 @@ public class WindowManagerService extends IWindowManager.Stub // trying to apply a new one. private static final boolean ALWAYS_KEEP_CURRENT = true; + static final int LOGTAG_INPUT_FOCUS = 62001; + /** * Restrict ability of activities overriding transition animation in a way such that * an activity can do it only when the transition happens within a same task. @@ -413,7 +411,6 @@ public class WindowManagerService extends IWindowManager.Stub */ private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY = "persist.wm.disable_custom_task_animation"; - static final int LOGTAG_INPUT_FOCUS = 62001; /** * @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY @@ -421,6 +418,19 @@ public class WindowManagerService extends IWindowManager.Stub static boolean sDisableCustomTaskAnimationProperty = SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true); + /** + * Run Keyguard animation as remote animation in System UI instead of local animation in + * the server process. + */ + private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY = + "persist.wm.enable_remote_keyguard_animation"; + + /** + * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY + */ + public static boolean sEnableRemoteKeyguardAnimation = + SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false); + private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY = "ro.sf.disable_triple_buffer"; @@ -444,12 +454,6 @@ public class WindowManagerService extends IWindowManager.Stub private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000; - /** The maximum count of window tokens without surface that an app can register. */ - private static final int MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE = 5; - - /** System UI can create more window context... */ - private static final int SYSTEM_UI_MULTIPLIER = 2; - /** * Override of task letterbox aspect ratio that is set via ADB with * set-task-letterbox-aspect-ratio or via {@link @@ -626,13 +630,6 @@ public class WindowManagerService extends IWindowManager.Stub final ArrayList<WindowState> mDestroySurface = new ArrayList<>(); /** - * Windows with a preserved surface waiting to be destroyed. These windows - * are going through a surface change. We keep the old surface around until - * the first frame on the new surface finishes drawing. - */ - final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>(); - - /** * This is set when we have run out of memory, and will either be an empty * list or contain windows that need to be force removed. */ @@ -1605,7 +1602,7 @@ public class WindowManagerService extends IWindowManager.Stub final Bundle options = mWindowContextListenerController .getOptions(windowContextToken); token = new WindowToken(this, binder, type, false /* persistOnEmpty */, - displayContent, session.mCanAddInternalSystemWindow, callingUid, + displayContent, session.mCanAddInternalSystemWindow, isRoundedCornerOverlay, true /* fromClientToken */, options); } else { final IBinder binder = attrs.token != null ? attrs.token : client.asBinder(); @@ -2171,6 +2168,7 @@ public class WindowManagerService extends IWindowManager.Stub void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets, Rect visibleInsets, Region touchableRegion) { + int uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -2196,8 +2194,8 @@ public class WindowManagerService extends IWindowManager.Stub // We need to report touchable region changes to accessibility. if (mAccessibilityController != null) { - mAccessibilityController.onSomeWindowResizedOrMovedLocked( - w.getDisplayContent().getDisplayId()); + mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid( + uid, w.getDisplayContent().getDisplayId()); } } } @@ -2211,7 +2209,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mAccessibilityController != null) { WindowState window = mWindowMap.get(token); if (window != null) { - mAccessibilityController.onRectangleOnScreenRequestedLocked( + mAccessibilityController.onRectangleOnScreenRequested( window.getDisplayId(), rectangle); } } @@ -2314,8 +2312,8 @@ public class WindowManagerService extends IWindowManager.Stub if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0) && (mAccessibilityController != null)) { // No move or resize, but the controller checks for title changes as well - mAccessibilityController.onSomeWindowResizedOrMovedLocked( - win.getDisplayContent().getDisplayId()); + mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid( + uid, win.getDisplayContent().getDisplayId()); } if ((privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) { @@ -2330,7 +2328,6 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs); - winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0; if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) { winAnimator.mAlpha = attrs.alpha; } @@ -2616,7 +2613,7 @@ public class WindowManagerService extends IWindowManager.Stub win.destroySurface(false, stopped); } if (mAccessibilityController != null) { - mAccessibilityController.onWindowTransitionLocked(win, transit); + mAccessibilityController.onWindowTransition(win, transit); } return focusMayChange; @@ -2709,91 +2706,36 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void addWindowToken(IBinder binder, int type, int displayId) { - addWindowTokenWithOptions(binder, type, displayId, null /* options */, - null /* packageName */, false /* fromClientToken */); - } - - @Override - public int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, - String packageName) { - if (tokenCountExceed()) { - return ADD_TOO_MANY_TOKENS; + public void addWindowToken(@NonNull IBinder binder, int type, int displayId, + @Nullable Bundle options) { + if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - return addWindowTokenWithOptions(binder, type, displayId, options, packageName, - true /* fromClientToken */); - } - private boolean tokenCountExceed() { - final int callingUid = Binder.getCallingUid(); - // Don't check if caller is from system server. - if (callingUid == myPid()) { - return false; - } - final int limit = - (checkCallingPermission(STATUS_BAR_SERVICE, "addWindowTokenWithOptions")) - ? MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE * SYSTEM_UI_MULTIPLIER - : MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE; synchronized (mGlobalLock) { - int[] count = new int[1]; - mRoot.forAllDisplays(d -> count[0] += d.getWindowTokensWithoutSurfaceCount(callingUid)); - return count[0] >= limit; - } - } - - private int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, - String packageName, boolean fromClientToken) { - final boolean callerCanManageAppTokens = - checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()"); - // WindowContext users usually don't hold MANAGE_APP_TOKEN permission. Check permissions - // by checkAddPermission. - if (!callerCanManageAppTokens) { - final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */, - packageName, new int[1]); - if (res != ADD_OKAY) { - return res; + final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */); + if (dc == null) { + ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s" + + " for non-exiting displayId=%d", binder, displayId); + return; } - } - final int callingUid = Binder.getCallingUid(); - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - if (!callerCanManageAppTokens) { - if (packageName == null || !unprivilegedAppCanCreateTokenWith( - null /* parentWindow */, callingUid, type, type, binder, - packageName)) { - throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); - } - } - - final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */); - if (dc == null) { - ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s" - + " for non-exiting displayId=%d", binder, displayId); - return WindowManagerGlobal.ADD_INVALID_DISPLAY; - } - - WindowToken token = dc.getWindowToken(binder); - if (token != null) { - ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s" - + " for already created window token: %s" - + " displayId=%d", binder, token, displayId); - return WindowManagerGlobal.ADD_DUPLICATE_ADD; - } - // TODO(window-container): Clean up dead tokens - if (type == TYPE_WALLPAPER) { - new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens, - options); - } else { - new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens, - callingUid, false /* roundedCornerOverlay */, fromClientToken, options); - } + WindowToken token = dc.getWindowToken(binder); + if (token != null) { + ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s" + + " for already created window token: %s" + + " displayId=%d", binder, token, displayId); + return; + } + if (type == TYPE_WALLPAPER) { + new WallpaperWindowToken(this, binder, true, dc, + true /* ownerCanManageAppTokens */, options); + } else { + new WindowToken(this, binder, type, true /* persistOnEmpty */, dc, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, + false /* fromClientToken */, options); } - } finally { - Binder.restoreCallingIdentity(origId); } - return WindowManagerGlobal.ADD_OKAY; } /** @@ -2873,38 +2815,26 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void removeWindowToken(IBinder binder, int displayId) { - final boolean callerCanManageAppTokens = - checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()"); - final WindowToken windowToken; - synchronized (mGlobalLock) { - windowToken = mRoot.getWindowToken(binder); - } - if (windowToken == null) { - ProtoLog.w(WM_ERROR, - "removeWindowToken: Attempted to remove non-existing token: %s", binder); - return; - } - final int callingUid = Binder.getCallingUid(); - - // If MANAGE_APP_TOKEN permission is not held(usually from WindowContext), callers can only - // remove the window tokens which they added themselves. - if (!callerCanManageAppTokens && (windowToken.getOwnerUid() == INVALID_UID - || callingUid != windowToken.getOwnerUid())) { - throw new SecurityException("removeWindowToken: Requires MANAGE_APP_TOKENS permission" - + " to remove token owned by another uid"); + if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s" + " for non-exiting displayId=%d", binder, displayId); return; } - - dc.removeWindowToken(binder); + final WindowToken token = dc.removeWindowToken(binder); + if (token == null) { + ProtoLog.w(WM_ERROR, + "removeWindowToken: Attempted to remove non-existing token: %s", + binder); + return; + } dc.getInputMonitor().updateInputWindowsLw(true /*force*/); } } finally { @@ -5507,14 +5437,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - void destroyPreservedSurfaceLocked() { - for (int i = mDestroyPreservedSurface.size() - 1; i >= 0 ; i--) { - final WindowState w = mDestroyPreservedSurface.get(i); - w.mWinAnimator.destroyPreservedSurfaceLocked(w.getSyncTransaction()); - } - mDestroyPreservedSurface.clear(); - } - // ------------------------------------------------------------- // IWindowManager API // ------------------------------------------------------------- @@ -7157,6 +7079,7 @@ public class WindowManagerService extends IWindowManager.Stub checkCallerOwnsDisplay(displayId); synchronized (mGlobalLock) { + int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { final WindowState win = windowForClientLocked(null, client, false); @@ -7168,8 +7091,8 @@ public class WindowManagerService extends IWindowManager.Stub // Notifies AccessibilityController to re-compute the window observer of // this embedded display if (mAccessibilityController != null) { - mAccessibilityController.handleWindowObserverOfEmbeddedDisplayLocked(displayId, - win); + mAccessibilityController.handleWindowObserverOfEmbeddedDisplay( + displayId, win, uid); } } finally { Binder.restoreCallingIdentity(token); @@ -7535,6 +7458,12 @@ public class WindowManagerService extends IWindowManager.Stub private final class LocalService extends WindowManagerInternal { @Override + public AccessibilityControllerInternal getAccessibilityController() { + return AccessibilityController.getAccessibilityControllerInternal( + WindowManagerService.this); + } + + @Override public void clearSnapshotCache() { synchronized (mGlobalLock) { mTaskSnapshotController.clearSnapshotCache(); @@ -7552,7 +7481,7 @@ public class WindowManagerService extends IWindowManager.Stub public void setMagnificationSpec(int displayId, MagnificationSpec spec) { synchronized (mGlobalLock) { if (mAccessibilityController != null) { - mAccessibilityController.setMagnificationSpecLocked(displayId, spec); + mAccessibilityController.setMagnificationSpec(displayId, spec); } else { throw new IllegalStateException("Magnification callbacks not set!"); } @@ -7563,7 +7492,7 @@ public class WindowManagerService extends IWindowManager.Stub public void setForceShowMagnifiableBounds(int displayId, boolean show) { synchronized (mGlobalLock) { if (mAccessibilityController != null) { - mAccessibilityController.setForceShowMagnifiableBoundsLocked(displayId, show); + mAccessibilityController.setForceShowMagnifiableBounds(displayId, show); } else { throw new IllegalStateException("Magnification callbacks not set!"); } @@ -7574,8 +7503,7 @@ public class WindowManagerService extends IWindowManager.Stub public void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion) { synchronized (mGlobalLock) { if (mAccessibilityController != null) { - mAccessibilityController.getMagnificationRegionLocked(displayId, - magnificationRegion); + mAccessibilityController.getMagnificationRegion(displayId, magnificationRegion); } else { throw new IllegalStateException("Magnification callbacks not set!"); } @@ -7591,7 +7519,7 @@ public class WindowManagerService extends IWindowManager.Stub } MagnificationSpec spec = null; if (mAccessibilityController != null) { - spec = mAccessibilityController.getMagnificationSpecForWindowLocked(windowState); + spec = mAccessibilityController.getMagnificationSpecForWindow(windowState); } if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) { return null; @@ -7613,9 +7541,9 @@ public class WindowManagerService extends IWindowManager.Stub mAccessibilityController = new AccessibilityController( WindowManagerService.this); } - boolean result = mAccessibilityController.setMagnificationCallbacksLocked( + boolean result = mAccessibilityController.setMagnificationCallbacks( displayId, callbacks); - if (!mAccessibilityController.hasCallbacksLocked()) { + if (!mAccessibilityController.hasCallbacks()) { mAccessibilityController = null; } return result; @@ -7631,9 +7559,9 @@ public class WindowManagerService extends IWindowManager.Stub WindowManagerService.this); } final boolean result = - mAccessibilityController.setWindowsForAccessibilityCallbackLocked( + mAccessibilityController.setWindowsForAccessibilityCallback( displayId, callback); - if (!mAccessibilityController.hasCallbacksLocked()) { + if (!mAccessibilityController.hasCallbacks()) { mAccessibilityController = null; } return result; @@ -7722,8 +7650,9 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void addWindowToken(IBinder token, int type, int displayId) { - WindowManagerService.this.addWindowToken(token, type, displayId); + public void addWindowToken(IBinder token, int type, int displayId, + @Nullable Bundle options) { + WindowManagerService.this.addWindowToken(token, type, displayId, options); } @Override @@ -7766,6 +7695,15 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void registerKeyguardExitAnimationStartListener( + KeyguardExitAnimationStartListener listener) { + synchronized (mGlobalLock) { + getDefaultDisplayContentLocked().mAppTransition + .registerKeygaurdExitAnimationStartListener(listener); + } + } + + @Override public void reportPasswordChanged(int userId) { mKeyguardDisableHandler.updateKeyguardEnabled(userId); } @@ -7822,7 +7760,7 @@ public class WindowManagerService extends IWindowManager.Stub accessibilityController = mAccessibilityController; } if (accessibilityController != null) { - accessibilityController.performComputeChangedWindowsNotLocked(displayId, true); + accessibilityController.performComputeChangedWindowsNot(displayId, true); } } diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java index da31bb253831..5ef9420a10d8 100644 --- a/services/core/java/com/android/server/wm/WindowOrientationListener.java +++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java @@ -16,25 +16,36 @@ package com.android.server.wm; +import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER; + import static com.android.server.wm.WindowOrientationListenerProto.ENABLED; import static com.android.server.wm.WindowOrientationListenerProto.ROTATION; +import android.app.ActivityThread; import android.content.Context; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.os.CancellationSignal; import android.os.Handler; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.UserHandle; +import android.provider.DeviceConfig; +import android.provider.Settings; +import android.rotationresolver.RotationResolverInternal; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Surface; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.LocalServices; import java.io.PrintWriter; import java.util.List; +import java.util.Set; /** * A special helper class used by the WindowManager @@ -55,6 +66,9 @@ public abstract class WindowOrientationListener { private static final boolean USE_GRAVITY_SENSOR = false; private static final int DEFAULT_BATCH_LATENCY = 100000; + private static final int DEFAULT_ROTATION_RESOLVER_ENABLED = 0; // disabled + private static final String KEY_ROTATION_RESOLVER_TIMEOUT = "rotation_resolver_timeout_millis"; + private static final long DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS = 700L; private Handler mHandler; private SensorManager mSensorManager; @@ -62,7 +76,13 @@ public abstract class WindowOrientationListener { private int mRate; private String mSensorType; private Sensor mSensor; - private OrientationJudge mOrientationJudge; + + @VisibleForTesting + OrientationJudge mOrientationJudge; + + @VisibleForTesting + RotationResolverInternal mRotationResolverService; + private int mCurrentRotation = -1; private final Context mContext; private final WindowManagerConstants mConstants; @@ -256,6 +276,32 @@ public abstract class WindowOrientationListener { } /** + * Returns true if the current status of the phone is suitable for using rotation resolver + * service. + * + * To reduce the power consumption of rotation resolver service, rotation query should run less + * frequently than other low power orientation sensors. This method is used to check whether + * the current status of the phone is necessary to request a suggested screen rotation from the + * rotation resolver service. Note that it always returns {@code false} in the base class. It + * should be overridden in the derived classes. + */ + public boolean canUseRotationResolver() { + return false; + } + + /** + * Returns true if the rotation resolver feature is enabled by setting. It means {@link + * WindowOrientationListener} will then ask {@link RotationResolverInternal} for the appropriate + * screen rotation. + */ + @VisibleForTesting + boolean isRotationResolverEnabled() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.CAMERA_AUTOROTATE, DEFAULT_ROTATION_RESOLVER_ENABLED, + UserHandle.USER_CURRENT) == 1; + } + + /** * Called when the rotation view of the device has changed. * * This method is called whenever the orientation becomes certain of an orientation. @@ -1045,6 +1091,30 @@ public abstract class WindowOrientationListener { private int mProposedRotation = -1; private int mDesiredRotation = -1; private boolean mRotationEvaluationScheduled; + private long mRotationResolverTimeoutMillis; + + OrientationSensorJudge() { + super(); + setupRotationResolverParameters(); + } + + private void setupRotationResolverParameters() { + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_WINDOW_MANAGER, + ActivityThread.currentApplication().getMainExecutor(), (properties) -> { + final Set<String> keys = properties.getKeyset(); + if (keys.contains(KEY_ROTATION_RESOLVER_TIMEOUT)) { + readRotationResolverParameters(); + } + }); + readRotationResolverParameters(); + } + + private void readRotationResolverParameters() { + mRotationResolverTimeoutMillis = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_WINDOW_MANAGER, + KEY_ROTATION_RESOLVER_TIMEOUT, + DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS); + } @Override public int getProposedRotationLocked() { @@ -1069,19 +1139,13 @@ public abstract class WindowOrientationListener { @Override public void onSensorChanged(SensorEvent event) { - int newRotation; - int reportedRotation = (int) event.values[0]; if (reportedRotation < 0 || reportedRotation > 3) { return; } - synchronized (mLock) { - mDesiredRotation = reportedRotation; - newRotation = evaluateRotationChangeLocked(); - } - if (newRotation >= 0) { - onProposedRotationChanged(newRotation); + // Log raw sensor rotation. + if (evaluateRotationChangeLocked() >= 0) { if (mConstants.mRawSensorLoggingEnabled) { FrameworkStatsLog.write( FrameworkStatsLog.DEVICE_ROTATED, @@ -1089,6 +1153,35 @@ public abstract class WindowOrientationListener { rotationToLogEnum(reportedRotation)); } } + + if (isRotationResolverEnabled() && canUseRotationResolver()) { + if (mRotationResolverService == null) { + mRotationResolverService = LocalServices.getService( + RotationResolverInternal.class); + } + + final CancellationSignal cancellationSignal = new CancellationSignal(); + mRotationResolverService.resolveRotation( + new RotationResolverInternal.RotationResolverCallbackInternal() { + @Override + public void onSuccess(int result) { + finalizeRotation(result); + } + + @Override + public void onFailure(int error) { + finalizeRotation(reportedRotation); + } + }, + reportedRotation, + mCurrentRotation, + mRotationResolverTimeoutMillis, + cancellationSignal); + getHandler().postDelayed(cancellationSignal::cancel, + mRotationResolverTimeoutMillis); + } else { + finalizeRotation(reportedRotation); + } } @Override @@ -1131,6 +1224,17 @@ public abstract class WindowOrientationListener { return -1; } + private void finalizeRotation(int reportedRotation) { + int newRotation; + synchronized (mLock) { + mDesiredRotation = reportedRotation; + newRotation = evaluateRotationChangeLocked(); + } + if (newRotation >= 0) { + onProposedRotationChanged(newRotation); + } + } + private boolean isDesiredRotationAcceptableLocked(long now) { if (mTouching) { return false; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 661118ff1990..fc1c7edb234c 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -104,7 +104,6 @@ import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED; import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM; import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME; -import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; @@ -1722,7 +1721,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override boolean isVisibleRequested() { - return isVisible(); + return isVisible() && (mActivityRecord == null || mActivityRecord.isVisibleRequested()); } /** @@ -2021,7 +2020,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final int winTransit = TRANSIT_EXIT; mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */); if (accessibilityController != null) { - accessibilityController.onWindowTransitionLocked(this, winTransit); + accessibilityController.onWindowTransition(this, winTransit); } } setDisplayLayoutNeeded(); @@ -2035,7 +2034,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isVisibleNow()) { mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false); if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT); + mWmService.mAccessibilityController.onWindowTransition(this, TRANSIT_EXIT); } changed = true; if (displayContent != null) { @@ -2113,7 +2112,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(getDisplayId()); + mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId()); } updateLocationInParentDisplayIfNeeded(); @@ -2133,7 +2132,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP && !mAnimatingExit && (mWindowFrames.mRelFrame.top != mWindowFrames.mLastRelFrame.top || mWindowFrames.mRelFrame.left != mWindowFrames.mLastRelFrame.left) - && (!mIsChildWindow || !getParentWindow().hasMoved()); + && (!mIsChildWindow || !getParentWindow().hasMoved()) + && !mWmService.mAtmService.getTransitionController().isCollecting(); } boolean isObscuringDisplay() { @@ -2243,7 +2243,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP disposeInputChannel(); - mWinAnimator.destroyDeferredSurfaceLocked(mTmpTransaction); mWinAnimator.destroySurfaceLocked(mTmpTransaction); mTmpTransaction.apply(); mSession.windowRemovedLocked(); @@ -2347,7 +2346,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWmService.requestTraversal(); } if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit); + mWmService.mAccessibilityController.onWindowTransition(this, transit); } } final boolean isAnimating = mAnimatingExit || isAnimating(TRANSITION | PARENTS, @@ -3309,11 +3308,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return destroyedSomething; } - if (appStopped || mWindowRemovalAllowed) { - mWinAnimator.destroyPreservedSurfaceLocked(mTmpTransaction); - mTmpTransaction.apply(); - } - if (mDestroying) { ProtoLog.e(WM_DEBUG_ADD_REMOVE, "win=%s" + " destroySurfaces: appStopped=%b" @@ -3642,6 +3636,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mActivityRecord != null && mActivityRecord.isRelaunching()) { return; } + // If the activity is invisible or going invisible, don't report either since it is going + // away. This is likely during a transition so we want to preserve the original state. + if (mActivityRecord != null && !mActivityRecord.isVisibleRequested()) { + return; + } if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag()); @@ -3679,7 +3678,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP displayId); if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(displayId); + mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(displayId); } updateLocationInParentDisplayIfNeeded(); } catch (RemoteException e) { @@ -4782,7 +4781,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(getDisplayId()); + mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId()); } if (!isSelfOrAncestorWindowAnimatingExit()) { @@ -5001,23 +5000,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } - // When we change the Surface size, in scenarios which may require changing - // the surface position in sync with the resize, we use a preserved surface - // so we can freeze it while waiting for the client to report draw on the newly - // sized surface. At the moment this logic is only in place for switching - // in and out of the big surface for split screen resize. if (isDragResizeChanged()) { setDragResizing(); - // We can only change top level windows to the full-screen surface when - // resizing (as we only have one full-screen surface). So there is no need - // to preserve and destroy windows which are attached to another, they - // will keep their surface and its size may change over time. - if (mHasSurface && !isChildWindow()) { - mWinAnimator.preserveSurfaceLocked(getSyncTransaction()); - result |= RELAYOUT_RES_SURFACE_CHANGED | - RELAYOUT_RES_FIRST_TIME; - scheduleAnimation(); - } } final boolean freeformResizing = isDragResizing() && getResizeMode() == DRAG_RESIZE_MODE_FREEFORM; @@ -5326,7 +5310,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP updateSurfacePositionNonOrganized(); // Send information to SufaceFlinger about the priority of the current window. updateFrameRateSelectionPriorityIfNeeded(); - updateGlobalScaleIfNeeded(); + if (isVisibleRequested()) updateGlobalScaleIfNeeded(); mWinAnimator.prepareSurfaceLocked(getSyncTransaction()); super.prepareSurfaces(); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index c8b940a831a8..2da3dda831e1 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -116,15 +116,7 @@ class WindowStateAnimator { boolean mAnimationIsEntrance; WindowSurfaceController mSurfaceController; - private WindowSurfaceController mPendingDestroySurface; - /** - * Set if the client has asked that the destroy of its surface be delayed - * until it explicitly says it is okay. - */ - boolean mSurfaceDestroyDeferred; - - private boolean mDestroyPreservedSurfaceUponRedraw; float mShownAlpha = 0; float mAlpha = 0; float mLastAlpha = 0; @@ -257,11 +249,6 @@ class WindowStateAnimator { //dump(); mLastHidden = true; - // We may have a preserved surface which we no longer need. If there was a quick - // VISIBLE, GONE, VISIBLE, GONE sequence, the surface may never draw, so we don't mark - // it to be destroyed in prepareSurfaceLocked. - markPreservedSurfaceForDestroy(); - if (mSurfaceController != null) { mSurfaceController.hide(transaction, reason); } @@ -323,70 +310,6 @@ class WindowStateAnimator { return result; } - void preserveSurfaceLocked(SurfaceControl.Transaction t) { - if (mDestroyPreservedSurfaceUponRedraw) { - // This could happen when switching the surface mode very fast. For example, - // we preserved a surface when dragResizing changed to true. Then before the - // preserved surface is removed, dragResizing changed to false again. - // In this case, we need to leave the preserved surface alone, and destroy - // the actual surface, so that the createSurface call could create a surface - // of the proper size. The preserved surface will still be removed when client - // finishes drawing to the new surface. - mSurfaceDestroyDeferred = false; - - // Make sure to reparent any children of the new surface back to the preserved - // surface before destroying it. - if (mSurfaceController != null && mPendingDestroySurface != null) { - mPostDrawTransaction.reparentChildren( - mSurfaceController.mSurfaceControl, - mPendingDestroySurface.mSurfaceControl).apply(); - } - destroySurfaceLocked(t); - mSurfaceDestroyDeferred = true; - return; - } - ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SET FREEZE LAYER: %s", mWin); - if (mSurfaceController != null) { - // Our SurfaceControl is always at layer 0 within the parent Surface managed by - // window-state. We want this old Surface to stay on top of the new one - // until we do the swap, so we place it at a positive layer. - t.setLayer(mSurfaceController.mSurfaceControl, PRESERVED_SURFACE_LAYER); - } - mDestroyPreservedSurfaceUponRedraw = true; - mSurfaceDestroyDeferred = true; - destroySurfaceLocked(t); - } - - void destroyPreservedSurfaceLocked(SurfaceControl.Transaction t) { - if (!mDestroyPreservedSurfaceUponRedraw) { - return; - } - - // If we are preserving a surface but we aren't relaunching that means - // we are just doing an in-place switch. In that case any SurfaceFlinger side - // child layers need to be reparented to the new surface to make this - // transparent to the app. - // If the children are detached, we don't want to reparent them to the new surface. - // Instead let the children get removed when the old surface is deleted. - if (mSurfaceController != null && mPendingDestroySurface != null - && !mPendingDestroySurface.mChildrenDetached - && (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) { - mPostDrawTransaction.reparentChildren( - mPendingDestroySurface.mSurfaceControl, - mSurfaceController.mSurfaceControl).apply(); - } - - destroyDeferredSurfaceLocked(t); - mDestroyPreservedSurfaceUponRedraw = false; - } - - private void markPreservedSurfaceForDestroy() { - if (mDestroyPreservedSurfaceUponRedraw - && !mService.mDestroyPreservedSurface.contains(mWin)) { - mService.mDestroyPreservedSurface.add(mWin); - } - } - void resetDrawState() { mDrawState = DRAW_PENDING; @@ -508,39 +431,23 @@ class WindowStateAnimator { return; } - // When destroying a surface we want to make sure child windows are hidden. If we are - // preserving the surface until redraw though we intend to swap it out with another surface - // for resizing. In this case the window always remains visible to the user and the child - // windows should likewise remain visible. - if (!mDestroyPreservedSurfaceUponRedraw) { - mWin.mHidden = true; - } + mWin.mHidden = true; try { - if (DEBUG_VISIBILITY) logWithStack(TAG, "Window " + this + " destroying surface " - + mSurfaceController + ", session " + mSession); - if (mSurfaceDestroyDeferred) { - if (mSurfaceController != null && mPendingDestroySurface != mSurfaceController) { - if (mPendingDestroySurface != null) { - ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s", - mWin, new RuntimeException().fillInStackTrace()); - mPendingDestroySurface.destroy(t); - } - mPendingDestroySurface = mSurfaceController; - } - } else { - ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s", - mWin, new RuntimeException().fillInStackTrace()); - destroySurface(t); + if (DEBUG_VISIBILITY) { + logWithStack(TAG, "Window " + this + " destroying surface " + + mSurfaceController + ", session " + mSession); } + ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s", + mWin, new RuntimeException().fillInStackTrace()); + destroySurface(t); // Don't hide wallpaper if we're deferring the surface destroy // because of a surface change. - if (!mDestroyPreservedSurfaceUponRedraw) { - mWallpaperControllerLocked.hideWallpapers(mWin); - } + mWallpaperControllerLocked.hideWallpapers(mWin); } catch (RuntimeException e) { Slog.w(TAG, "Exception thrown when destroying Window " + this - + " surface " + mSurfaceController + " session " + mSession + ": " + e.toString()); + + " surface " + mSurfaceController + " session " + mSession + ": " + + e.toString()); } // Whether the surface was preserved (and copied to mPendingDestroySurface) or not, it @@ -554,27 +461,6 @@ class WindowStateAnimator { mDrawState = NO_SURFACE; } - void destroyDeferredSurfaceLocked(SurfaceControl.Transaction t) { - try { - if (mPendingDestroySurface != null) { - ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s", - mWin, new RuntimeException().fillInStackTrace()); - mPendingDestroySurface.destroy(t); - // Don't hide wallpaper if we're destroying a deferred surface - // after a surface mode change. - if (!mDestroyPreservedSurfaceUponRedraw) { - mWallpaperControllerLocked.hideWallpapers(mWin); - } - } - } catch (RuntimeException e) { - Slog.w(TAG, "Exception thrown when destroying Window " - + this + " surface " + mPendingDestroySurface - + " session " + mSession + ": " + e.toString()); - } - mSurfaceDestroyDeferred = false; - mPendingDestroySurface = null; - } - void computeShownFrameLocked() { if (mIsWallpaper && mService.mRoot.mWallpaperActionPending) { return; @@ -744,7 +630,6 @@ class WindowStateAnimator { if (prepared && mDrawState == HAS_DRAWN) { if (mLastHidden) { if (showSurfaceRobustlyLocked(t)) { - markPreservedSurfaceForDestroy(); mAnimator.requestRemovalOfReplacedWindows(w); mLastHidden = false; if (mIsWallpaper) { @@ -905,20 +790,6 @@ class WindowStateAnimator { if (!shown) return false; - // If we had a preserved surface it's no longer needed, and it may be harmful - // if we are transparent. - if (mPendingDestroySurface != null && mDestroyPreservedSurfaceUponRedraw) { - final SurfaceControl pendingSurfaceControl = mPendingDestroySurface.mSurfaceControl; - mPostDrawTransaction.reparent(pendingSurfaceControl, null); - // If the children are detached, we don't want to reparent them to the new surface. - // Instead let the children get removed when the old surface is deleted. - if (!mPendingDestroySurface.mChildrenDetached) { - mPostDrawTransaction.reparentChildren( - mPendingDestroySurface.mSurfaceControl, - mSurfaceController.mSurfaceControl); - } - } - t.merge(mPostDrawTransaction); return true; } @@ -946,7 +817,7 @@ class WindowStateAnimator { } if (mService.mAccessibilityController != null) { - mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit); + mService.mAccessibilityController.onWindowTransition(mWin, transit); } } @@ -1058,13 +929,6 @@ class WindowStateAnimator { pw.println(); } - if (mPendingDestroySurface != null) { - pw.print(prefix); pw.print("mPendingDestroySurface="); - pw.println(mPendingDestroySurface); - } - if (mSurfaceDestroyDeferred) { - pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred); - } if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) { pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha); pw.print(" mAlpha="); pw.print(mAlpha); diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 5f4477df021c..82ba3c188b76 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -55,8 +55,6 @@ class WindowSurfaceController { private boolean mSurfaceShown = false; private float mSurfaceX = 0; private float mSurfaceY = 0; - private int mSurfaceW = 0; - private int mSurfaceH = 0; // Initialize to the identity matrix. private float mLastDsdx = 1; @@ -82,9 +80,6 @@ class WindowSurfaceController { int flags, WindowStateAnimator animator, int windowType) { mAnimator = animator; - mSurfaceW = w; - mSurfaceH = h; - title = name; mService = animator.mService; @@ -104,8 +99,8 @@ class WindowSurfaceController { .setMetadata(METADATA_OWNER_PID, mWindowSession.mPid) .setCallsite("WindowSurfaceController"); - final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags & - WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0); + final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags + & WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0); if (useBLAST) { b.setBLASTLayer(); @@ -119,7 +114,6 @@ class WindowSurfaceController { void hide(SurfaceControl.Transaction transaction, String reason) { ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, title); - mAnimator.destroyPreservedSurfaceLocked(transaction); if (mSurfaceShown) { hideSurface(transaction); } @@ -335,9 +329,7 @@ class WindowSurfaceController { pw.print(" layer="); pw.print(mSurfaceLayer); pw.print(" alpha="); pw.print(mSurfaceAlpha); pw.print(" rect=("); pw.print(mSurfaceX); - pw.print(","); pw.print(mSurfaceY); - pw.print(") "); pw.print(mSurfaceW); - pw.print(" x "); pw.print(mSurfaceH); + pw.print(","); pw.print(mSurfaceY); pw.print(") "); pw.print(" transform=("); pw.print(mLastDsdx); pw.print(", "); pw.print(mLastDtdx); pw.print(", "); pw.print(mLastDsdy); pw.print(", "); pw.print(mLastDtdy); pw.println(")"); diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index cd18311d7d54..c3a4609c02a1 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; @@ -113,8 +112,6 @@ class WindowToken extends WindowContainer<WindowState> { */ private final boolean mFromClientToken; - private final int mOwnerUid; - /** * Used to fix the transform of the token to be rotated to a rotation different than it's * display. The window frames and surfaces corresponding to this token will be layouted and @@ -205,27 +202,19 @@ class WindowToken extends WindowContainer<WindowState> { WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) { - this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, INVALID_UID, - roundedCornerOverlay, false /* fromClientToken */); - } - - WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, - DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, - boolean roundedCornerOverlay, boolean fromClientToken) { - this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, ownerUid, - roundedCornerOverlay, fromClientToken, null /* options */); + this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, + roundedCornerOverlay, false /* fromClientToken */, null /* options */); } WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, - DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, - boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) { + DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay, + boolean fromClientToken, @Nullable Bundle options) { super(service); token = _token; windowType = type; mOptions = options; mPersistOnEmpty = persistOnEmpty; mOwnerCanManageAppTokens = ownerCanManageAppTokens; - mOwnerUid = ownerUid; mRoundedCornerOverlay = roundedCornerOverlay; mFromClientToken = fromClientToken; if (dc != null) { @@ -739,10 +728,6 @@ class WindowToken extends WindowContainer<WindowState> { mRoundedCornerOverlay); } - int getOwnerUid() { - return mOwnerUid; - } - boolean isFromClient() { return mFromClientToken; } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index cc7e00a43a6e..91be0564a26f 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -134,7 +134,7 @@ cc_defaults { "android.hardware.broadcastradio@1.0", "android.hardware.broadcastradio@1.1", "android.hardware.contexthub@1.0", - "android.hardware.gnss-cpp", + "android.hardware.gnss-V1-cpp", "android.hardware.gnss@1.0", "android.hardware.gnss@1.1", "android.hardware.gnss@2.0", @@ -147,12 +147,12 @@ cc_defaults { "android.hardware.light@2.0", "android.hardware.power@1.0", "android.hardware.power@1.1", - "android.hardware.power-cpp", + "android.hardware.power-V1-cpp", "android.hardware.power.stats@1.0", "android.hardware.power.stats-ndk_platform", "android.hardware.thermal@1.0", "android.hardware.tv.input@1.0", - "android.hardware.vibrator-unstable-cpp", + "android.hardware.vibrator-V2-cpp", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", @@ -162,7 +162,7 @@ cc_defaults { "android.frameworks.schedulerservice@1.0", "android.frameworks.sensorservice@1.0", "android.frameworks.stats@1.0", - "android.system.suspend.control-cpp", + "android.system.suspend.control-V1-cpp", "android.system.suspend.control.internal-cpp", "android.system.suspend@1.0", "service.incremental", diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 5b587e9859d8..643503d18bed 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -106,6 +106,7 @@ static struct { jmethodID notifyFocusChanged; jmethodID notifySensorEvent; jmethodID notifySensorAccuracy; + jmethodID notifyVibratorState; jmethodID notifyUntrustedTouch; jmethodID filterInputEvent; jmethodID interceptKeyBeforeQueueing; @@ -305,6 +306,7 @@ public: const std::vector<float>& values) override; void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy) override; + void notifyVibratorState(int32_t deviceId, bool isOn) override; void notifyUntrustedTouch(const std::string& obscuringPackage) override; bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override; void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override; @@ -918,6 +920,18 @@ void NativeInputManager::notifySensorAccuracy(int32_t deviceId, InputDeviceSenso checkAndClearExceptionFromCallback(env, "notifySensorAccuracy"); } +void NativeInputManager::notifyVibratorState(int32_t deviceId, bool isOn) { +#if DEBUG_INPUT_DISPATCHER_POLICY + ALOGD("notifyVibratorState isOn:%d", isOn); +#endif + ATRACE_CALL(); + JNIEnv* env = jniEnv(); + ScopedLocalFrame localFrame(env); + env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyVibratorState, + static_cast<jint>(deviceId), static_cast<jboolean>(isOn)); + checkAndClearExceptionFromCallback(env, "notifyVibratorState"); +} + void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) { ATRACE_CALL(); JNIEnv* env = jniEnv(); @@ -2248,6 +2262,8 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gServiceClassInfo.notifySensorAccuracy, clazz, "notifySensorAccuracy", "(III)V"); + GET_METHOD_ID(gServiceClassInfo.notifyVibratorState, clazz, "notifyVibratorState", "(IZ)V"); + GET_METHOD_ID(gServiceClassInfo.notifyUntrustedTouch, clazz, "notifyUntrustedTouch", "(Ljava/lang/String;)V"); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index 48f8b1505d3a..59b7367102c8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -136,6 +136,8 @@ class ActiveAdmin { private static final String TAG_PASSWORD_COMPLEXITY = "password-complexity"; private static final String TAG_ORGANIZATION_ID = "organization-id"; private static final String TAG_ENROLLMENT_SPECIFIC_ID = "enrollment-specific-id"; + private static final String TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS = + "admin-can-grant-sensors-permissions"; private static final String ATTR_VALUE = "value"; private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification"; private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications"; @@ -277,6 +279,7 @@ class ActiveAdmin { boolean mCommonCriteriaMode; public String mOrganizationId; public String mEnrollmentSpecificId; + public boolean mAdminCanGrantSensorsPermissions; ActiveAdmin(DeviceAdminInfo info, boolean isParent) { this.info = info; @@ -543,6 +546,8 @@ class ActiveAdmin { if (!TextUtils.isEmpty(mEnrollmentSpecificId)) { writeTextToXml(out, TAG_ENROLLMENT_SPECIFIC_ID, mEnrollmentSpecificId); } + writeAttributeValueToXml(out, TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS, + mAdminCanGrantSensorsPermissions); } void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException { @@ -792,6 +797,9 @@ class ActiveAdmin { Log.w(DevicePolicyManagerService.LOG_TAG, "Missing Enrollment-specific ID."); } + } else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) { + mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE, + false); } else { Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -1143,5 +1151,8 @@ class ActiveAdmin { pw.print("mEnrollmentSpecificId="); pw.println(mEnrollmentSpecificId); } + + pw.print("mAdminCanGrantSensorsPermissions"); + pw.println(mAdminCanGrantSensorsPermissions); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 11e4db503ec5..b52347f509f8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -16,6 +16,7 @@ package com.android.server.devicepolicy; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.admin.DevicePolicySafetyChecker; import android.app.admin.FullyManagedDeviceProvisioningParams; import android.app.admin.IDevicePolicyManager; @@ -127,4 +128,10 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public void provisionFullyManagedDevice( FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) { } + + public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {} + + public boolean canAdminGrantSensorsPermissionsForUser(int userId) { + return false; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java index 8b2beb22bead..8ea21ec74ad6 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java @@ -46,11 +46,15 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache { @GuardedBy("mLock") private final SparseIntArray mPermissionPolicy = new SparseIntArray(); + @GuardedBy("mLock") + private final SparseBooleanArray mCanGrantSensorsPermissions = new SparseBooleanArray(); + public void onUserRemoved(int userHandle) { synchronized (mLock) { mScreenCaptureDisabled.delete(userHandle); mPasswordQuality.delete(userHandle); mPermissionPolicy.delete(userHandle); + mCanGrantSensorsPermissions.delete(userHandle); } } @@ -97,6 +101,21 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache { } } + @Override + public boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle) { + synchronized (mLock) { + return mCanGrantSensorsPermissions.get(userHandle, false); + } + } + + /** Sets ahmin control over permission grants for user. */ + public void setAdminCanGrantSensorsPermissions(@UserIdInt int userHandle, + boolean canGrant) { + synchronized (mLock) { + mCanGrantSensorsPermissions.put(userHandle, canGrant); + } + } + /** Dump content */ public void dump(IndentingPrintWriter pw) { pw.println("Device policy cache:"); @@ -104,6 +123,8 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache { pw.println("Screen capture disabled: " + mScreenCaptureDisabled.toString()); pw.println("Password quality: " + mPasswordQuality.toString()); pw.println("Permission policy: " + mPermissionPolicy.toString()); + pw.println("Admin can grant sensors permission: " + + mCanGrantSensorsPermissions.toString()); pw.decreaseIndent(); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 3d2e5de0c19b..404b0cf8c7b8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -313,6 +313,7 @@ import com.android.server.PersistentDataBlockManagerInternal; import com.android.server.SystemServerInitThreadPool; import com.android.server.SystemService; import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo; +import com.android.server.devicepolicy.Owners.OwnerDto; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.pm.RestrictionsSet; @@ -910,12 +911,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { }; protected static class RestrictionsListener implements UserRestrictionsListener { - private Context mContext; - - public RestrictionsListener(Context context) { + private final Context mContext; + private final UserManagerInternal mUserManagerInternal; + private final DevicePolicyManagerService mDpms; + + public RestrictionsListener( + Context context, + UserManagerInternal userManagerInternal, + DevicePolicyManagerService dpms) { mContext = context; + mUserManagerInternal = userManagerInternal; + mDpms = dpms; } + @Override public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions) { final boolean newlyDisallowed = @@ -925,13 +934,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final boolean restrictionChanged = (newlyDisallowed != previouslyDisallowed); if (restrictionChanged) { - // Notify ManagedProvisioning to update the built-in cross profile intent filters. - Intent intent = new Intent( - DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED); - intent.setPackage(getManagedProvisioningPackage(mContext)); - intent.putExtra(Intent.EXTRA_USER_ID, userId); - intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM); + final int parentId = mUserManagerInternal.getProfileParentId(userId); + if (parentId == userId) { + return; + } + + // Always reset filters on the parent user, which handles cross profile intent + // filters between the parent and its profiles. + Slog.i(LOG_TAG, "Resetting cross-profile intent filters on restriction " + + "change"); + mDpms.resetDefaultCrossProfileIntentFilters(parentId); + mContext.sendBroadcastAsUser(new Intent( + DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED), + UserHandle.of(userId)); } } } @@ -1116,6 +1131,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason); } + // Used by DevicePolicyManagerServiceShellCommand + List<OwnerDto> listAllOwners() { + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); + + List<OwnerDto> owners = mOwners.listAllOwners(); + synchronized (getLockObject()) { + for (int i = 0; i < owners.size(); i++) { + OwnerDto owner = owners.get(i); + owner.isAffiliated = isUserAffiliatedWithDeviceLocked(owner.userId); + } + } + + return owners; + } + /** * Unit test will subclass it to inject mocks. */ @@ -1621,7 +1652,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mSetupContentObserver = new SetupContentObserver(mHandler); - mUserManagerInternal.addUserRestrictionsListener(new RestrictionsListener(mContext)); + mUserManagerInternal.addUserRestrictionsListener( + new RestrictionsListener(mContext, mUserManagerInternal, this)); mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener()); loadOwners(); @@ -2959,6 +2991,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { updatePasswordQualityCacheForUserGroup( userId == UserHandle.USER_SYSTEM ? UserHandle.USER_ALL : userId); updatePermissionPolicyCache(userId); + updateAdminCanGrantSensorsPermissionCache(userId); startOwnerService(userId, "start-user"); } @@ -13451,15 +13484,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mOwners.hasDeviceOwner()) { return false; } - if (userId == mOwners.getDeviceOwnerUserId()) { - // The user that the DO is installed on is always affiliated with the device. - return true; - } if (userId == UserHandle.USER_SYSTEM) { // The system user is always affiliated in a DO device, // even if in headless system user mode. return true; } + if (userId == mOwners.getDeviceOwnerUserId()) { + // The user that the DO is installed on is always affiliated with the device. + return true; + } final ComponentName profileOwner = getProfileOwnerAsUser(userId); if (profileOwner == null) { @@ -16008,19 +16041,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { provisioningParams.isKeepAccountMigrated(), callerPackage); if (provisioningParams.isOrganizationOwnedProvisioning()) { - markIsProfileOwnerOnOrganizationOwnedDevice(admin, userInfo.id); - restrictRemovalOfManagedProfile(admin, userInfo.id); + setProfileOwnerOnOrgOwnedDeviceState(admin, userInfo.id, caller.getUserId()); } - final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_PROFILE_CREATED) - .putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id) - .putExtra( - DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED, - provisioningParams.isLeaveAllSystemAppsEnabled()) - .setPackage(getManagedProvisioningPackage(mContext)) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM); - return userInfo.getUserHandle(); } catch (Exception e) { DevicePolicyEventLogger @@ -16250,21 +16273,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void markIsProfileOwnerOnOrganizationOwnedDevice( - ComponentName admin, @UserIdInt int profileId) { - getDpmForProfile(profileId).markProfileOwnerOnOrganizationOwnedDevice(admin); - } - - private void restrictRemovalOfManagedProfile( - ComponentName admin, @UserIdInt int profileId) { - getDpmForProfile(profileId).addUserRestriction( - admin, UserManager.DISALLOW_REMOVE_MANAGED_PROFILE); + private void setProfileOwnerOnOrgOwnedDeviceState( + ComponentName admin, @UserIdInt int profileId, @UserIdInt int parentUserId) { + synchronized (getLockObject()) { + markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, profileId); + } + restrictRemovalOfManagedProfile(parentUserId); } - private DevicePolicyManager getDpmForProfile(@UserIdInt int profileId) { - final Context profileContext = mContext.createContextAsUser( - UserHandle.of(profileId), /* flags= */ 0); - return profileContext.getSystemService(DevicePolicyManager.class); + private void restrictRemovalOfManagedProfile(@UserIdInt int parentUserId) { + final UserHandle parentUserHandle = UserHandle.of(parentUserId); + mUserManager.setUserRestriction( + UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, + /* value= */ true, + parentUserHandle); } @Override @@ -16313,15 +16335,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } disallowAddUser(); - - final Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISIONED_MANAGED_DEVICE) - .putExtra(Intent.EXTRA_USER_HANDLE, caller.getUserId()) - .putExtra( - DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED, - provisioningParams.isLeaveAllSystemAppsEnabled()) - .setPackage(getManagedProvisioningPackage(mContext)) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM); + setAdminCanGrantSensorsPermissionForUserUnchecked(caller.getUserId(), + provisioningParams.canDeviceOwnerGrantSensorsPermissions()); } catch (Exception e) { DevicePolicyEventLogger .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR) @@ -16420,4 +16435,76 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .setStrings(callerPackage) .write(); } + + @Override + public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) { + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); + + mInjector.binderWithCleanCallingIdentity(() -> { + try { + final List<UserInfo> profiles = mUserManager.getProfiles(userId); + final int numOfProfiles = profiles.size(); + if (numOfProfiles <= 1) { + return; + } + + final String managedProvisioningPackageName = getManagedProvisioningPackage( + mContext); + // Removes cross profile intent filters from the parent to all the profiles. + mIPackageManager.clearCrossProfileIntentFilters( + userId, mContext.getOpPackageName()); + // Setting and resetting default cross profile intent filters was previously handled + // by Managed Provisioning. For backwards compatibility, clear any intent filters + // that were set by ManagedProvisioning. + mIPackageManager.clearCrossProfileIntentFilters( + userId, managedProvisioningPackageName); + + // For each profile reset cross profile intent filters + for (int i = 0; i < numOfProfiles; i++) { + UserInfo profile = profiles.get(i); + mIPackageManager.clearCrossProfileIntentFilters( + profile.id, mContext.getOpPackageName()); + // Clear any intent filters that were set by ManagedProvisioning. + mIPackageManager.clearCrossProfileIntentFilters( + profile.id, managedProvisioningPackageName); + + mUserManagerInternal.setDefaultCrossProfileIntentFilters(userId, profile.id); + } + } catch (RemoteException e) { + // Shouldn't happen. + } + }); + } + + private void setAdminCanGrantSensorsPermissionForUserUnchecked(int userId, boolean canGrant) { + synchronized (getLockObject()) { + ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId); + + Preconditions.checkState( + isDeviceOwner(owner) && owner.getUserHandle().getIdentifier() == userId, + "May only be set on a the user of a device owner."); + + owner.mAdminCanGrantSensorsPermissions = canGrant; + mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant); + saveSettingsLocked(userId); + } + } + + private void updateAdminCanGrantSensorsPermissionCache(int userId) { + synchronized (getLockObject()) { + ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId); + final boolean canGrant = owner != null ? owner.mAdminCanGrantSensorsPermissions : false; + mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant); + } + } + + @Override + public boolean canAdminGrantSensorsPermissionsForUser(int userId) { + if (!mHasFeature) { + return false; + } + + return mPolicyCache.canAdminGrantSensorsPermissionsForUser(userId); + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java index fc1d83158801..222c987d906f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java @@ -18,13 +18,17 @@ package com.android.server.devicepolicy; import android.app.admin.DevicePolicyManager; import android.os.ShellCommand; +import com.android.server.devicepolicy.Owners.OwnerDto; + import java.io.PrintWriter; +import java.util.List; import java.util.Objects; final class DevicePolicyManagerServiceShellCommand extends ShellCommand { private static final String CMD_IS_SAFE_OPERATION = "is-operation-safe"; private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe"; + private static final String CMD_LIST_OWNERS = "list-owners"; private final DevicePolicyManagerService mService; @@ -51,6 +55,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { return runIsSafeOperation(pw); case CMD_SET_SAFE_OPERATION: return runSetSafeOperation(pw); + case CMD_LIST_OWNERS: + return runListOwners(pw); default: return onInvalidCommand(pw, cmd); } @@ -76,6 +82,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { pw.printf(" %s <OPERATION_ID> <REASON_ID>\n", CMD_SET_SAFE_OPERATION); pw.printf(" Emulates the result of the next call to check if the given operation is safe" + " \n\n"); + pw.printf(" %s\n", CMD_LIST_OWNERS); + pw.printf(" Lists the device / profile owners per user \n\n"); } private int runIsSafeOperation(PrintWriter pw) { @@ -97,4 +105,36 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { DevicePolicyManager.unsafeOperationReasonToString(reason)); return 0; } + + private int runListOwners(PrintWriter pw) { + List<OwnerDto> owners = mService.listAllOwners(); + if (owners.isEmpty()) { + pw.println("none"); + return 0; + } + int size = owners.size(); + if (size == 1) { + pw.println("1 owner:"); + } else { + pw.printf("%d owners:\n", size); + } + + for (int i = 0; i < size; i++) { + OwnerDto owner = owners.get(i); + pw.printf("User %2d: admin=%s", owner.userId, owner.admin.flattenToShortString()); + if (owner.isDeviceOwner) { + pw.print(",DeviceOwner"); + } + if (owner.isProfileOwner) { + pw.print(",ProfileOwner"); + } + if (owner.isAffiliated) { + pw.print(",Affiliated"); + } + pw.println(); + } + + return 0; + } + } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 809afe01da2d..1e70d59a5fd5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -17,6 +17,7 @@ package com.android.server.devicepolicy; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.app.AppOpsManagerInternal; import android.app.admin.SystemUpdateInfo; @@ -57,6 +58,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.time.LocalDate; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -433,6 +435,23 @@ class Owners { } } + List<OwnerDto> listAllOwners() { + List<OwnerDto> owners = new ArrayList<>(); + synchronized (mLock) { + if (mDeviceOwner != null) { + owners.add(new OwnerDto(mDeviceOwnerUserId, mDeviceOwner.admin, + /* isDeviceOwner= */ true)); + } + for (int i = 0; i < mProfileOwners.size(); i++) { + int userId = mProfileOwners.keyAt(i); + OwnerInfo info = mProfileOwners.valueAt(i); + owners.add(new OwnerDto(userId, info.admin, /* isDeviceOwner= */ false)); + } + } + return owners; + } + + SystemUpdatePolicy getSystemUpdatePolicy() { synchronized (mLock) { return mSystemUpdatePolicy; @@ -1076,6 +1095,24 @@ class Owners { } } + /** + * Data-transfer object used by {@link DevicePolicyManagerServiceShellCommand}. + */ + static final class OwnerDto { + public final @UserIdInt int userId; + public final ComponentName admin; + public final boolean isDeviceOwner; + public final boolean isProfileOwner; + public boolean isAffiliated; + + private OwnerDto(@UserIdInt int userId, ComponentName admin, boolean isDeviceOwner) { + this.userId = userId; + this.admin = Objects.requireNonNull(admin, "admin must not be null"); + this.isDeviceOwner = isDeviceOwner; + this.isProfileOwner = !isDeviceOwner; + } + } + public void dump(IndentingPrintWriter pw) { boolean needBlank = false; if (mDeviceOwner != null) { diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp index e978ed4000e0..7534c7c40a3d 100644 --- a/services/incremental/Android.bp +++ b/services/incremental/Android.bp @@ -51,9 +51,9 @@ cc_defaults { static_libs: [ "libbase", "libext2_uuid", - "libdataloader_aidl-unstable-cpp", - "libincremental_aidl-unstable-cpp", - "libincremental_manager_aidl-unstable-cpp", + "libdataloader_aidl-cpp", + "libincremental_aidl-cpp", + "libincremental_manager_aidl-cpp", "libprotobuf-cpp-lite", "service.incremental.proto", "libutils", diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 50cb00f1887f..98c3b99124ee 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -147,6 +147,7 @@ import com.android.server.oemlock.OemLockService; import com.android.server.om.OverlayManagerService; import com.android.server.os.BugreportManagerService; import com.android.server.os.DeviceIdentifiersPolicyService; +import com.android.server.os.NativeTombstoneManagerService; import com.android.server.os.SchedulingPolicyService; import com.android.server.people.PeopleService; import com.android.server.pm.BackgroundDexOptService; @@ -185,6 +186,7 @@ import com.android.server.telecom.TelecomLoaderService; import com.android.server.testharness.TestHarnessModeService; import com.android.server.textclassifier.TextClassificationManagerService; import com.android.server.textservices.TextServicesManagerService; +import com.android.server.tracing.TracingServiceProxy; import com.android.server.trust.TrustManagerService; import com.android.server.tv.TvInputManagerService; import com.android.server.tv.TvRemoteService; @@ -207,12 +209,15 @@ import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Timer; +import java.util.TreeSet; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; @@ -481,6 +486,50 @@ public final class SystemServer implements Dumpable { private static native void fdtrackAbort(); + private static final File HEAP_DUMP_PATH = new File("/data/system/heapdump/"); + private static final int MAX_HEAP_DUMPS = 2; + + /** + * Dump system_server's heap. + * + * For privacy reasons, these aren't automatically pulled into bugreports: + * they must be manually pulled by the user. + */ + private static void dumpHprof() { + // hprof dumps are rather large, so ensure we don't fill the disk by generating + // hundreds of these that will live forever. + TreeSet<File> existingTombstones = new TreeSet<>(); + for (File file : HEAP_DUMP_PATH.listFiles()) { + if (!file.isFile()) { + continue; + } + if (!file.getName().startsWith("fdtrack-")) { + continue; + } + existingTombstones.add(file); + } + if (existingTombstones.size() >= MAX_HEAP_DUMPS) { + for (int i = 0; i < MAX_HEAP_DUMPS - 1; ++i) { + // Leave the newest `MAX_HEAP_DUMPS - 1` tombstones in place. + existingTombstones.pollLast(); + } + for (File file : existingTombstones) { + if (!file.delete()) { + Slog.w("System", "Failed to clean up hprof " + file); + } + } + } + + try { + String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); + String filename = "/data/system/heapdump/fdtrack-" + date + ".hprof"; + Debug.dumpHprofData(filename); + } catch (IOException ex) { + Slog.e("System", "Failed to dump fdtrack hprof"); + ex.printStackTrace(); + } + } + /** * Spawn a thread that monitors for fd leaks. */ @@ -505,6 +554,7 @@ public final class SystemServer implements Dumpable { enabled = true; } else if (maxFd > abortThreshold) { Slog.i("System", "fdtrack abort threshold reached, dumping and aborting"); + dumpHprof(); fdtrackAbort(); } @@ -1217,6 +1267,11 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS); t.traceEnd(); + // Tracks native tombstones. + t.traceBegin("StartNativeTombstoneManagerService"); + mSystemServiceManager.startService(NativeTombstoneManagerService.class); + t.traceEnd(); + // Service to capture bugreports. t.traceBegin("StartBugreportManagerService"); mSystemServiceManager.startService(BugreportManagerService.class); @@ -2429,6 +2484,11 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(AppBindingService.Lifecycle.class); t.traceEnd(); + // Perfetto TracingServiceProxy + t.traceBegin("startTracingServiceProxy"); + mSystemServiceManager.startService(TracingServiceProxy.class); + t.traceEnd(); + // It is now time to start up the app processes... t.traceBegin("MakeVibratorServiceReady"); diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt new file mode 100644 index 000000000000..5792e02e37a2 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.test.verify.domain + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.content.pm.PackageUserState +import android.content.pm.verify.domain.DomainVerificationManager +import android.content.pm.parsing.component.ParsedActivity +import android.content.pm.parsing.component.ParsedIntentInfo +import android.os.Build +import android.os.Process +import android.util.ArraySet +import android.util.SparseArray +import com.android.server.pm.PackageSetting +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal +import com.android.server.pm.verify.domain.DomainVerificationService +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy +import com.android.server.pm.parsing.pkg.AndroidPackage +import com.android.server.testutils.mockThrowOnUnmocked +import com.android.server.testutils.spyThrowOnUnmocked +import com.android.server.testutils.whenever +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.mockito.Mockito +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.anyLong +import org.mockito.Mockito.any +import org.mockito.Mockito.anyString +import org.mockito.Mockito.eq +import org.mockito.Mockito.verify +import java.io.File +import java.util.UUID + +@RunWith(Parameterized::class) +class DomainVerificationSettingsMutationTest { + + companion object { + private const val TEST_PKG = "com.test" + + // Pretend to be the system. This class doesn't verify any enforcement behavior. + private const val TEST_UID = Process.SYSTEM_UID + private const val TEST_USER_ID = 10 + private val TEST_UUID = UUID.fromString("5168e42e-327e-432b-b562-cfb553518a70") + + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun parameters(): Array<Any> { + val context: Context = mockThrowOnUnmocked { + whenever( + enforcePermission( + eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT), + anyInt(), anyInt(), anyString() + ) + ) + whenever( + enforcePermission( + eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION), + anyInt(), anyInt(), anyString() + ) + ) + whenever( + enforcePermission( + eq(android.Manifest.permission.INTERACT_ACROSS_USERS), + anyInt(), anyInt(), anyString() + ) + ) + whenever( + enforcePermission( + eq(android.Manifest.permission.SET_PREFERRED_APPLICATIONS), + anyInt(), anyInt(), anyString() + ) + ) + } + val proxy: DomainVerificationProxy = mockThrowOnUnmocked { + whenever(isCallerVerifier(anyInt())) { true } + whenever(sendBroadcastForPackages(any())) + } + + val makeService: (DomainVerificationManagerInternal.Connection) -> DomainVerificationService = + { connection -> + DomainVerificationService( + context, + mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } }, + mockThrowOnUnmocked { + whenever(isChangeEnabled(anyLong(),any())) { true } + }).apply { + setConnection(connection) + } + } + + fun service(name: String, block: DomainVerificationService.() -> Unit) = + Params(makeService, name) { service -> + service.proxy = proxy + service.addPackage(mockPkgSetting()) + service.block() + } + + return arrayOf( + service("clearPackage") { + clearPackage(TEST_PKG) + }, + service("clearUser") { + clearUser(TEST_USER_ID) + }, + service("clearState") { + clearDomainVerificationState(listOf(TEST_PKG)) + }, + service("clearUserSelections") { + clearUserSelections(listOf(TEST_PKG), TEST_USER_ID) + }, + service("setStatus") { + setDomainVerificationStatus( + TEST_UUID, + setOf("example.com"), + DomainVerificationManager.STATE_SUCCESS + ) + }, + service("setStatusInternalPackageName") { + setDomainVerificationStatusInternal( + TEST_PKG, + DomainVerificationManager.STATE_SUCCESS, + ArraySet(setOf("example.com")) + ) + }, + service("setStatusInternalUid") { + setDomainVerificationStatusInternal( + TEST_UID, + TEST_UUID, + setOf("example.com"), + DomainVerificationManager.STATE_SUCCESS + ) + }, + service("setLinkHandlingAllowed") { + setDomainVerificationLinkHandlingAllowed(TEST_PKG, true) + }, + service("setLinkHandlingAllowedUserId") { + setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, TEST_USER_ID) + }, + service("setLinkHandlingAllowedInternal") { + setDomainVerificationLinkHandlingAllowedInternal(TEST_PKG, true, TEST_USER_ID) + }, + service("setUserSelection") { + setDomainVerificationUserSelection(TEST_UUID, setOf("example.com"), true) + }, + service("setUserSelectionUserId") { + setDomainVerificationUserSelection( + TEST_UUID, + setOf("example.com"), + true, + TEST_USER_ID + ) + }, + service("setUserSelectionInternal") { + setDomainVerificationUserSelectionInternal( + TEST_USER_ID, + TEST_PKG, + true, + ArraySet(setOf("example.com")), + ) + }, + service("setLegacyUserState") { + setLegacyUserState( + TEST_PKG, + TEST_USER_ID, + PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER + ) + }, + ) + } + + data class Params( + val construct: ( + DomainVerificationManagerInternal.Connection + ) -> DomainVerificationService, + val name: String, + val method: (DomainVerificationService) -> Unit + ) { + override fun toString() = name + } + + + fun mockPkg() = mockThrowOnUnmocked<AndroidPackage> { + whenever(packageName) { TEST_PKG } + whenever(targetSdkVersion) { Build.VERSION_CODES.S } + whenever(activities) { + listOf( + ParsedActivity().apply { + addIntent( + ParsedIntentInfo().apply { + autoVerify = true + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_BROWSABLE) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("https") + addDataAuthority("example.com", null) + } + ) + } + ) + } + } + + // TODO: PackageSetting field encapsulation to move to whenever(name) + fun mockPkgSetting() = spyThrowOnUnmocked( + PackageSetting( + TEST_PKG, + TEST_PKG, + File("/test"), + null, + null, + null, + null, + 1, + 0, + 0, + 0, + null, + null, + null, + TEST_UUID + ) + ) { + whenever(getPkg()) { mockPkg() } + whenever(domainSetId) { TEST_UUID } + whenever(userState) { + SparseArray<PackageUserState>().apply { + this[0] = PackageUserState() + } + } + } + } + + @Parameterized.Parameter(0) + lateinit var params: Params + + @Test + fun writeScheduled() { + val connection = mockConnection() + val service = params.construct(connection) + params.method(service) + + verify(connection).scheduleWriteSettings() + } + + private fun mockConnection(): DomainVerificationManagerInternal.Connection = + mockThrowOnUnmocked { + whenever(callingUid) { TEST_UID } + whenever(callingUserId) { TEST_USER_ID } + whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting() } + whenever(getPackageLocked(TEST_PKG)) { mockPkg() } + whenever(schedule(anyInt(), any())) + whenever(scheduleWriteSettings()) + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index af4130d92cfe..ca534927bd66 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -200,12 +200,12 @@ public class LocalDisplayAdapterTest { } private static class DisplayModeWrapper { - public SurfaceControl.DisplayConfig config; + public SurfaceControl.DisplayMode mode; public float[] expectedAlternativeRefreshRates; - DisplayModeWrapper(SurfaceControl.DisplayConfig config, + DisplayModeWrapper(SurfaceControl.DisplayMode mode, float[] expectedAlternativeRefreshRates) { - this.config = config; + this.mode = mode; this.expectedAlternativeRefreshRates = expectedAlternativeRefreshRates; } } @@ -215,14 +215,15 @@ public class LocalDisplayAdapterTest { * <code>expectedAlternativeRefreshRates</code> are present for each of the * <code>modes</code>. */ - private void testAlternativeRefreshRatesCommon(FakeDisplay display, DisplayModeWrapper[] modes) + private void testAlternativeRefreshRatesCommon(FakeDisplay display, + DisplayModeWrapper[] wrappedModes) throws InterruptedException { // Update the display. - SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[modes.length]; - for (int i = 0; i < modes.length; i++) { - configs[i] = modes[i].config; + SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[wrappedModes.length]; + for (int i = 0; i < wrappedModes.length; i++) { + modes[i] = wrappedModes[i].mode; } - display.configs = configs; + display.modes = modes; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); @@ -233,11 +234,11 @@ public class LocalDisplayAdapterTest { mListener.changedDisplays.get(mListener.changedDisplays.size() - 1); displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); Display.Mode[] supportedModes = displayDevice.getDisplayDeviceInfoLocked().supportedModes; - assertThat(supportedModes.length).isEqualTo(configs.length); + assertThat(supportedModes.length).isEqualTo(modes.length); - for (int i = 0; i < modes.length; i++) { - assertModeIsSupported(supportedModes, configs[i], - modes[i].expectedAlternativeRefreshRates); + for (int i = 0; i < wrappedModes.length; i++) { + assertModeIsSupported(supportedModes, modes[i], + wrappedModes[i].expectedAlternativeRefreshRates); } } @@ -251,56 +252,56 @@ public class LocalDisplayAdapterTest { testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { new DisplayModeWrapper( - createFakeDisplayConfig(1920, 1080, 60f, 0), new float[]{24f, 50f}), + createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{24f, 50f}), new DisplayModeWrapper( - createFakeDisplayConfig(1920, 1080, 50f, 0), new float[]{24f, 60f}), + createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{24f, 60f}), new DisplayModeWrapper( - createFakeDisplayConfig(1920, 1080, 24f, 0), new float[]{50f, 60f}), + createFakeDisplayMode(1920, 1080, 24f, 0), new float[]{50f, 60f}), new DisplayModeWrapper( - createFakeDisplayConfig(3840, 2160, 60f, 0), new float[]{24f, 50f}), + createFakeDisplayMode(3840, 2160, 60f, 0), new float[]{24f, 50f}), new DisplayModeWrapper( - createFakeDisplayConfig(3840, 2160, 50f, 0), new float[]{24f, 60f}), + createFakeDisplayMode(3840, 2160, 50f, 0), new float[]{24f, 60f}), new DisplayModeWrapper( - createFakeDisplayConfig(3840, 2160, 24f, 0), new float[]{50f, 60f}), + createFakeDisplayMode(3840, 2160, 24f, 0), new float[]{50f, 60f}), }); testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { new DisplayModeWrapper( - createFakeDisplayConfig(1920, 1080, 60f, 0), new float[]{50f}), + createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{50f}), new DisplayModeWrapper( - createFakeDisplayConfig(1920, 1080, 50f, 0), new float[]{60f}), + createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{60f}), new DisplayModeWrapper( - createFakeDisplayConfig(1920, 1080, 24f, 1), new float[0]), + createFakeDisplayMode(1920, 1080, 24f, 1), new float[0]), new DisplayModeWrapper( - createFakeDisplayConfig(3840, 2160, 60f, 2), new float[0]), + createFakeDisplayMode(3840, 2160, 60f, 2), new float[0]), new DisplayModeWrapper( - createFakeDisplayConfig(3840, 2160, 50f, 3), new float[]{24f}), + createFakeDisplayMode(3840, 2160, 50f, 3), new float[]{24f}), new DisplayModeWrapper( - createFakeDisplayConfig(3840, 2160, 24f, 3), new float[]{50f}), + createFakeDisplayMode(3840, 2160, 24f, 3), new float[]{50f}), }); testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { new DisplayModeWrapper( - createFakeDisplayConfig(1920, 1080, 60f, 0), new float[0]), + createFakeDisplayMode(1920, 1080, 60f, 0), new float[0]), new DisplayModeWrapper( - createFakeDisplayConfig(1920, 1080, 50f, 1), new float[0]), + createFakeDisplayMode(1920, 1080, 50f, 1), new float[0]), new DisplayModeWrapper( - createFakeDisplayConfig(1920, 1080, 24f, 2), new float[0]), + createFakeDisplayMode(1920, 1080, 24f, 2), new float[0]), new DisplayModeWrapper( - createFakeDisplayConfig(3840, 2160, 60f, 3), new float[0]), + createFakeDisplayMode(3840, 2160, 60f, 3), new float[0]), new DisplayModeWrapper( - createFakeDisplayConfig(3840, 2160, 50f, 4), new float[0]), + createFakeDisplayMode(3840, 2160, 50f, 4), new float[0]), new DisplayModeWrapper( - createFakeDisplayConfig(3840, 2160, 24f, 5), new float[0]), + createFakeDisplayMode(3840, 2160, 24f, 5), new float[0]), }); } @Test public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception { - SurfaceControl.DisplayConfig displayConfig = createFakeDisplayConfig(1920, 1080, 60f); - SurfaceControl.DisplayConfig[] configs = - new SurfaceControl.DisplayConfig[]{displayConfig}; - FakeDisplay display = new FakeDisplay(PORT_A, configs, 0); + SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(1920, 1080, 60f); + SurfaceControl.DisplayMode[] modes = + new SurfaceControl.DisplayMode[]{displayMode}; + FakeDisplay display = new FakeDisplay(PORT_A, modes, 0); setUpDisplay(display); updateAvailableDisplays(); mAdapter.registerLocked(); @@ -312,29 +313,29 @@ public class LocalDisplayAdapterTest { DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get( 0).getDisplayDeviceInfoLocked(); - assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length); - assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig); + assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length); + assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode); Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId); - assertThat(defaultMode.matches(displayConfig.width, displayConfig.height, - displayConfig.refreshRate)).isTrue(); + assertThat(defaultMode.matches(displayMode.width, displayMode.height, + displayMode.refreshRate)).isTrue(); Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId); - assertThat(activeMode.matches(displayConfig.width, displayConfig.height, - displayConfig.refreshRate)).isTrue(); + assertThat(activeMode.matches(displayMode.width, displayMode.height, + displayMode.refreshRate)).isTrue(); // Change the display - SurfaceControl.DisplayConfig addedDisplayInfo = createFakeDisplayConfig(3840, 2160, + SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(3840, 2160, 60f); - configs = new SurfaceControl.DisplayConfig[]{displayConfig, addedDisplayInfo}; - display.configs = configs; - display.activeConfig = 1; + modes = new SurfaceControl.DisplayMode[]{displayMode, addedDisplayInfo}; + display.modes = modes; + display.activeMode = 1; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); - assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1); - assertThat(SurfaceControl.getDisplayConfigs(display.token).length).isEqualTo(2); + assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1); + assertThat(SurfaceControl.getDisplayModes(display.token).length).isEqualTo(2); assertThat(mListener.addedDisplays.size()).isEqualTo(1); assertThat(mListener.changedDisplays.size()).isEqualTo(1); @@ -343,8 +344,8 @@ public class LocalDisplayAdapterTest { displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); - assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length); - assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig); + assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length); + assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode); assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo); activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId); @@ -358,11 +359,11 @@ public class LocalDisplayAdapterTest { @Test public void testAfterDisplayChange_ActiveModeIsUpdated() throws Exception { - SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{ - createFakeDisplayConfig(1920, 1080, 60f), - createFakeDisplayConfig(1920, 1080, 50f) + SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{ + createFakeDisplayMode(1920, 1080, 60f), + createFakeDisplayMode(1920, 1080, 50f) }; - FakeDisplay display = new FakeDisplay(PORT_A, configs, /* activeConfig */ 0); + FakeDisplay display = new FakeDisplay(PORT_A, modes, /* activeMode */ 0); setUpDisplay(display); updateAvailableDisplays(); mAdapter.registerLocked(); @@ -378,12 +379,12 @@ public class LocalDisplayAdapterTest { assertThat(activeMode.matches(1920, 1080, 60f)).isTrue(); // Change the display - display.activeConfig = 1; + display.activeMode = 1; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); - assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1); + assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1); assertThat(mListener.addedDisplays.size()).isEqualTo(1); assertThat(mListener.changedDisplays.size()).isEqualTo(1); @@ -471,14 +472,14 @@ public class LocalDisplayAdapterTest { } @Test - public void testDisplayChange_withStaleDesiredDisplayConfigSpecs() throws Exception { - SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{ - createFakeDisplayConfig(1920, 1080, 60f), - createFakeDisplayConfig(1920, 1080, 50f) + public void testDisplayChange_withStaleDesiredDisplayModeSpecs() throws Exception { + SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{ + createFakeDisplayMode(1920, 1080, 60f), + createFakeDisplayMode(1920, 1080, 50f) }; - final int activeConfig = 0; - FakeDisplay display = new FakeDisplay(PORT_A, configs, activeConfig); - display.desiredDisplayConfigSpecs.defaultConfig = 1; + final int activeMode = 0; + FakeDisplay display = new FakeDisplay(PORT_A, modes, activeMode); + display.desiredDisplayModeSpecs.defaultMode = 1; setUpDisplay(display); updateAvailableDisplays(); @@ -486,12 +487,12 @@ public class LocalDisplayAdapterTest { waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); // Change the display - display.configs = new SurfaceControl.DisplayConfig[]{ - createFakeDisplayConfig(1920, 1080, 60f) + display.modes = new SurfaceControl.DisplayMode[]{ + createFakeDisplayMode(1920, 1080, 60f) }; - // SurfaceFlinger can return a stale defaultConfig. Make sure this doesn't + // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't // trigger ArrayOutOfBoundsException. - display.desiredDisplayConfigSpecs.defaultConfig = 1; + display.desiredDisplayModeSpecs.defaultMode = 1; setUpDisplay(display); updateAvailableDisplays(); @@ -562,13 +563,13 @@ public class LocalDisplayAdapterTest { } private void assertModeIsSupported(Display.Mode[] supportedModes, - SurfaceControl.DisplayConfig mode) { + SurfaceControl.DisplayMode mode) { assertThat(Arrays.stream(supportedModes).anyMatch( x -> x.matches(mode.width, mode.height, mode.refreshRate))).isTrue(); } private void assertModeIsSupported(Display.Mode[] supportedModes, - SurfaceControl.DisplayConfig mode, float[] alternativeRefreshRates) { + SurfaceControl.DisplayMode mode, float[] alternativeRefreshRates) { float[] sortedAlternativeRates = Arrays.copyOf(alternativeRefreshRates, alternativeRefreshRates.length); Arrays.sort(sortedAlternativeRates); @@ -588,13 +589,13 @@ public class LocalDisplayAdapterTest { public final DisplayAddress.Physical address; public final IBinder token = new Binder(); public final SurfaceControl.DisplayInfo info; - public SurfaceControl.DisplayConfig[] configs; - public int activeConfig; + public SurfaceControl.DisplayMode[] modes; + public int activeMode; public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT }; public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0], 1000, 1000, 0); - public SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs = - new SurfaceControl.DesiredDisplayConfigSpecs(/* defaultConfig */ 0, + public SurfaceControl.DesiredDisplayModeSpecs desiredDisplayModeSpecs = + new SurfaceControl.DesiredDisplayModeSpecs(/* defaultMode */ 0, /* allowGroupSwitching */ false, /* primaryRefreshRateMin */ 60.f, /* primaryRefreshRateMax */ 60.f, @@ -604,17 +605,17 @@ public class LocalDisplayAdapterTest { private FakeDisplay(int port) { this.address = createDisplayAddress(port); this.info = createFakeDisplayInfo(); - this.configs = new SurfaceControl.DisplayConfig[]{ - createFakeDisplayConfig(800, 600, 60f) + this.modes = new SurfaceControl.DisplayMode[]{ + createFakeDisplayMode(800, 600, 60f) }; - this.activeConfig = 0; + this.activeMode = 0; } - private FakeDisplay(int port, SurfaceControl.DisplayConfig[] configs, int activeConfig) { + private FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode) { this.address = createDisplayAddress(port); this.info = createFakeDisplayInfo(); - this.configs = configs; - this.activeConfig = activeConfig; + this.modes = modes; + this.activeMode = activeMode; } } @@ -623,16 +624,16 @@ public class LocalDisplayAdapterTest { doReturn(display.token).when(() -> SurfaceControl.getPhysicalDisplayToken(display.address.getPhysicalDisplayId())); doReturn(display.info).when(() -> SurfaceControl.getDisplayInfo(display.token)); - doReturn(display.configs).when( - () -> SurfaceControl.getDisplayConfigs(display.token)); - doReturn(display.activeConfig).when(() -> SurfaceControl.getActiveConfig(display.token)); + doReturn(display.modes).when( + () -> SurfaceControl.getDisplayModes(display.token)); + doReturn(display.activeMode).when(() -> SurfaceControl.getActiveDisplayMode(display.token)); doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token)); doReturn(display.colorModes).when( () -> SurfaceControl.getDisplayColorModes(display.token)); doReturn(display.hdrCapabilities).when( () -> SurfaceControl.getHdrCapabilities(display.token)); - doReturn(display.desiredDisplayConfigSpecs) - .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(display.token)); + doReturn(display.desiredDisplayModeSpecs) + .when(() -> SurfaceControl.getDesiredDisplayModeSpecs(display.token)); } private void updateAvailableDisplays() { @@ -655,21 +656,21 @@ public class LocalDisplayAdapterTest { return info; } - private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height, + private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height, float refreshRate) { - return createFakeDisplayConfig(width, height, refreshRate, 0); - } - - private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height, - float refreshRate, int configGroup) { - final SurfaceControl.DisplayConfig config = new SurfaceControl.DisplayConfig(); - config.width = width; - config.height = height; - config.refreshRate = refreshRate; - config.xDpi = 100; - config.yDpi = 100; - config.configGroup = configGroup; - return config; + return createFakeDisplayMode(width, height, refreshRate, 0); + } + + private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height, + float refreshRate, int group) { + final SurfaceControl.DisplayMode mode = new SurfaceControl.DisplayMode(); + mode.width = width; + mode.height = height; + mode.refreshRate = refreshRate; + mode.xDpi = 100; + mode.yDpi = 100; + mode.group = group; + return mode; } private static void waitForHandlerToComplete(Handler handler, long waitTimeMs) diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java new file mode 100644 index 000000000000..35ac8979d46a --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.job; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.app.ActivityManagerInternal; +import android.app.job.JobInfo; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.os.UserHandle; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.R; +import com.android.server.LocalServices; +import com.android.server.job.JobConcurrencyManager.GracePeriodObserver; +import com.android.server.job.controllers.JobStatus; +import com.android.server.pm.UserManagerInternal; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class JobConcurrencyManagerTest { + private static final int UNAVAILABLE_USER = 0; + private JobConcurrencyManager mJobConcurrencyManager; + private UserManagerInternal mUserManagerInternal; + private ActivityManagerInternal mActivityManagerInternal; + private int mNextUserId; + private GracePeriodObserver mGracePeriodObserver; + private Context mContext; + private Resources mResources; + + @BeforeClass + public static void setUpOnce() { + LocalServices.addService(UserManagerInternal.class, mock(UserManagerInternal.class)); + LocalServices.addService( + ActivityManagerInternal.class, mock(ActivityManagerInternal.class)); + } + + @AfterClass + public static void tearDownOnce() { + LocalServices.removeServiceForTest(UserManagerInternal.class); + LocalServices.removeServiceForTest(ActivityManagerInternal.class); + } + + @Before + public void setUp() { + final JobSchedulerService jobSchedulerService = mock(JobSchedulerService.class); + mContext = mock(Context.class); + mResources = mock(Resources.class); + doReturn(true).when(mResources).getBoolean( + R.bool.config_jobSchedulerRestrictBackgroundUser); + when(mContext.getResources()).thenReturn(mResources); + doReturn(mContext).when(jobSchedulerService).getTestableContext(); + mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService); + mGracePeriodObserver = mock(GracePeriodObserver.class); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); + mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); + mNextUserId = 10; + mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver; + } + + @Test + public void testShouldRunAsFgUserJob_currentUser() { + assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob( + createJob(createCurrentUser(false)))); + } + + @Test + public void testShouldRunAsFgUserJob_currentProfile() { + assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob( + createJob(createCurrentUser(true)))); + } + + @Test + public void testShouldRunAsFgUserJob_primaryUser() { + assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob( + createJob(createPrimaryUser(false)))); + } + + @Test + public void testShouldRunAsFgUserJob_primaryProfile() { + assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob( + createJob(createPrimaryUser(true)))); + } + + @Test + public void testShouldRunAsFgUserJob_UnexpiredUser() { + assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob( + createJob(createUnexpiredUser(false)))); + } + + @Test + public void testShouldRunAsFgUserJob_UnexpiredProfile() { + assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob( + createJob(createUnexpiredUser(true)))); + } + + @Test + public void testShouldRunAsFgUserJob_restrictedUser() { + assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob( + createJob(createRestrictedUser(false)))); + } + + @Test + public void testShouldRunAsFgUserJob_restrictedProfile() { + assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob( + createJob(createRestrictedUser(true)))); + } + + private UserInfo createCurrentUser(boolean isProfile) { + final UserInfo ui = createNewUser(); + doReturn(ui.id).when(mActivityManagerInternal).getCurrentUserId(); + return isProfile ? createNewProfile(ui) : ui; + } + + private UserInfo createPrimaryUser(boolean isProfile) { + final UserInfo ui = createNewUser(); + doReturn(true).when(ui).isPrimary(); + return isProfile ? createNewProfile(ui) : ui; + } + + private UserInfo createUnexpiredUser(boolean isProfile) { + final UserInfo ui = createNewUser(); + doReturn(true).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id); + return isProfile ? createNewProfile(ui) : ui; + } + + private UserInfo createRestrictedUser(boolean isProfile) { + final UserInfo ui = createNewUser(); + doReturn(UNAVAILABLE_USER).when(mActivityManagerInternal).getCurrentUserId(); + doReturn(false).when(ui).isPrimary(); + doReturn(false).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id); + return isProfile ? createNewProfile(ui) : ui; + } + + private UserInfo createNewProfile(UserInfo parent) { + final UserInfo ui = createNewUser(); + parent.profileGroupId = parent.id; + ui.profileGroupId = parent.id; + doReturn(true).when(ui).isProfile(); + return ui; + } + + private UserInfo createNewUser() { + final UserInfo ui = mock(UserInfo.class); + ui.id = mNextUserId++; + doReturn(ui).when(mUserManagerInternal).getUserInfo(ui.id); + ui.profileGroupId = UserInfo.NO_PROFILE_GROUP_ID; + return ui; + } + + private static JobStatus createJob(UserInfo userInfo) { + JobStatus jobStatus = JobStatus.createFromJobInfo( + new JobInfo.Builder(1, new ComponentName("foo", "bar")).build(), + userInfo.id * UserHandle.PER_USER_RANGE, + null, userInfo.id, "JobConcurrencyManagerTest"); + return jobStatus; + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index 4effa4d445bb..f2bb47bfb8ad 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -62,6 +62,7 @@ import com.android.server.LocalServices; import com.android.server.PowerAllowlistInternal; import com.android.server.SystemServiceManager; import com.android.server.job.controllers.JobStatus; +import com.android.server.pm.UserManagerInternal; import com.android.server.usage.AppStandbyInternal; import org.junit.After; @@ -128,6 +129,9 @@ public class JobSchedulerServiceTest { // Called in DeviceIdleJobsController constructor. doReturn(mock(DeviceIdleInternal.class)) .when(() -> LocalServices.getService(DeviceIdleInternal.class)); + // Used in JobConcurrencyManager. + doReturn(mock(UserManagerInternal.class)) + .when(() -> LocalServices.getService(UserManagerInternal.class)); // Used in JobStatus. doReturn(mock(PackageManagerInternal.class)) .when(() -> LocalServices.getService(PackageManagerInternal.class)); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java index 3b5cc887c798..54fa89a1e24b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java @@ -64,6 +64,7 @@ import android.location.LocationManagerInternal; import android.location.LocationManagerInternal.ProviderEnabledListener; import android.location.LocationRequest; import android.location.LocationResult; +import android.location.provider.IProviderRequestListener; import android.location.provider.ProviderProperties; import android.location.provider.ProviderRequest; import android.location.util.identity.CallerIdentity; @@ -662,6 +663,23 @@ public class LocationProviderManagerTest { } @Test + public void testProviderRequestListener() throws Exception { + IProviderRequestListener requestListener = mock(IProviderRequestListener.class); + mManager.addProviderRequestListener(requestListener); + + ILocationListener locationListener = createMockLocationListener(); + LocationRequest request = new LocationRequest.Builder(1).setWorkSource( + WORK_SOURCE).build(); + mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, locationListener); + + verify(requestListener, timeout(TIMEOUT_MS).times(1)).onProviderRequestChanged(anyString(), + any(ProviderRequest.class)); + + mManager.unregisterLocationRequest(locationListener); + mManager.removeProviderRequestListener(requestListener); + } + + @Test public void testGetCurrentLocation() throws Exception { ILocationCallback listener = createMockGetCurrentLocationListener(); LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 6daa381f526e..7a0cb8e5dead 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -59,9 +59,9 @@ android_test { }, libs: [ - "android.hardware.power-java", + "android.hardware.power-V1-java", "android.hardware.tv.cec-V1.0-java", - "android.hardware.vibrator-java", + "android.hardware.vibrator-V1-java", "android.hidl.manager-V1.0-java", "android.test.mock", "android.test.base", @@ -88,7 +88,7 @@ android_test { "libui", "libunwindstack", "libutils", - "netd_aidl_interface-cpp", + "netd_aidl_interface-V5-cpp", ], dxflags: ["--multi-dex"], diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java index 2a7905a451b9..633957a8b13a 100644 --- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java @@ -50,6 +50,7 @@ import android.os.Looper; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; +import android.os.SystemClock; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationEffect; @@ -81,6 +82,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import java.util.stream.Collectors; /** @@ -92,6 +94,7 @@ import java.util.stream.Collectors; @Presubmit public class VibratorServiceTest { + private static final int TEST_TIMEOUT_MILLIS = 1_000; private static final int UID = Process.ROOT_UID; private static final int VIBRATOR_ID = 1; private static final String PACKAGE_NAME = "package"; @@ -342,8 +345,8 @@ public class VibratorServiceTest { verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any()); // VibrationThread will start this vibration async, so wait before checking it never played. - Thread.sleep(10); - assertTrue(mVibratorProvider.getEffects().isEmpty()); + assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service, + /* timeout= */ 20)); } @Test @@ -399,8 +402,8 @@ public class VibratorServiceTest { verify(mIInputManagerMock).vibrate(eq(1), any(), any()); // VibrationThread will start this vibration async, so wait before checking it never played. - Thread.sleep(10); - assertTrue(mVibratorProvider.getEffects().isEmpty()); + assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service, + /* timeout= */ 20)); } @Test @@ -409,16 +412,10 @@ public class VibratorServiceTest { mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS); - - // VibrationThread will start this vibration async, so wait before triggering callbacks. - Thread.sleep(10); - assertTrue(service.isVibrating()); + assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS)); mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); - - // Wait for callback to cancel vibration. - Thread.sleep(10); - assertFalse(service.isVibrating()); + assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS)); } @Test @@ -427,26 +424,18 @@ public class VibratorServiceTest { mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); vibrate(service, VibrationEffect.createOneShot(1000, 100), RINGTONE_ATTRS); - - // VibrationThread will start this vibration async, so wait before triggering callbacks. - Thread.sleep(10); - assertTrue(service.isVibrating()); + assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS)); mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); - - // Wait for callback to cancel vibration. - Thread.sleep(10); - assertTrue(service.isVibrating()); + // Settings callback is async, so wait before checking it never got cancelled. + assertFalse(waitUntil(s -> !s.isVibrating(), service, /* timeout= */ 20)); } @Test public void vibrate_withSettingsChanged_doNotCancelVibration() throws Exception { VibratorService service = createService(); vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS); - - // VibrationThread will start this vibration async, so wait before triggering callbacks. - Thread.sleep(10); - assertTrue(service.isVibrating()); + assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS)); setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); @@ -454,9 +443,8 @@ public class VibratorServiceTest { // FakeSettingsProvider don't support testing triggering ContentObserver yet. service.updateVibrators(); - // Wait for callback to cancel vibration. - Thread.sleep(10); - assertTrue(service.isVibrating()); + // Settings callback is async, so wait before checking it never got cancelled. + assertFalse(waitUntil(s -> !s.isVibrating(), service, /* timeout= */ 20)); } @Test @@ -488,8 +476,8 @@ public class VibratorServiceTest { inOrderVerifier.verify(mIInputManagerMock).vibrate(eq(2), eq(effect), any()); // VibrationThread will start this vibration async, so wait before checking it never played. - Thread.sleep(10); - assertTrue(mVibratorProvider.getEffects().isEmpty()); + assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service, + /* timeout= */ 20)); } @Test @@ -521,8 +509,8 @@ public class VibratorServiceTest { verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any()); // VibrationThread will start this vibration async, so wait before checking it never played. - Thread.sleep(10); - assertTrue(mVibratorProvider.getEffects().isEmpty()); + assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service, + /* timeout= */ 20)); } @Test @@ -531,18 +519,12 @@ public class VibratorServiceTest { VibratorService service = createService(); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS); - - // VibrationThread will start this vibration async, so wait before triggering callbacks. - Thread.sleep(10); - assertTrue(service.isVibrating()); + assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS)); // Trigger callbacks from controller. mTestLooper.moveTimeForward(50); mTestLooper.dispatchAll(); - - // VibrationThread needs some time to react to native callbacks and stop the vibrator. - Thread.sleep(10); - assertFalse(service.isVibrating()); + assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS)); } @Test @@ -550,16 +532,10 @@ public class VibratorServiceTest { VibratorService service = createService(); vibrate(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS); - - // VibrationThread will start this vibration async, so wait before checking. - Thread.sleep(10); - assertTrue(service.isVibrating()); + assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS)); service.cancelVibrate(service); - - // VibrationThread will stop this vibration async, so wait before checking. - Thread.sleep(10); - assertFalse(service.isVibrating()); + assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS)); } @Test @@ -584,17 +560,13 @@ public class VibratorServiceTest { service.registerVibratorStateListener(mVibratorStateListenerMock); vibrate(service, VibrationEffect.createOneShot(30, 100), ALARM_ATTRS); - - // VibrationThread will start this vibration async, so wait before triggering callbacks. - Thread.sleep(10); - assertTrue(service.isVibrating()); + assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS)); service.unregisterVibratorStateListener(mVibratorStateListenerMock); // Trigger callbacks from controller. mTestLooper.moveTimeForward(50); mTestLooper.dispatchAll(); - Thread.sleep(20); - assertFalse(service.isVibrating()); + assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS)); InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock); // First notification done when listener is registered. @@ -771,4 +743,15 @@ public class VibratorServiceTest { private void setGlobalSetting(String settingName, int value) { Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value); } + + private boolean waitUntil(Predicate<VibratorService> predicate, + VibratorService service, long timeout) throws InterruptedException { + long timeoutTimestamp = SystemClock.uptimeMillis() + timeout; + boolean predicateResult = false; + while (!predicateResult && SystemClock.uptimeMillis() < timeoutTimestamp) { + Thread.sleep(10); + predicateResult = predicate.test(service); + } + return predicateResult; + } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 8c853ccdd55b..7597cbf322f5 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -5616,43 +5616,52 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testDisallowSharingIntoProfileSetRestriction() { when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package)) .thenReturn("com.android.managedprovisioning"); + when(getServices().userManagerInternal.getProfileParentId(anyInt())) + .thenReturn(UserHandle.USER_SYSTEM); + mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID; + mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID; Bundle restriction = new Bundle(); restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true); - mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - RestrictionsListener listener = new RestrictionsListener(mContext); + RestrictionsListener listener = new RestrictionsListener( + mServiceContext, getServices().userManagerInternal, dpms); listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, restriction, new Bundle()); - verifyDataSharingChangedBroadcast(); + + verifyDataSharingAppliedBroadcast(); } @Test public void testDisallowSharingIntoProfileClearRestriction() { when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package)) .thenReturn("com.android.managedprovisioning"); + when(getServices().userManagerInternal.getProfileParentId(anyInt())) + .thenReturn(UserHandle.USER_SYSTEM); + mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID; + mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID; Bundle restriction = new Bundle(); restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true); - mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - RestrictionsListener listener = new RestrictionsListener(mContext); + RestrictionsListener listener = new RestrictionsListener( + mServiceContext, getServices().userManagerInternal, dpms); listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), restriction); - verifyDataSharingChangedBroadcast(); + + verifyDataSharingAppliedBroadcast(); } @Test public void testDisallowSharingIntoProfileUnchanged() { - RestrictionsListener listener = new RestrictionsListener(mContext); + RestrictionsListener listener = new RestrictionsListener( + mContext, getServices().userManagerInternal, dpms); listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), new Bundle()); verify(mContext.spiedContext, never()).sendBroadcastAsUser(any(), any()); } - private void verifyDataSharingChangedBroadcast() { + private void verifyDataSharingAppliedBroadcast() { Intent expectedIntent = new Intent( - DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED); - expectedIntent.setPackage("com.android.managedprovisioning"); - expectedIntent.putExtra(Intent.EXTRA_USER_ID, CALLER_USER_HANDLE); + DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED); verify(mContext.spiedContext, times(1)).sendBroadcastAsUser( MockUtils.checkIntent(expectedIntent), - MockUtils.checkUserHandle(UserHandle.USER_SYSTEM)); + MockUtils.checkUserHandle(CALLER_USER_HANDLE)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index 54da6436ad89..1a2266139405 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -19,10 +19,14 @@ package com.android.server.devicestate; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertThrows; +import android.hardware.devicestate.DeviceStateRequest; import android.hardware.devicestate.IDeviceStateManagerCallback; +import android.os.Binder; +import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; @@ -33,6 +37,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.HashMap; import java.util.Optional; import javax.annotation.Nullable; @@ -61,87 +66,69 @@ public final class DeviceStateManagerServiceTest { } @Test - public void requestStateChange() { + public void baseStateChanged() { assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); - mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier()); + mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); } @Test - public void requestStateChange_pendingState() { + public void baseStateChanged_withStatePendingPolicyCallback() { mPolicy.blockConfigure(); - mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier()); + mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); - mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier()); + mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier()); assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); mPolicy.resumeConfigure(); assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); } @Test - public void requestStateChange_unsupportedState() { - mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE.getIdentifier()); + public void baseStateChanged_unsupportedState() { + assertThrows(IllegalArgumentException.class, () -> { + mProvider.setState(UNSUPPORTED_DEVICE_STATE.getIdentifier()); + }); + assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); } @Test - public void requestStateChange_invalidState() { + public void baseStateChanged_invalidState() { assertThrows(IllegalArgumentException.class, () -> { - mProvider.notifyRequestState(INVALID_DEVICE_STATE); + mProvider.setState(INVALID_DEVICE_STATE); }); - } - - @Test - public void requestOverrideState() { - mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier()); - // Committed state changes as there is a requested override. - assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); - assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), - OTHER_DEVICE_STATE.getIdentifier()); - - // Committed state is set back to the requested state once the override is cleared. - mService.clearOverrideState(); - assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); - assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), - DEFAULT_DEVICE_STATE.getIdentifier()); - } - @Test - public void requestOverrideState_unsupportedState() { - mService.setOverrideState(UNSUPPORTED_DEVICE_STATE.getIdentifier()); - // Committed state remains the same as the override state is unsupported. assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getPendingState(), Optional.empty()); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); } @@ -150,7 +137,7 @@ public final class DeviceStateManagerServiceTest { public void supportedStatesChanged() { assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE }); @@ -158,46 +145,27 @@ public final class DeviceStateManagerServiceTest { // supported. assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); } @Test - public void supportedStatesChanged_unsupportedRequestedState() { + public void supportedStatesChanged_unsupportedBaseState() { assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); mProvider.notifySupportedDeviceStates(new DeviceState[]{ OTHER_DEVICE_STATE }); // The current requested state is cleared because it is no longer supported. assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState(), Optional.empty()); + assertEquals(mService.getBaseState(), Optional.empty()); - mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier()); + mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE); - } - - @Test - public void supportedStatesChanged_unsupportedOverrideState() { - mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier()); - // Committed state changes as there is a requested override. - assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); - assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), - OTHER_DEVICE_STATE.getIdentifier()); - - mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE }); - - // Committed state is set back to the requested state as the override state is no longer - // supported. - assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); - assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), - DEFAULT_DEVICE_STATE.getIdentifier()); + assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE); } @Test @@ -205,17 +173,17 @@ public final class DeviceStateManagerServiceTest { TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); mService.getBinderService().registerCallback(callback); - mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier()); + mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); assertNotNull(callback.getLastNotifiedValue()); assertEquals(callback.getLastNotifiedValue().intValue(), OTHER_DEVICE_STATE.getIdentifier()); - mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier()); + mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier()); assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE.getIdentifier()); mPolicy.blockConfigure(); - mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier()); + mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); // The callback should not have been notified of the state change as the policy is still // pending callback. assertEquals(callback.getLastNotifiedValue().intValue(), @@ -237,6 +205,148 @@ public final class DeviceStateManagerServiceTest { DEFAULT_DEVICE_STATE.getIdentifier()); } + @Test + public void getSupportedDeviceStates() throws RemoteException { + final int[] expectedStates = new int[] { 0, 1 }; + assertEquals(mService.getBinderService().getSupportedDeviceStates(), expectedStates); + } + + @Test + public void requestState() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + + final IBinder token = new Binder(); + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_UNKNOWN); + + mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(), + 0 /* flags */); + + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_ACTIVE); + // Committed state changes as there is a requested override. + assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + OTHER_DEVICE_STATE.getIdentifier()); + + + mService.getBinderService().cancelRequest(token); + + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_CANCELED); + // Committed state is set back to the requested state once the override is cleared. + assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertFalse(mService.getOverrideState().isPresent()); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + DEFAULT_DEVICE_STATE.getIdentifier()); + } + + @Test + public void requestState_flagCancelWhenBaseChanges() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + + final IBinder token = new Binder(); + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_UNKNOWN); + + mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(), + DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES); + + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_ACTIVE); + + // Committed state changes as there is a requested override. + assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + OTHER_DEVICE_STATE.getIdentifier()); + + mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); + + // Request is canceled because the base state changed. + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_CANCELED); + // Committed state is set back to the requested state once the override is cleared. + assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE); + assertFalse(mService.getOverrideState().isPresent()); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + OTHER_DEVICE_STATE.getIdentifier()); + } + + @Test + public void requestState_becomesUnsupported() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + + final IBinder token = new Binder(); + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_UNKNOWN); + + mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(), + 0 /* flags */); + + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_ACTIVE); + // Committed state changes as there is a requested override. + assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + OTHER_DEVICE_STATE.getIdentifier()); + + mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE }); + + // Request is canceled because the state is no longer supported. + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_CANCELED); + // Committed state is set back to the requested state as the override state is no longer + // supported. + assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertFalse(mService.getOverrideState().isPresent()); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + DEFAULT_DEVICE_STATE.getIdentifier()); + } + + @Test + public void requestState_unsupportedState() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + + assertThrows(IllegalArgumentException.class, () -> { + final IBinder token = new Binder(); + mService.getBinderService().requestState(token, + UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */); + }); + } + + @Test + public void requestState_invalidState() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + + assertThrows(IllegalArgumentException.class, () -> { + final IBinder token = new Binder(); + mService.getBinderService().requestState(token, INVALID_DEVICE_STATE, 0 /* flags */); + }); + } + + @Test + public void requestState_beforeRegisteringCallback() { + assertThrows(IllegalStateException.class, () -> { + final IBinder token = new Binder(); + mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(), + 0 /* flags */); + }); + } + private static final class TestDeviceStatePolicy implements DeviceStatePolicy { private final DeviceStateProvider mProvider; private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE; @@ -306,23 +416,48 @@ public final class DeviceStateManagerServiceTest { mListener.onSupportedDeviceStatesChanged(supportedDeviceStates); } - public void notifyRequestState(int identifier) { + public void setState(int identifier) { mListener.onStateChanged(identifier); } } private static final class TestDeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub { - Integer mLastNotifiedValue; + public static final int STATUS_UNKNOWN = 0; + public static final int STATUS_ACTIVE = 1; + public static final int STATUS_SUSPENDED = 2; + public static final int STATUS_CANCELED = 3; + + private Integer mLastNotifiedValue; + private final HashMap<IBinder, Integer> mLastNotifiedStatus = new HashMap<>(); @Override public void onDeviceStateChanged(int deviceState) { mLastNotifiedValue = deviceState; } + @Override + public void onRequestActive(IBinder token) { + mLastNotifiedStatus.put(token, STATUS_ACTIVE); + } + + @Override + public void onRequestSuspended(IBinder token) { + mLastNotifiedStatus.put(token, STATUS_SUSPENDED); + } + + @Override + public void onRequestCanceled(IBinder token) { + mLastNotifiedStatus.put(token, STATUS_CANCELED); + } + @Nullable Integer getLastNotifiedValue() { return mLastNotifiedValue; } + + int getLastNotifiedStatus(IBinder requestToken) { + return mLastNotifiedStatus.getOrDefault(requestToken, STATUS_UNKNOWN); + } } } diff --git a/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java b/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java new file mode 100644 index 000000000000..1915b8c36892 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.job; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import android.app.ActivityManagerInternal; +import android.content.Context; +import android.os.RemoteException; +import android.os.SystemClock; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.LocalServices; +import com.android.server.job.JobConcurrencyManager.GracePeriodObserver; +import com.android.server.pm.UserManagerInternal; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.time.Clock; +import java.time.Duration; +import java.time.ZoneOffset; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class GracePeriodObserverTest { + private GracePeriodObserver mGracePeriodObserver; + private UserManagerInternal mUserManagerInternal; + private static final int FIRST_USER = 0; + + @BeforeClass + public static void setUpOnce() { + UserManagerInternal userManagerInternal = mock(UserManagerInternal.class); + LocalServices.addService(UserManagerInternal.class, userManagerInternal); + ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class); + LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal); + } + + @AfterClass + public static void tearDownOnce() { + LocalServices.removeServiceForTest(UserManagerInternal.class); + LocalServices.removeServiceForTest(ActivityManagerInternal.class); + } + + @Before + public void setUp() { + final Context context = ApplicationProvider.getApplicationContext(); + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); + doReturn(FIRST_USER) + .when(LocalServices.getService(ActivityManagerInternal.class)).getCurrentUserId(); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); + doReturn(true).when(mUserManagerInternal).exists(FIRST_USER); + mGracePeriodObserver = new GracePeriodObserver(context); + } + + @Test + public void testGracePeriod() throws RemoteException { + final int oldUser = FIRST_USER; + final int newUser = 10; + doReturn(true).when(mUserManagerInternal).exists(newUser); + mGracePeriodObserver.onUserSwitchComplete(newUser); + assertTrue(mGracePeriodObserver.isWithinGracePeriodForUser(oldUser)); + JobSchedulerService.sElapsedRealtimeClock = + Clock.offset(JobSchedulerService.sElapsedRealtimeClock, + Duration.ofMillis(mGracePeriodObserver.mGracePeriod)); + assertFalse(mGracePeriodObserver.isWithinGracePeriodForUser(oldUser)); + } + + @Test + public void testCleanUp() throws RemoteException { + final int removedUser = FIRST_USER; + final int newUser = 10; + mGracePeriodObserver.onUserSwitchComplete(newUser); + + final int sizeBefore = mGracePeriodObserver.mGracePeriodExpiration.size(); + doReturn(false).when(mUserManagerInternal).exists(removedUser); + + mGracePeriodObserver.onUserRemoved(removedUser); + assertEquals(sizeBefore - 1, mGracePeriodObserver.mGracePeriodExpiration.size()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java index 263cf48a8a18..353ac4bd2129 100644 --- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java @@ -16,7 +16,10 @@ package com.android.server.job; +import static com.android.server.job.JobConcurrencyManager.NUM_WORK_TYPES; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG; +import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER; +import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP; @@ -47,6 +50,10 @@ import java.util.Random; public class WorkCountTrackerTest { private static final String TAG = "WorkerCountTrackerTest"; + private static final double[] EQUAL_PROBABILITY_CDF = + buildCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, + 1.0 / NUM_WORK_TYPES); + private Random mRandom; private WorkCountTracker mWorkCountTracker; @@ -56,6 +63,47 @@ public class WorkCountTrackerTest { mWorkCountTracker = new WorkCountTracker(); } + @NonNull + private static double[] buildCdf(double pTop, double pEj, double pBg, double pBgUser) { + double[] cdf = new double[JobConcurrencyManager.NUM_WORK_TYPES]; + double sum = 0; + + sum += pTop; + cdf[0] = sum; + sum += pEj; + cdf[1] = sum; + sum += pBg; + cdf[2] = sum; + sum += pBgUser; + cdf[3] = sum; + + if (Double.compare(1, sum) != 0) { + throw new IllegalArgumentException("probabilities don't sum to one: " + sum); + } + return cdf; + } + + @JobConcurrencyManager.WorkType + static int getRandomWorkType(double[] cdf, double rand) { + for (int i = cdf.length - 1; i >= 0; --i) { + if (rand < cdf[i] && (i == 0 || rand > cdf[i - 1])) { + switch (i) { + case 0: + return WORK_TYPE_TOP; + case 1: + return WORK_TYPE_EJ; + case 2: + return WORK_TYPE_BG; + case 3: + return WORK_TYPE_BGUSER; + default: + throw new IllegalStateException("Unknown work type"); + } + } + } + throw new IllegalStateException("Couldn't pick random work type"); + } + /** * Represents running and pending jobs. */ @@ -63,25 +111,22 @@ public class WorkCountTrackerTest { public final SparseIntArray running = new SparseIntArray(); public final SparseIntArray pending = new SparseIntArray(); - public void maybeEnqueueJobs(double startRatio, double fgJobRatio) { - while (mRandom.nextDouble() < startRatio) { - if (mRandom.nextDouble() < fgJobRatio) { - pending.put(WORK_TYPE_TOP, pending.get(WORK_TYPE_TOP) + 1); - } else { - pending.put(WORK_TYPE_BG, pending.get(WORK_TYPE_BG) + 1); - } + public void maybeEnqueueJobs(double probStart, double[] typeCdf) { + while (mRandom.nextDouble() < probStart) { + final int workType = getRandomWorkType(typeCdf, mRandom.nextDouble()); + pending.put(workType, pending.get(workType) + 1); } } - public void maybeFinishJobs(double stopRatio) { + public void maybeFinishJobs(double probStop) { for (int i = running.get(WORK_TYPE_BG); i > 0; i--) { - if (mRandom.nextDouble() < stopRatio) { + if (mRandom.nextDouble() < probStop) { running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1); mWorkCountTracker.onJobFinished(WORK_TYPE_BG); } } for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) { - if (mRandom.nextDouble() < stopRatio) { + if (mRandom.nextDouble() < probStop) { running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1); mWorkCountTracker.onJobFinished(WORK_TYPE_TOP); } @@ -116,29 +161,27 @@ public class WorkCountTrackerTest { mWorkCountTracker.onCountDone(); } + private boolean hasStartablePendingJob(Jobs jobs) { + for (int i = 0; i < jobs.pending.size(); ++i) { + if (jobs.pending.valueAt(i) > 0 + && mWorkCountTracker.canJobStart(jobs.pending.keyAt(i)) != WORK_TYPE_NONE) { + return true; + } + } + return false; + } + private void startPendingJobs(Jobs jobs) { - while ((jobs.pending.get(WORK_TYPE_TOP) > 0 - && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) - || (jobs.pending.get(WORK_TYPE_BG) > 0 - && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) { - final boolean isStartingFg = mRandom.nextBoolean(); - - if (isStartingFg) { - if (jobs.pending.get(WORK_TYPE_TOP) > 0 - && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) { - jobs.pending.put(WORK_TYPE_TOP, jobs.pending.get(WORK_TYPE_TOP) - 1); - jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) + 1); - mWorkCountTracker.stageJob(WORK_TYPE_TOP); - mWorkCountTracker.onJobStarted(WORK_TYPE_TOP); - } - } else { - if (jobs.pending.get(WORK_TYPE_BG) > 0 - && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) { - jobs.pending.put(WORK_TYPE_BG, jobs.pending.get(WORK_TYPE_BG) - 1); - jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) + 1); - mWorkCountTracker.stageJob(WORK_TYPE_BG); - mWorkCountTracker.onJobStarted(WORK_TYPE_BG); - } + while (hasStartablePendingJob(jobs)) { + final int startingWorkType = + getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble()); + + if (jobs.pending.get(startingWorkType) > 0 + && mWorkCountTracker.canJobStart(startingWorkType) != WORK_TYPE_NONE) { + jobs.pending.put(startingWorkType, jobs.pending.get(startingWorkType) - 1); + jobs.running.put(startingWorkType, jobs.running.get(startingWorkType) + 1); + mWorkCountTracker.stageJob(startingWorkType); + mWorkCountTracker.onJobStarted(startingWorkType); } } } @@ -149,10 +192,10 @@ public class WorkCountTrackerTest { private void checkRandom(Jobs jobs, int numTests, int totalMax, @NonNull List<Pair<Integer, Integer>> minLimits, @NonNull List<Pair<Integer, Integer>> maxLimits, - double startRatio, double fgJobRatio, double stopRatio) { + double probStart, double[] typeCdf, double probStop) { for (int i = 0; i < numTests; i++) { - jobs.maybeFinishJobs(stopRatio); - jobs.maybeEnqueueJobs(startRatio, fgJobRatio); + jobs.maybeFinishJobs(probStop); + jobs.maybeEnqueueJobs(probStart, typeCdf); recount(jobs, totalMax, minLimits, maxLimits); startPendingJobs(jobs); @@ -178,18 +221,19 @@ public class WorkCountTrackerTest { */ @Test public void testRandom1() { + assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES); + final Jobs jobs = new Jobs(); final int numTests = 5000; final int totalMax = 6; final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4)); final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); - final double stopRatio = 0.1; - final double fgJobRatio = 0.5; - final double startRatio = 0.1; + final double probStop = 0.1; + final double probStart = 0.1; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, + EQUAL_PROBABILITY_CDF, probStop); } @Test @@ -198,14 +242,14 @@ public class WorkCountTrackerTest { final int numTests = 5000; final int totalMax = 2; - final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); + final List<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final List<Pair<Integer, Integer>> minLimits = List.of(); - final double stopRatio = 0.5; - final double fgJobRatio = 0.5; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(0.5, 0, 0.5, 0); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -214,14 +258,14 @@ public class WorkCountTrackerTest { final int numTests = 5000; final int totalMax = 2; - final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); + final List<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); - final double stopRatio = 0.5; - final double fgJobRatio = 0.5; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -230,14 +274,14 @@ public class WorkCountTrackerTest { final int numTests = 5000; final int totalMax = 10; - final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); + final List<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); final List<Pair<Integer, Integer>> minLimits = List.of(); - final double stopRatio = 0.5; - final double fgJobRatio = 0.5; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -246,14 +290,14 @@ public class WorkCountTrackerTest { final int numTests = 5000; final int totalMax = 6; - final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4)); + final List<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); - final double stopRatio = 0.5; - final double fgJobRatio = 0.1; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(0.1, 0, 0.8, .1); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -262,14 +306,14 @@ public class WorkCountTrackerTest { final int numTests = 5000; final int totalMax = 6; - final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4)); + final List<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); - final double stopRatio = 0.5; - final double fgJobRatio = 0.9; - final double startRatio = 0.5; + final double probStop = 0.5; + final double[] cdf = buildCdf(0.9, 0, 0.1, 0); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -278,14 +322,14 @@ public class WorkCountTrackerTest { final int numTests = 5000; final int totalMax = 6; - final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4)); + final List<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); - final double stopRatio = 0.4; - final double fgJobRatio = 0.1; - final double startRatio = 0.5; + final double probStop = 0.4; + final double[] cdf = buildCdf(0.1, 0, 0.1, .8); + final double probStart = 0.5; - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, stopRatio); + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } @Test @@ -294,14 +338,136 @@ public class WorkCountTrackerTest { final int numTests = 5000; final int totalMax = 6; + final List<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); + final List<Pair<Integer, Integer>> minLimits = + List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); + final double probStop = 0.4; + final double[] cdf = buildCdf(0.9, 0, 0.05, 0.05); + final double probStart = 0.5; + + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); + } + + @Test + public void testRandom9() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final List<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); + final List<Pair<Integer, Integer>> minLimits = + List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); + final double probStop = 0.5; + final double[] cdf = buildCdf(0, 0, 0.5, 0.5); + final double probStart = 0.5; + + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); + } + + @Test + public void testRandom10() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final List<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); + final List<Pair<Integer, Integer>> minLimits = + List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); + final double probStop = 0.5; + final double[] cdf = buildCdf(0, 0, 0.1, 0.9); + final double probStart = 0.5; + + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); + } + + @Test + public void testRandom11() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final List<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)); + final List<Pair<Integer, Integer>> minLimits = + List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1)); + final double probStop = 0.5; + final double[] cdf = buildCdf(0, 0, 0.9, 0.1); + final double probStart = 0.5; + + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); + } + + @Test + public void testRandom12() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4)); + final List<Pair<Integer, Integer>> minLimits = + List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)); + final double probStop = 0.4; + final double[] cdf = buildCdf(0.5, 0.5, 0, 0); + final double probStart = 0.5; + + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); + } + + @Test + public void testRandom13() { + assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES); + + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 13; + final List<Pair<Integer, Integer>> maxLimits = List.of( + Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4), + Pair.create(WORK_TYPE_BGUSER, 3)); + final List<Pair<Integer, Integer>> minLimits = + List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1)); + final double probStop = 0.01; + final double probStart = 0.99; + + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, + EQUAL_PROBABILITY_CDF, probStop); + } + + @Test + public void testRandom14() { + final Jobs jobs = new Jobs(); + + final int numTests = 5000; + final int totalMax = 6; + final List<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4)); final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2)); - final double stopRatio = 0.4; - final double fgJobRatio = 0.9; - final double startRatio = 0.5; + final double probStop = 0.4; + final double[] cdf = buildCdf(.1, 0.5, 0.35, 0.05); + final double probStart = 0.5; + + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); + } + + @Test + public void testRandom15() { + final Jobs jobs = new Jobs(); - checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, - startRatio, fgJobRatio, stopRatio); + final int numTests = 5000; + final int totalMax = 6; + final List<Pair<Integer, Integer>> maxLimits = + List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4), + Pair.create(WORK_TYPE_BGUSER, 1)); + final List<Pair<Integer, Integer>> minLimits = + List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2)); + final double probStop = 0.4; + final double[] cdf = buildCdf(0.01, 0.49, 0.1, 0.4); + final double probStart = 0.5; + + checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop); } /** Used by the following tests */ @@ -337,7 +503,7 @@ public class WorkCountTrackerTest { public void testBasic() { checkSimple(6, /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)), - /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)), /* run */ List.of(), /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 1)), /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 1)), @@ -345,7 +511,7 @@ public class WorkCountTrackerTest { checkSimple(6, /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)), - /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)), /* run */ List.of(), /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10)), /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)), @@ -354,7 +520,7 @@ public class WorkCountTrackerTest { // When there are BG jobs pending, 2 (min-BG) jobs should run. checkSimple(6, /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)), - /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)), /* run */ List.of(), /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)), /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)), @@ -385,6 +551,16 @@ public class WorkCountTrackerTest { checkSimple(8, /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 6)), + /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)), + /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 49), Pair.create(WORK_TYPE_BG, 49)), + /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 4)), + /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 47), Pair.create(WORK_TYPE_BG, 49)) + ); + + + checkSimple(8, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)), /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 6)), /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)), /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)), @@ -407,6 +583,14 @@ public class WorkCountTrackerTest { /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)), /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49))); + checkSimple(8, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 1)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)), + /* run */ List.of(Pair.create(WORK_TYPE_BG, 6)), + /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)), + /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)), + /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49))); + checkSimple(6, /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)), /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)), @@ -415,6 +599,16 @@ public class WorkCountTrackerTest { /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)), /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3))); + checkSimple(8, + /* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)), + /* run */ List.of(Pair.create(WORK_TYPE_TOP, 6)), + /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_EJ, 5), + Pair.create(WORK_TYPE_BG, 3)), + /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 2)), + /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3), + Pair.create(WORK_TYPE_BG, 3))); + // This could happen if we lower the effective config due to higher memory pressure after // we've already started running jobs. We shouldn't stop already running jobs, but also // shouldn't start new ones. @@ -425,6 +619,38 @@ public class WorkCountTrackerTest { /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)), /* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)), /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3))); + + checkSimple(6, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)), + /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)), + /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), + Pair.create(WORK_TYPE_BG, 3), + Pair.create(WORK_TYPE_BGUSER, 3)), + /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)), + /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 6), + Pair.create(WORK_TYPE_BG, 3), + Pair.create(WORK_TYPE_BGUSER, 3))); + + checkSimple(6, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 3)), + /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)), + /* pen */ List.of(Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 3)), + /* resRun */ List.of( + Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)), + /* resPen */ List.of( + Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))); + + checkSimple(6, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)), + /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)), + /* pen */ List.of(Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 3)), + /* resRun */ List.of( + Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)), + /* resPen */ List.of( + Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 2))); } /** Tests that the counter updates properly when jobs are stopped. */ diff --git a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java index c28292f03357..2288a8925561 100644 --- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java @@ -16,8 +16,11 @@ package com.android.server.job; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG; +import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER; +import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import android.annotation.NonNull; @@ -31,11 +34,9 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.job.JobConcurrencyManager.WorkTypeConfig; import org.junit.After; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.ArrayList; import java.util.List; @RunWith(AndroidJUnit4.class) @@ -43,9 +44,13 @@ import java.util.List; public class WorkTypeConfigTest { private static final String KEY_MAX_TOTAL = "concurrency_max_total_test"; private static final String KEY_MAX_TOP = "concurrency_max_top_test"; + private static final String KEY_MAX_EJ = "concurrency_max_ej_test"; private static final String KEY_MAX_BG = "concurrency_max_bg_test"; + private static final String KEY_MAX_BGUSER = "concurrency_max_bguser_test"; private static final String KEY_MIN_TOP = "concurrency_min_top_test"; + private static final String KEY_MIN_EJ = "concurrency_min_ej_test"; private static final String KEY_MIN_BG = "concurrency_min_bg_test"; + private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test"; @After public void tearDown() throws Exception { @@ -56,43 +61,27 @@ public class WorkTypeConfigTest { // DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually. DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, "", false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, "", false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, "", false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, "", false); } private void check(@Nullable DeviceConfig.Properties config, int defaultTotal, - @Nullable Pair<Integer, Integer> defaultTopLimits, - @Nullable Pair<Integer, Integer> defaultBgLimits, + @NonNull List<Pair<Integer, Integer>> defaultMin, + @NonNull List<Pair<Integer, Integer>> defaultMax, boolean expectedValid, int expectedTotal, - @NonNull Pair<Integer, Integer> expectedTopLimits, - @NonNull Pair<Integer, Integer> expectedBgLimits) throws Exception { + @NonNull List<Pair<Integer, Integer>> expectedMinLimits, + @NonNull List<Pair<Integer, Integer>> expectedMaxLimits) throws Exception { resetConfig(); if (config != null) { DeviceConfig.setProperties(config); } - List<Pair<Integer, Integer>> defaultMin = new ArrayList<>(); - List<Pair<Integer, Integer>> defaultMax = new ArrayList<>(); - Integer val; - if (defaultTopLimits != null) { - if ((val = defaultTopLimits.first) != null) { - defaultMin.add(Pair.create(WORK_TYPE_TOP, val)); - } - if ((val = defaultTopLimits.second) != null) { - defaultMax.add(Pair.create(WORK_TYPE_TOP, val)); - } - } - if (defaultBgLimits != null) { - if ((val = defaultBgLimits.first) != null) { - defaultMin.add(Pair.create(WORK_TYPE_BG, val)); - } - if ((val = defaultBgLimits.second) != null) { - defaultMax.add(Pair.create(WORK_TYPE_BG, val)); - } - } - final WorkTypeConfig counts; try { counts = new WorkTypeConfig("test", @@ -112,40 +101,126 @@ public class WorkTypeConfigTest { counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER)); - Assert.assertEquals(expectedTotal, counts.getMaxTotal()); - Assert.assertEquals((int) expectedTopLimits.first, counts.getMinReserved(WORK_TYPE_TOP)); - Assert.assertEquals((int) expectedTopLimits.second, counts.getMax(WORK_TYPE_TOP)); - Assert.assertEquals((int) expectedBgLimits.first, counts.getMinReserved(WORK_TYPE_BG)); - Assert.assertEquals((int) expectedBgLimits.second, counts.getMax(WORK_TYPE_BG)); + assertEquals(expectedTotal, counts.getMaxTotal()); + for (Pair<Integer, Integer> min : expectedMinLimits) { + assertEquals((int) min.second, counts.getMinReserved(min.first)); + } + for (Pair<Integer, Integer> max : expectedMaxLimits) { + assertEquals((int) max.second, counts.getMax(max.first)); + } } @Test public void test() throws Exception { // Tests with various combinations. - check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1), - /*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1)); - check(null, /*default*/ 5, Pair.create(5, null), Pair.create(0, 0), - /*expected*/ true, 5, Pair.create(5, 5), Pair.create(0, 1)); - check(null, /*default*/ 0, Pair.create(5, null), Pair.create(0, 0), - /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1)); - check(null, /*default*/ -1, null, Pair.create(-1, -1), - /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1)); - check(null, /*default*/ 5, null, Pair.create(5, 5), - /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5)); - check(null, /*default*/ 6, Pair.create(1, null), Pair.create(6, 5), - /*expected*/ false, 6, Pair.create(1, 6), Pair.create(5, 5)); - check(null, /*default*/ 4, null, Pair.create(6, 5), - /*expected*/ false, 4, Pair.create(1, 4), Pair.create(3, 4)); - check(null, /*default*/ 5, Pair.create(4, null), Pair.create(1, 1), - /*expected*/ true, 5, Pair.create(4, 5), Pair.create(1, 1)); - check(null, /*default*/ 15, null, Pair.create(15, 15), - /*expected*/ true, 15, Pair.create(1, 15), Pair.create(14, 15)); - check(null, /*default*/ 16, null, Pair.create(16, 16), - /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16)); - check(null, /*default*/ 20, null, Pair.create(20, 20), - /*expected*/ false, 16, Pair.create(1, 16), Pair.create(15, 16)); - check(null, /*default*/ 20, null, Pair.create(16, 16), - /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16)); + check(null, /*default*/ 13, + /* min */ List.of(), + /* max */ List.of(), + /*expected*/ true, 13, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 0), + Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 13), Pair.create(WORK_TYPE_EJ, 13), + Pair.create(WORK_TYPE_BG, 13), Pair.create(WORK_TYPE_BGUSER, 13))); + check(null, /*default*/ 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)), + /*expected*/ true, 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1))); + check(null, /*default*/ 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5), + Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of( + Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 1)), + /*expected*/ true, 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5), + Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), + Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))); + check(null, /*default*/ 0, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 0)), + /*expected*/ false, 1, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1))); + check(null, /*default*/ -1, + /* min */ List.of(Pair.create(WORK_TYPE_BG, -1)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, -1)), + /*expected*/ false, 1, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1))); + check(null, /*default*/ 5, + /* min */ List.of( + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of( + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)), + /*expected*/ true, 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), + Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5))); + check(null, /*default*/ 6, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), + Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 2)), + /* max */ List.of( + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1)), + /*expected*/ false, 6, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1))); + check(null, /*default*/ 4, + /* min */ List.of( + Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 6)), + /* max */ List.of( + Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)), + /*expected*/ false, 4, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), + Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 4), + Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 4))); + check(null, /*default*/ 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)), + /*expected*/ true, 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1))); + check(null, /*default*/ 10, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3), + Pair.create(WORK_TYPE_BG, 1)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)), + /*expected*/ true, 10, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3), + Pair.create(WORK_TYPE_BG, 1)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1))); + check(null, /*default*/ 15, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 15)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 15)), + /*expected*/ true, 15, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 14)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 15), Pair.create(WORK_TYPE_BG, 15))); + check(null, /*default*/ 16, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 16)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 16)), + /*expected*/ true, 16, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16))); + check(null, /*default*/ 20, + /* min */ List.of( + Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 10)), + /* max */ List.of( + Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 20)), + /*expected*/ false, 16, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), + Pair.create(WORK_TYPE_BG, 15), Pair.create(WORK_TYPE_BGUSER, 0)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), + Pair.create(WORK_TYPE_BG, 16), Pair.create(WORK_TYPE_BGUSER, 16))); + check(null, /*default*/ 20, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 16)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 16)), + /*expected*/ true, 16, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16))); // Test for overriding with a setting string. check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) @@ -153,26 +228,66 @@ public class WorkTypeConfigTest { .setInt(KEY_MAX_BG, 4) .setInt(KEY_MIN_BG, 3) .build(), - /*default*/ 9, null, Pair.create(9, 9), - /*expected*/ true, 5, Pair.create(1, 5), Pair.create(3, 4)); + /*default*/ 9, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /* max */ List.of( + Pair.create(WORK_TYPE_BG, 9), Pair.create(WORK_TYPE_BGUSER, 2)), + /*expected*/ true, 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), + Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))); check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) .setInt(KEY_MAX_TOTAL, 5).build(), - /*default*/ 9, null, Pair.create(9, 9), - /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5)); + /*default*/ 9, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /*expected*/ true, 5, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 5))); + check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) .setInt(KEY_MAX_BG, 4).build(), - /*default*/ 9, null, Pair.create(9, 9), - /*expected*/ true, 9, Pair.create(1, 9), Pair.create(4, 4)); + /*default*/ 9, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /*expected*/ true, 9, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 4))); check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) .setInt(KEY_MIN_BG, 3).build(), - /*default*/ 9, null, Pair.create(9, 9), - /*expected*/ true, 9, Pair.create(1, 9), Pair.create(3, 9)); + /*default*/ 9, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /*expected*/ true, 9, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 9))); + check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) + .setInt(KEY_MAX_TOTAL, 20) + .setInt(KEY_MAX_EJ, 5) + .setInt(KEY_MIN_EJ, 2) + .setInt(KEY_MAX_BG, 16) + .setInt(KEY_MIN_BG, 8) + .build(), + /*default*/ 9, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /*expected*/ true, 16, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 2), + Pair.create(WORK_TYPE_BG, 8)), + /* max */ + List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_EJ, 5), + Pair.create(WORK_TYPE_BG, 16))); + check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER) .setInt(KEY_MAX_TOTAL, 20) .setInt(KEY_MAX_BG, 20) .setInt(KEY_MIN_BG, 8) .build(), - /*default*/ 9, null, Pair.create(9, 9), - /*expected*/ true, 16, Pair.create(1, 16), Pair.create(8, 16)); + /*default*/ 9, + /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)), + /*expected*/ true, 16, + /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 8)), + /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16))); } } diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index df19aeb13707..58ba90726b80 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -1829,11 +1829,11 @@ public class NetworkPolicyManagerServiceTest { } /** - * Exhaustively test isUidNetworkingBlocked to output the expected results based on external + * Exhaustively test checkUidNetworkingBlocked to output the expected results based on external * conditions. */ @Test - public void testIsUidNetworkingBlocked() { + public void testCheckUidNetworkingBlocked() { final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>(); // Metered network. Data saver on. @@ -1877,17 +1877,16 @@ public class NetworkPolicyManagerServiceTest { private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted, ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) { - final NetworkPolicyManagerInternal npmi = LocalServices - .getService(NetworkPolicyManagerInternal.class); for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) { final boolean expectedResult = pair.first; final int rule = pair.second; assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted), - expectedResult, - npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted)); + expectedResult, mService.checkUidNetworkingBlocked(UID_A, rule, + metered, backgroundRestricted)); assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted), - npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted)); + mService.checkUidNetworkingBlocked(SYSTEM_UID, rule, metered, + backgroundRestricted)); } } diff --git a/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java new file mode 100644 index 000000000000..764c504eeede --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static com.android.server.devicepolicy.DpmTestUtils.assertRestrictions; +import static com.android.server.devicepolicy.DpmTestUtils.newRestrictions; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Bundle; +import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.BundleUtils; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Build/Install/Run: + * atest com.android.server.pm.BundleUtilsTest + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BundleUtilsTest { + + @Test + public void testIsEmpty() { + assertThat(BundleUtils.isEmpty(null)).isTrue(); + assertThat(BundleUtils.isEmpty(new Bundle())).isTrue(); + assertThat(BundleUtils.isEmpty(newRestrictions("a"))).isFalse(); + } + + @Test + public void testClone() { + Bundle in = new Bundle(); + Bundle out = BundleUtils.clone(in); + assertThat(in).isNotSameInstanceAs(out); + assertRestrictions(out, new Bundle()); + + out = BundleUtils.clone(null); + assertThat(out).isNotNull(); + out.putBoolean("a", true); // Should not be Bundle.EMPTY. + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java index 9ba096766be2..7b9a00d582be 100644 --- a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java @@ -71,12 +71,12 @@ public class IncrementalStatesTest { @Before public void setUp() { mIncrementalStates = new IncrementalStates(); - assertFalse(mIncrementalStates.isStartable()); + assertFalse(mIncrementalStates.getIncrementalStatesInfo().isStartable()); mIncrementalStates.setCallback(mCallback); mIncrementalStates.onCommit(true); // Test that package is now startable and loading - assertTrue(mIncrementalStates.isStartable()); - assertTrue(mIncrementalStates.isLoading()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isLoading()); mUnstartableCalled.close(); mFullyLoadedCalled.close(); } @@ -90,7 +90,7 @@ public class IncrementalStatesTest { IStorageHealthListener.HEALTH_STATUS_UNHEALTHY); // Test that package is still startable assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get()); } @@ -104,7 +104,7 @@ public class IncrementalStatesTest { IStorageHealthListener.HEALTH_STATUS_READS_PENDING); // Test that package is still startable assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); } /** @@ -116,7 +116,7 @@ public class IncrementalStatesTest { IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE); // Test that package is still startable assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get()); } @@ -130,7 +130,7 @@ public class IncrementalStatesTest { IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT); // Test that package is still startable assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get()); } @@ -145,12 +145,12 @@ public class IncrementalStatesTest { mIncrementalStates.setProgress(1.0f); // Test that package is now fully loaded assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS)); - assertFalse(mIncrementalStates.isLoading()); + assertFalse(mIncrementalStates.getIncrementalStatesInfo().isLoading()); mIncrementalStates.onStorageHealthStatusChanged( IStorageHealthListener.HEALTH_STATUS_UNHEALTHY); // Test that package is still startable assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); } /** @@ -159,6 +159,6 @@ public class IncrementalStatesTest { @Test public void testStartableTransition_AppCrashOrAnr() { mIncrementalStates.onCrashOrAnr(); - assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java index e6c3d7c3fc5b..b21b04979424 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.array; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertContains; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertHaveIds; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertSuccess; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list; @@ -25,6 +26,8 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resultContains; import android.content.ComponentName; +import android.content.pm.LauncherApps; +import android.content.pm.ShortcutManager; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.Process; @@ -47,6 +50,9 @@ import java.util.concurrent.atomic.AtomicInteger; */ @SmallTest public class ShortcutManagerTest7 extends BaseShortcutManagerTest { + + private static final int CACHE_OWNER = LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS; + private List<String> callShellCommand(String... args) throws IOException, RemoteException { // For reset to work, the current time needs to be incrementing. @@ -215,11 +221,13 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest { // This command is deprecated. Will remove the test later. public void testLauncherCommands() throws Exception { + prepareGetRoleHoldersAsUser(getSystemLauncher().activityInfo.packageName, USER_0); prepareGetHomeActivitiesAsUser( /* preferred */ getSystemLauncher().activityInfo.getComponentName(), list(getSystemLauncher(), getFallbackLauncher()), USER_0); + prepareGetRoleHoldersAsUser(CALLING_PACKAGE_2, USER_10); prepareGetHomeActivitiesAsUser( /* preferred */ cn(CALLING_PACKAGE_2, "name"), list(getSystemLauncher(), getFallbackLauncher(), @@ -241,6 +249,7 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest { "Launcher: ComponentInfo{com.android.test.2/name}"); // Change user-0's launcher. + prepareGetRoleHoldersAsUser(CALLING_PACKAGE_1, USER_0); prepareGetHomeActivitiesAsUser( /* preferred */ cn(CALLING_PACKAGE_1, "name"), list(ri(CALLING_PACKAGE_1, "name", false, 0)), @@ -323,6 +332,72 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest { }); } + public void testGetShortcuts() throws Exception { + + mRunningUsers.put(USER_10, true); + + // Add two manifests and two dynamics. + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), + R.xml.shortcut_2); + updatePackageVersion(CALLING_PACKAGE_1, 1); + mService.mPackageMonitor.onReceive(getTestContext(), + genPackageAddIntent(CALLING_PACKAGE_1, USER_10)); + + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertTrue(mManager.addDynamicShortcuts(list( + makeLongLivedShortcut("s1"), makeShortcut("s2")))); + }); + runWithCaller(LAUNCHER_1, USER_10, () -> { + mInjectCheckAccessShortcutsPermission = true; + mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_10, + CACHE_OWNER); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "s2"), HANDLE_USER_10); + }); + + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1", "ms2", "s1", "s2") + .areAllEnabled() + + .selectPinned() + .haveIds("ms2", "s2"); + }); + + + mRunningUsers.put(USER_10, true); + mUnlockedUsers.put(USER_10, true); + + mInjectedCallingUid = Process.SHELL_UID; + + assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags", + Integer.toString(ShortcutManager.FLAG_MATCH_CACHED), CALLING_PACKAGE_1), + "s1"); + + assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags", + Integer.toString(ShortcutManager.FLAG_MATCH_DYNAMIC), CALLING_PACKAGE_1), + "s1", "s2"); + + assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags", + Integer.toString(ShortcutManager.FLAG_MATCH_MANIFEST), CALLING_PACKAGE_1), + "ms1", "ms2"); + + assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags", + Integer.toString(ShortcutManager.FLAG_MATCH_PINNED), CALLING_PACKAGE_1), + "ms2", "s2"); + + assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags", + Integer.toString(ShortcutManager.FLAG_MATCH_DYNAMIC + | ShortcutManager.FLAG_MATCH_PINNED), CALLING_PACKAGE_1), + "ms2", "s1", "s2"); + + assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags", + Integer.toString(ShortcutManager.FLAG_MATCH_MANIFEST + | ShortcutManager.FLAG_MATCH_CACHED), CALLING_PACKAGE_1), + "ms1", "ms2", "s1"); + + } + public void testDumpsysArgs() { checkDumpsysArgs(null, true, false, false); checkDumpsysArgs(array("-u"), true, true, false); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java index ee30f68de7a0..cd98d44075ca 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java @@ -72,11 +72,18 @@ public class UserManagerServiceUserTypeTest { @Test public void testUserTypeBuilder_createUserType() { final Bundle restrictions = makeRestrictionsBundle("r1", "r2"); + final Bundle systemSettings = makeSettingsBundle("s1", "s2"); + final Bundle secureSettings = makeSettingsBundle("secure_s1", "secure_s2"); + final List<DefaultCrossProfileIntentFilter> filters = List.of( + new DefaultCrossProfileIntentFilter.Builder( + DefaultCrossProfileIntentFilter.Direction.TO_PARENT, + /* flags= */0, + /* letsPersonalDataIntoProfile= */false).build()); final UserTypeDetails type = new UserTypeDetails.Builder() .setName("a.name") .setEnabled(true) .setMaxAllowed(21) - .setBaseType(FLAG_FULL) + .setBaseType(FLAG_PROFILE) .setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL) .setBadgeLabels(23, 24, 25) .setBadgeColors(26, 27) @@ -86,20 +93,45 @@ public class UserManagerServiceUserTypeTest { .setLabel(31) .setMaxAllowedPerParent(32) .setDefaultRestrictions(restrictions) + .setDefaultSystemSettings(systemSettings) + .setDefaultSecureSettings(secureSettings) + .setDefaultCrossProfileIntentFilters(filters) .createUserTypeDetails(); assertEquals("a.name", type.getName()); assertTrue(type.isEnabled()); assertEquals(21, type.getMaxAllowed()); - assertEquals(FLAG_FULL | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags()); + assertEquals(FLAG_PROFILE | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags()); assertEquals(28, type.getIconBadge()); assertEquals(29, type.getBadgePlain()); assertEquals(30, type.getBadgeNoBackground()); assertEquals(31, type.getLabel()); assertEquals(32, type.getMaxAllowedPerParent()); + assertTrue(UserRestrictionsUtils.areEqual(restrictions, type.getDefaultRestrictions())); assertNotSame(restrictions, type.getDefaultRestrictions()); + assertNotSame(systemSettings, type.getDefaultSystemSettings()); + assertEquals(systemSettings.size(), type.getDefaultSystemSettings().size()); + for (String key : systemSettings.keySet()) { + assertEquals( + systemSettings.getString(key), + type.getDefaultSystemSettings().getString(key)); + } + + assertNotSame(secureSettings, type.getDefaultSecureSettings()); + assertEquals(secureSettings.size(), type.getDefaultSecureSettings().size()); + for (String key : secureSettings.keySet()) { + assertEquals( + secureSettings.getString(key), + type.getDefaultSecureSettings().getString(key)); + } + + assertNotSame(filters, type.getDefaultCrossProfileIntentFilters()); + assertEquals(filters.size(), type.getDefaultCrossProfileIntentFilters().size()); + for (int i = 0; i < filters.size(); i++) { + assertEquals(filters.get(i), type.getDefaultCrossProfileIntentFilters().get(i)); + } assertEquals(23, type.getBadgeLabel(0)); assertEquals(24, type.getBadgeLabel(1)); @@ -135,6 +167,9 @@ public class UserManagerServiceUserTypeTest { assertEquals(Resources.ID_NULL, type.getBadgeColor(0)); assertEquals(Resources.ID_NULL, type.getLabel()); assertTrue(type.getDefaultRestrictions().isEmpty()); + assertTrue(type.getDefaultSystemSettings().isEmpty()); + assertTrue(type.getDefaultSecureSettings().isEmpty()); + assertTrue(type.getDefaultCrossProfileIntentFilters().isEmpty()); assertFalse(type.hasBadge()); } @@ -416,4 +451,13 @@ public class UserManagerServiceUserTypeTest { } return bundle; } + + /** Creates a Bundle of the given settings keys and puts true for the value. */ + private static Bundle makeSettingsBundle(String ... settings) { + final Bundle bundle = new Bundle(); + for (String setting : settings) { + bundle.putBoolean(setting, true); + } + return bundle; + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java index dc181a959d83..ddf0cd0e9235 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java @@ -48,23 +48,6 @@ public class UserRestrictionsUtilsTest extends AndroidTestCase { assertSame(in, UserRestrictionsUtils.nonNull(in)); } - public void testIsEmpty() { - assertTrue(UserRestrictionsUtils.isEmpty(null)); - assertTrue(UserRestrictionsUtils.isEmpty(new Bundle())); - assertFalse(UserRestrictionsUtils.isEmpty(newRestrictions("a"))); - } - - public void testClone() { - Bundle in = new Bundle(); - Bundle out = UserRestrictionsUtils.clone(in); - assertNotSame(in, out); - assertRestrictions(out, new Bundle()); - - out = UserRestrictionsUtils.clone(null); - assertNotNull(out); - out.putBoolean("a", true); // Should not be Bundle.EMPTY. - } - public void testMerge() { Bundle a = newRestrictions("a", "d"); Bundle b = newRestrictions("b", "d", "e"); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java index d876b05af3d5..7d208799ee88 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java @@ -229,9 +229,9 @@ public class VibrationThreadTest { .compose(); VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect); - Thread.sleep(20); + assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread, + TEST_TIMEOUT_MILLIS)); assertTrue(vibrationThread.isAlive()); - assertTrue(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating()); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. @@ -254,9 +254,9 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0); VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect); - Thread.sleep(20); + assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread, + TEST_TIMEOUT_MILLIS)); assertTrue(vibrationThread.isAlive()); - assertTrue(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating()); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. @@ -621,10 +621,8 @@ public class VibrationThreadTest { .combine(); VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect); - Thread.sleep(10); - assertTrue(vibrationThread.isAlive()); - assertTrue(vibrationThread.getVibrators().get(1).isVibrating()); - assertTrue(vibrationThread.getVibrators().get(2).isVibrating()); + assertTrue(waitUntil(t -> t.getVibrators().get(2).isVibrating(), vibrationThread, + TEST_TIMEOUT_MILLIS)); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java new file mode 100644 index 000000000000..f5d0ca7c5f9f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; + +import android.annotation.DurationMillisLong; +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorManager; +import android.hardware.input.InputSensorInfo; +import android.os.CancellationSignal; +import android.os.Handler; +import android.rotationresolver.RotationResolverInternal; +import android.view.Surface; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link com.android.server.wm.WindowOrientationListener} + */ +public class WindowOrientationListenerTest { + + @Mock + private Context mMockContext; + @Mock + private Handler mMockHandler; + @Mock + private InputSensorInfo mMockInputSensorInfo; + @Mock + private SensorManager mMockSensorManager; + @Mock + private WindowManagerService mMockWindowManagerService; + + private TestableRotationResolver mFakeRotationResolverInternal; + private com.android.server.wm.WindowOrientationListener mWindowOrientationListener; + private int mFinalizedRotation; + private boolean mRotationResolverEnabled; + private boolean mCanUseRotationResolver; + private SensorEvent mFakeSensorEvent; + private Sensor mFakeSensor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mRotationResolverEnabled = true; + mCanUseRotationResolver = true; + + mFakeRotationResolverInternal = new TestableRotationResolver(); + doReturn(mMockSensorManager).when(mMockContext).getSystemService(Context.SENSOR_SERVICE); + mWindowOrientationListener = new TestableWindowOrientationListener(mMockContext, + mMockHandler, mMockWindowManagerService); + mWindowOrientationListener.mRotationResolverService = mFakeRotationResolverInternal; + + mFakeSensor = new Sensor(mMockInputSensorInfo); + mFakeSensorEvent = new SensorEvent(mFakeSensor, /* accuracy */ 1, /* timestamp */ 1L, + new float[]{(float) Surface.ROTATION_90}); + } + + @Test + public void testOnSensorChanged_rotationResolverDisabled_useSensorResult() { + mRotationResolverEnabled = false; + + mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent); + + assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90); + } + + @Test + public void testOnSensorChanged_cannotUseRotationResolver_useSensorResult() { + mCanUseRotationResolver = false; + + mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent); + + assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90); + + } + + @Test + public void testOnSensorChanged_normalCase() { + mFakeRotationResolverInternal.mResult = Surface.ROTATION_180; + + mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent); + + assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_180); + } + + final class TestableRotationResolver extends RotationResolverInternal { + @Surface.Rotation + int mResult; + + @Override + public boolean isRotationResolverSupported() { + return true; + } + + @Override + public void resolveRotation(@NonNull RotationResolverCallbackInternal callback, + @Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation, + @DurationMillisLong long timeoutMillis, + @NonNull CancellationSignal cancellationSignal) { + callback.onSuccess(mResult); + } + } + + final class TestableWindowOrientationListener extends WindowOrientationListener { + + TestableWindowOrientationListener(Context context, Handler handler, + WindowManagerService service) { + super(context, handler, service); + this.mOrientationJudge = new OrientationSensorJudge(); + } + + @Override + public void onProposedRotationChanged(int rotation) { + mFinalizedRotation = rotation; + } + + @Override + public boolean canUseRotationResolver() { + return mCanUseRotationResolver; + } + + @Override + public boolean isRotationResolverEnabled() { + return mRotationResolverEnabled; + } + } +} diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp index c2cb688175b2..35ca3544d62e 100644 --- a/services/tests/shortcutmanagerutils/Android.bp +++ b/services/tests/shortcutmanagerutils/Android.bp @@ -22,5 +22,9 @@ java_library { "android.test.runner.stubs", ], + static_libs: [ + "compatibility-device-util-axt", + ], + sdk_version: "test_current", } diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java index 907f887ec228..5182b3b69655 100644 --- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java +++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java @@ -15,6 +15,8 @@ */ package com.android.server.pm.shortcutmanagertest; +import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; @@ -34,6 +36,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.Instrumentation; +import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; import android.content.LocusId; @@ -50,6 +53,7 @@ import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.UserHandle; +import android.os.UserManager; import android.test.MoreAsserts; import android.util.Log; @@ -136,6 +140,20 @@ public class ShortcutManagerTestUtils { return sb.toString(); } + public static List<String> extractShortcutIds(List<String> result) { + final String prefix = "ShortcutInfo {id="; + final String postfix = ", "; + + List<String> ids = new ArrayList<>(); + for (String line : result) { + if (line.contains(prefix)) { + ids.add(line.substring( + line.indexOf(prefix) + prefix.length(), line.indexOf(postfix))); + } + } + return ids; + } + public static boolean resultContains(List<String> result, String expected) { for (String line : result) { if (line.contains(expected)) { @@ -160,6 +178,16 @@ public class ShortcutManagerTestUtils { return result; } + public static List<String> assertHaveIds(List<String> result, String... expectedIds) { + assertSuccess(result); + + final SortedSet<String> expected = new TreeSet<>(list(expectedIds)); + final SortedSet<String> actual = new TreeSet<>(extractShortcutIds(result)); + assertEquals(expected, actual); + + return result; + } + public static List<String> runCommand(Instrumentation instrumentation, String command) { return runCommand(instrumentation, command, null); } @@ -193,30 +221,60 @@ public class ShortcutManagerTestUtils { return runShortcutCommand(instrumentation, command, result -> result.contains("Success")); } - public static String getDefaultLauncher(Instrumentation instrumentation) { - final String PREFIX = "Launcher: ComponentInfo{"; - final String POSTFIX = "}"; - final List<String> result = runShortcutCommandForSuccess( - instrumentation, "get-default-launcher --user " - + instrumentation.getContext().getUserId()); - for (String s : result) { - if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) { - return s.substring(PREFIX.length(), s.length() - POSTFIX.length()); + private static UserHandle getParentUser(Context context) { + final UserHandle user = context.getUser(); + final UserManager userManager = context.getSystemService(UserManager.class); + if (!userManager.isManagedProfile(user.getIdentifier())) { + return user; + } + + final List<UserHandle> profiles = userManager.getUserProfiles(); + for (UserHandle handle : profiles) { + if (!userManager.isManagedProfile(handle.getIdentifier())) { + return handle; } } - fail("Default launcher not found"); return null; } - public static void setDefaultLauncher(Instrumentation instrumentation, String component) { - runCommand(instrumentation, "cmd package set-home-activity --user " - + instrumentation.getContext().getUserId() + " " + component, - result -> result.contains("Success")); + public static String getDefaultLauncher(Instrumentation instrumentation) throws Exception { + final Context context = instrumentation.getContext(); + final RoleManager roleManager = context.getSystemService(RoleManager.class); + final UserHandle user = getParentUser(context); + List<String> roleHolders = callWithShellPermissionIdentity( + () -> roleManager.getRoleHoldersAsUser(RoleManager.ROLE_HOME, user)); + if (roleHolders.size() == 1) { + return roleHolders.get(0); + } + fail("Failed to get the default launcher for user " + context.getUserId()); + return null; + } + + public static void setDefaultLauncher(Instrumentation instrumentation, String packageName) { + runCommandForNoOutput(instrumentation, "cmd role add-role-holder --user " + + instrumentation.getContext().getUserId() + " " + RoleManager.ROLE_HOME + " " + + packageName + " 0"); + waitUntil("Failed to get shortcut access", + () -> hasShortcutAccess(instrumentation, packageName), 20); } public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) { - setDefaultLauncher(instrumentation, packageContext.getPackageName() - + "/android.content.pm.cts.shortcutmanager.packages.Launcher"); + setDefaultLauncher(instrumentation, packageContext.getPackageName()); + } + + public static boolean hasShortcutAccess(Instrumentation instrumentation, String packageName) { + final List<String> result = runShortcutCommandForSuccess(instrumentation, + "has-shortcut-access --user " + instrumentation.getContext().getUserId() + + " " + packageName); + for (String s : result) { + if (s.startsWith("true")) { + return true; + } else if (s.startsWith("false")) { + return false; + } + } + fail("Failed to check shortcut access"); + return false; } public static void overrideConfig(Instrumentation instrumentation, String config) { diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index e5646db7731f..1dd42127ec06 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -60,6 +60,6 @@ android_test { "libui", "libunwindstack", "libutils", - "netd_aidl_interface-cpp", + "netd_aidl_interface-V5-cpp", ], } diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index cf977b4a18db..ddf2844012e0 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -51,7 +51,7 @@ android_test { ], libs: [ - "android.hardware.power-java", + "android.hardware.power-V1-java", "android.test.mock", "android.test.base", "android.test.runner", diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 6b69ee921c8b..002859e3366b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -96,6 +96,8 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { .setTask(mTrampolineActivity.getTask()) .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TopActivity")) .build(); + // becomes invisible when covered by mTopActivity + mTrampolineActivity.mVisibleRequested = false; } @After @@ -230,7 +232,6 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { @Test public void testOnActivityLaunchCancelled_finishedBeforeDrawn() { - mTopActivity.mVisibleRequested = true; doReturn(true).when(mTopActivity).isReportedDrawn(); // Create an activity with different process that meets process switch. 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 42b080e69211..72b84396e985 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -679,8 +679,10 @@ public class ActivityRecordTests extends WindowTestsBase { new RemoteAnimationAdapter(new Stub() { @Override - public void onAnimationStart(RemoteAnimationTarget[] apps, + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) { } @@ -2178,6 +2180,7 @@ public class ActivityRecordTests extends WindowTestsBase { activity.setOccludesParent(true); activity.setVisible(false); + activity.mVisibleRequested = false; // Can not specify orientation if app isn't visible even though it occludes parent. assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation()); // Can specify orientation if the current orientation candidate is orientation behind. diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 22ee886c46fe..3a7954b3a5ce 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -1424,6 +1424,9 @@ public class ActivityStackTests extends WindowTestsBase { final ActivityRecord nonTopVisibleActivity = new ActivityBuilder(mAtm).setTask(task).build(); new ActivityBuilder(mAtm).setTask(task).build(); + // The scenario we are testing is when the app isn't visible yet. + nonTopVisibleActivity.setVisible(false); + nonTopVisibleActivity.mVisibleRequested = false; doReturn(false).when(nonTopVisibleActivity).attachedToProcess(); doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked(); doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(), diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 36adf28b276a..37bc23e6a17d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -844,6 +844,9 @@ public class ActivityStarterTests extends WindowTestsBase { final ActivityRecord singleTaskActivity = createSingleTaskActivityOn( secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); + // Activity should start invisible since we are bringing it to front. + singleTaskActivity.setVisible(false); + singleTaskActivity.mVisibleRequested = false; // Create another activity on top of the secondary display. final Task topStack = secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN, diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index 91b9449eddb0..71f19148d616 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -36,6 +36,7 @@ import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationTarget; +import android.view.WindowManager; import androidx.test.filters.SmallTest; @@ -70,8 +71,10 @@ public class AppChangeTransitionTests extends WindowTestsBase { class TestRemoteAnimationRunner implements IRemoteAnimationRunner { @Override - public void onAnimationStart(RemoteAnimationTarget[] apps, + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) { for (RemoteAnimationTarget target : apps) { assertNotNull(target.startBounds); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index f1e36098d84e..83aca5e2d482 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -265,8 +265,10 @@ public class AppTransitionTests extends WindowTestsBase { private class TestRemoteAnimationRunner implements IRemoteAnimationRunner { boolean mCancelled = false; @Override - public void onAnimationStart(RemoteAnimationTarget[] apps, + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index 2f4c8e256760..d13e4dcaf9fd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -15,7 +15,7 @@ */ package com.android.server.wm; -import static android.os.Process.INVALID_UID; + import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -587,7 +587,7 @@ public class DisplayAreaPolicyBuilderTest { final WindowToken token = new WindowToken(mWms, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */); policy.addWindow(token); @@ -621,11 +621,11 @@ public class DisplayAreaPolicyBuilderTest { final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */); final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class), TYPE_WALLPAPER, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */); policy.addWindow(token1); policy.addWindow(token2); @@ -672,15 +672,15 @@ public class DisplayAreaPolicyBuilderTest { options2.putInt("HIERARCHY_ROOT_ID", mGroupRoot2.mFeatureId); final WindowToken token0 = new WindowToken(mWms, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */); final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, false /* fromClientToken */, options1); final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, false /* fromClientToken */, options2); policy.addWindow(token0); 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 4bea9a2eea45..4c2d12436858 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1608,6 +1608,16 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testGetOrCreateRootHomeTask_dontMoveToTop() { + DisplayContent display = createNewDisplay(); + display.mDontMoveToTop = true; + TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); + + assertNull(taskDisplayArea.getRootHomeTask()); + assertNull(taskDisplayArea.getOrCreateRootHomeTask()); + } + + @Test public void testValidWindowingLayer() { final SurfaceControl windowingLayer = mDisplayContent.getWindowingLayer(); assertNotNull(windowingLayer); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java index 33e8fc0bd94b..3e05c86898e2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java @@ -217,6 +217,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); overrideSettings.mShouldShowSystemDecors = true; overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; + overrideSettings.mDontMoveToTop = true; provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings); assertTrue(mOverrideSettingsStorage.wasWriteSuccessful()); @@ -227,6 +228,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { getStoredDisplayAttributeValue(mOverrideSettingsStorage, "shouldShowSystemDecors")); assertEquals("Attribute value must be stored", "0", getStoredDisplayAttributeValue(mOverrideSettingsStorage, "imePolicy")); + assertEquals("Attribute value must be stored", "true", + getStoredDisplayAttributeValue(mOverrideSettingsStorage, "dontMoveToTop")); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java index fa3e3aefea3b..7714a6cead1d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java @@ -183,6 +183,10 @@ public class LetterboxTest { mColor = Color.GREEN; assertTrue(mLetterbox.needsApplySurfaceChanges()); + + mLetterbox.applySurfaceChanges(mTransaction); + + verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 1, 0}); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index 2efd4b53efcc..15e045c85c29 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -17,6 +17,9 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_OLD_NONE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -100,15 +103,18 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); - mController.goodToGo(); + mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> nonApsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); - verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(), + verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN), + appsCaptor.capture(), wallpapersCaptor.capture(), nonApsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, appsCaptor.getValue().length); final RemoteAnimationTarget app = appsCaptor.getValue()[0]; @@ -136,7 +142,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); - mController.goodToGo(); + mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); adapter.onAnimationCancelled(mMockLeash); verify(mMockRunner).onAnimationCancelled(); @@ -149,7 +155,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); - mController.goodToGo(); + mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mClock.fastForward(2500); mHandler.timeAdvance(); @@ -170,7 +176,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); - mController.goodToGo(); + mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mClock.fastForward(2500); mHandler.timeAdvance(); @@ -190,7 +196,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { @Test public void testZeroAnimations() { - mController.goodToGo(); + mController.goodToGo(TRANSIT_OLD_NONE); verifyNoMoreInteractionsExceptAsBinder(mMockRunner); } @@ -199,7 +205,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mController.createRemoteAnimationRecord(win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null); - mController.goodToGo(); + mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); verifyNoMoreInteractionsExceptAsBinder(mMockRunner); } @@ -213,15 +219,18 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); - mController.goodToGo(); + mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); - verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(), + verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN), + appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, appsCaptor.getValue().length); assertEquals(mMockLeash, appsCaptor.getValue()[0].leash); @@ -235,7 +244,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); win.mActivityRecord.removeImmediately(); - mController.goodToGo(); + mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); verifyNoMoreInteractionsExceptAsBinder(mMockRunner); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), eq(adapter)); @@ -255,15 +264,18 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { mFinishedCallback); ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback); - mController.goodToGo(); + mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); - verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(), + verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE), + appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, appsCaptor.getValue().length); final RemoteAnimationTarget app = appsCaptor.getValue()[0]; @@ -305,15 +317,18 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { mFinishedCallback); ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback); - mController.goodToGo(); + mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); - verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(), + verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE), + appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, appsCaptor.getValue().length); final RemoteAnimationTarget app = appsCaptor.getValue()[0]; @@ -354,15 +369,18 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); - mController.goodToGo(); + mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); - verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(), + verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN), + appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, wallpapersCaptor.getValue().length); } finally { @@ -383,15 +401,18 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, mFinishedCallback); - mController.goodToGo(); + mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN); mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> nonAPpsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); - verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(), + verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN), + appsCaptor.capture(), wallpapersCaptor.capture(), nonAPpsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, wallpapersCaptor.getValue().length); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 6f775cf301b5..cc4d4eaa9e8b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -39,8 +39,6 @@ import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; import static com.android.server.wm.Task.ActivityState.STOPPED; import static com.android.server.wm.WindowContainer.POSITION_TOP; -import static com.google.common.truth.Truth.assertThat; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -119,13 +117,13 @@ public class SizeCompatTests extends WindowTestsBase { @Test public void testKeepBoundsWhenChangingFromFreeformToFullscreen() { removeGlobalMinSizeRestriction(); - // Create landscape freeform display and a freeform app. + // create freeform display and a freeform app DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000) .setCanRotate(false) .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build(); setUpApp(display); - // Put app window into portrait freeform and then make it a compat app. + // Put app window into freeform and then make it a compat app. final Rect bounds = new Rect(100, 100, 400, 600); mTask.setBounds(bounds); prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); @@ -138,7 +136,7 @@ public class SizeCompatTests extends WindowTestsBase { final int density = mActivity.getConfiguration().densityDpi; - // Change display configuration to fullscreen. + // change display configuration to fullscreen Configuration c = new Configuration(display.getRequestedOverrideConfiguration()); c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN); display.onRequestedOverrideConfigurationChanged(c); @@ -148,8 +146,6 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(bounds.width(), mActivity.getBounds().width()); assertEquals(bounds.height(), mActivity.getBounds().height()); assertEquals(density, mActivity.getConfiguration().densityDpi); - // Size compat mode is sandboxed at the activity level. - assertActivityMaxBoundsSandboxedForSizeCompat(); } @Test @@ -175,12 +171,6 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */); // The decor height should be a part of the effective bounds. assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight); - // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio. - assertActivityMaxBoundsSandboxedForLetterbox(); - // Activity max bounds ignore notch, since an app can be shown past the notch (although app - // is currently limited by the notch). - assertThat(mActivity.getWindowConfiguration().getMaxBounds().height()) - .isEqualTo(displayBounds.height()); mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); assertFitted(); @@ -190,17 +180,9 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */); // The notch is no longer on top. assertEquals(appBounds, mActivity.getBounds()); - // Activity max bounds are sandboxed. - assertActivityMaxBoundsSandboxedForLetterbox(); mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); assertFitted(); - // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio. - assertActivityMaxBoundsSandboxedForLetterbox(); - // Activity max bounds ignore notch, since an app can be shown past the notch (although app - // is currently limited by the notch). - assertThat(mActivity.getWindowConfiguration().getMaxBounds().height()) - .isEqualTo(displayBounds.height()); } @Test @@ -228,9 +210,6 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(originalBounds.width(), mActivity.getBounds().width()); assertEquals(originalBounds.height(), mActivity.getBounds().height()); assertEquals(originalDpi, mActivity.getConfiguration().densityDpi); - // Activity is sandboxed; it is in size compat mode since it is not resizable and has a - // max aspect ratio. - assertActivityMaxBoundsSandboxedForSizeCompat(); assertScaled(); } @@ -238,13 +217,11 @@ public class SizeCompatTests extends WindowTestsBase { public void testFixedScreenBoundsWhenDisplaySizeChanged() { setUpDisplaySizeWithApp(1000, 2500); prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); - final DisplayContent display = mActivity.mDisplayContent; assertFitted(); - // Activity and task inherit bounds from TaskDisplayArea, since not sandboxed. - assertMaxBoundsInheritDisplayAreaBounds(); final Rect origBounds = new Rect(mActivity.getBounds()); final Rect currentBounds = mActivity.getWindowConfiguration().getBounds(); + final DisplayContent display = mActivity.mDisplayContent; // Change the size of current display. resizeDisplay(display, 1000, 2000); @@ -261,8 +238,6 @@ public class SizeCompatTests extends WindowTestsBase { // The position of configuration bounds should be the same as compat bounds. assertEquals(mActivity.getBounds().left, currentBounds.left); assertEquals(mActivity.getBounds().top, currentBounds.top); - // Activity is sandboxed to the offset size compat bounds. - assertActivityMaxBoundsSandboxedForSizeCompat(); // Change display size to a different orientation resizeDisplay(display, 2000, 1000); @@ -271,8 +246,6 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(origBounds.height(), currentBounds.height()); assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation); assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation); - // Activity is sandboxed to the offset size compat bounds. - assertActivityMaxBoundsSandboxedForSizeCompat(); // The previous resize operation doesn't consider the rotation change after size changed. // These setups apply the requested orientation to rotation as real case that the top fixed @@ -292,8 +265,6 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(origBounds.height(), currentBounds.height()); assertEquals(offsetX, currentBounds.left); assertScaled(); - // Activity is sandboxed due to size compat mode. - assertActivityMaxBoundsSandboxedForSizeCompat(); } @Test @@ -309,8 +280,6 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */); // The position should be horizontal centered. assertEquals((displayWidth - bounds.width()) / 2, bounds.left); - // Activity max bounds should be sandboxed since it is letterboxed. - assertActivityMaxBoundsSandboxedForLetterbox(); mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity)); // Make sure IME cannot attach to the app, otherwise IME window will also be shifted. @@ -322,8 +291,6 @@ public class SizeCompatTests extends WindowTestsBase { // It should keep non-attachable because the resolved bounds will be computed according to // the aspect ratio that won't match its parent bounds. assertFalse(mActivity.mDisplayContent.isImeAttachedToApp()); - // Activity max bounds should be sandboxed since it is letterboxed. - assertActivityMaxBoundsSandboxedForLetterbox(); } @Test @@ -349,13 +316,14 @@ public class SizeCompatTests extends WindowTestsBase { } @Test - public void testMoveToDifferentOrientationDisplay() { + public void testMoveToDifferentOrientDisplay() { setUpDisplaySizeWithApp(1000, 2500); prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); assertFitted(); - final Rect currentBounds = mActivity.getWindowConfiguration().getBounds(); - final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds()); + final Rect configBounds = mActivity.getWindowConfiguration().getBounds(); + final int origWidth = configBounds.width(); + final int origHeight = configBounds.height(); final int notchHeight = 100; final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000) @@ -364,44 +332,37 @@ public class SizeCompatTests extends WindowTestsBase { // Move the non-resizable activity to the new display. mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */); // The configuration bounds [820, 0 - 1820, 2500] should keep the same. - assertEquals(originalBounds.width(), currentBounds.width()); - assertEquals(originalBounds.height(), currentBounds.height()); + assertEquals(origWidth, configBounds.width()); + assertEquals(origHeight, configBounds.height()); assertScaled(); - // Activity max bounds are sandboxed due to size compat mode on the new display. - assertActivityMaxBoundsSandboxedForSizeCompat(); final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds(); // The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900). assertEquals(newDisplayBounds.height() - notchHeight, - (int) ((float) mActivity.getBounds().width() * originalBounds.height() - / originalBounds.width())); + (int) ((float) mActivity.getBounds().width() * origHeight / origWidth)); // Recompute the natural configuration in the new display. mActivity.clearSizeCompatMode(); mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); // Because the display cannot rotate, the portrait activity will fit the short side of // display with keeping portrait bounds [200, 0 - 700, 1000] in center. - assertEquals(newDisplayBounds.height(), currentBounds.height()); - assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(), - currentBounds.width()); + assertEquals(newDisplayBounds.height(), configBounds.height()); + assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(), + configBounds.width()); assertFitted(); // The appBounds should be [200, 100 - 700, 1000]. final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds(); - assertEquals(currentBounds.width(), appBounds.width()); - assertEquals(currentBounds.height() - notchHeight, appBounds.height()); - // Task max bounds are sandboxed due to letterboxing from orientation mismatch with display. - assertTaskMaxBoundsSandboxed(); + assertEquals(configBounds.width(), appBounds.width()); + assertEquals(configBounds.height() - notchHeight, appBounds.height()); } @Test - public void testFixedOrientationRotateCutoutDisplay() { + public void testFixedOrientRotateCutoutDisplay() { // Create a display with a notch/cutout final int notchHeight = 60; - final int width = 1000; - setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500) + setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500) .setNotch(notchHeight).build()); - // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460]. - final float maxAspect = 1.4f; + // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460]. prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); final Rect currentBounds = mActivity.getWindowConfiguration().getBounds(); @@ -409,11 +370,6 @@ public class SizeCompatTests extends WindowTestsBase { final Rect origBounds = new Rect(currentBounds); final Rect origAppBounds = new Rect(appBounds); - // Activity is sandboxed, and bounds include the area consumed by the notch. - assertActivityMaxBoundsSandboxedForLetterbox(); - assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height()) - .isEqualTo(Math.round(width * maxAspect) + notchHeight); - // Although the activity is fixed orientation, force rotate the display. rotateDisplay(mActivity.mDisplayContent, ROTATION_270); assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation()); @@ -429,13 +385,10 @@ public class SizeCompatTests extends WindowTestsBase { // The position in configuration should be global coordinates. assertEquals(mActivity.getBounds().left, currentBounds.left); assertEquals(mActivity.getBounds().top, currentBounds.top); - - // Activity max bounds are sandboxed due to size compat mode. - assertActivityMaxBoundsSandboxedForSizeCompat(); } @Test - public void testFixedAspectRatioOrientationChangeOrientation() { + public void testFixedAspOrientChangeOrient() { setUpDisplaySizeWithApp(1000, 2500); final float maxAspect = 1.4f; @@ -447,8 +400,6 @@ public class SizeCompatTests extends WindowTestsBase { final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds()); assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height()); - // Activity is sandboxed due to fixed aspect ratio. - assertActivityMaxBoundsSandboxedForLetterbox(); // Change the fixed orientation. mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); @@ -460,8 +411,6 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.getWindowConfiguration().getAppBounds().height()); assertEquals(originalAppBounds.height(), mActivity.getWindowConfiguration().getAppBounds().width()); - // Activity is sandboxed due to fixed aspect ratio. - assertActivityMaxBoundsSandboxedForLetterbox(); } @Test @@ -510,8 +459,6 @@ public class SizeCompatTests extends WindowTestsBase { // restarted and the override configuration won't be cleared. verify(mActivity, never()).restartProcessIfVisible(); assertScaled(); - // Activity max bounds are sandboxed due to size compat mode, even if is not visible. - assertActivityMaxBoundsSandboxedForSizeCompat(); // Change display density display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity); @@ -586,16 +533,12 @@ public class SizeCompatTests extends WindowTestsBase { // in multi-window mode. mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); assertFalse(activity.shouldUseSizeCompatMode()); - // Activity and task should not be sandboxed. - assertMaxBoundsInheritDisplayAreaBounds(); // The non-resizable activity should not be size compat because the display support // changing windowing mode from fullscreen to freeform. mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN); assertFalse(activity.shouldUseSizeCompatMode()); - // Activity and task should not be sandboxed. - assertMaxBoundsInheritDisplayAreaBounds(); } @Test @@ -659,9 +602,6 @@ public class SizeCompatTests extends WindowTestsBase { // be transparent. assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR)); - // Activity is sandboxed. - assertActivityMaxBoundsSandboxedForLetterbox(); - // Make the activity fill the display. prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE); w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN; @@ -671,7 +611,6 @@ public class SizeCompatTests extends WindowTestsBase { // The letterbox should only cover the notch area, so status bar can be transparent. assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets()); assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR)); - assertActivityMaxBoundsSandboxedForLetterbox(); } @Test @@ -696,8 +635,6 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(mTask.isTaskLetterboxed()); assertFalse(mActivity.inSizeCompatMode()); assertEquals(taskBounds, activityBounds); - // Activity inherits max bounds from task, since sandboxing applied to task. - assertTaskMaxBoundsSandboxed(); // Task bounds should be 700x1400 with the ratio as the display. assertEquals(displayBounds.height(), taskBounds.height()); @@ -728,8 +665,6 @@ public class SizeCompatTests extends WindowTestsBase { assertScaled(); assertEquals(activityBounds.width(), newActivityBounds.width()); assertEquals(activityBounds.height(), newActivityBounds.height()); - // Activity max bounds are sandboxed due to size compat mode. - assertActivityMaxBoundsSandboxedForSizeCompat(); } @Test @@ -741,30 +676,29 @@ public class SizeCompatTests extends WindowTestsBase { // Portrait fixed app without max aspect. prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); + Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); + Rect activityBounds = new Rect(mActivity.getBounds()); + // App should launch in fullscreen. assertFalse(mTask.isTaskLetterboxed()); assertFalse(mActivity.inSizeCompatMode()); - // Activity and task inherit max bounds from TaskDisplayArea. - assertMaxBoundsInheritDisplayAreaBounds(); + assertEquals(displayBounds, activityBounds); // Rotate display to landscape. rotateDisplay(mActivity.mDisplayContent, ROTATION_90); - final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds()); - final Rect rotatedActivityBounds = new Rect(mActivity.getBounds()); - assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height()); + displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); + activityBounds = new Rect(mActivity.getBounds()); + assertTrue(displayBounds.width() > displayBounds.height()); // App should be in size compat. assertFalse(mTask.isTaskLetterboxed()); assertScaled(); - assertThat(mActivity.inSizeCompatMode()).isTrue(); - // Activity max bounds are sandboxed due to size compat mode. - assertActivityMaxBoundsSandboxedForSizeCompat(); // App bounds should be 700x1400 with the ratio as the display. - assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height()); - assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height() - / rotatedDisplayBounds.width(), rotatedActivityBounds.width()); + assertEquals(displayBounds.height(), activityBounds.height()); + assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(), + activityBounds.width()); } @Test @@ -797,17 +731,14 @@ public class SizeCompatTests extends WindowTestsBase { final Rect displayBounds = new Rect(display.getBounds()); final Rect taskBounds = new Rect(mTask.getBounds()); final Rect newActivityBounds = new Rect(newActivity.getBounds()); - final float displayAspectRatio = (float) displayBounds.height() / displayBounds.width(); // Task and app bounds should be 700x1400 with the ratio as the display. assertTrue(mTask.isTaskLetterboxed()); assertFalse(newActivity.inSizeCompatMode()); assertEquals(taskBounds, newActivityBounds); assertEquals(displayBounds.height(), taskBounds.height()); - assertThat(taskBounds.width()) - .isEqualTo(Math.round(displayBounds.height() * displayAspectRatio)); - // Task max bounds are sandboxed due to letterbox, with the ratio of the display. - assertTaskMaxBoundsSandboxed(); + assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(), + taskBounds.width()); } @Test @@ -847,14 +778,6 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(displayBounds.height(), taskBounds.height()); assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio), taskBounds.width()); - // New activity max bounds are sandboxed due to letterbox. - assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds()) - .isEqualTo(taskBounds); - // Task max bounds are sandboxed due to letterbox, with the ratio of the display. - assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().height()) - .isEqualTo(displayBounds.height()); - assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().width()) - .isEqualTo(Math.round(displayBounds.height() / newActivity.info.maxAspectRatio)); // App bounds should be fullscreen in Task bounds. assertFalse(newActivity.inSizeCompatMode()); @@ -883,9 +806,6 @@ public class SizeCompatTests extends WindowTestsBase { assertFalse(mTask.isTaskLetterboxed()); assertScaled(); assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity); - assertThat(mActivity.inSizeCompatMode()).isTrue(); - // Activity max bounds are sandboxed due to size compat mode. - assertActivityMaxBoundsSandboxedForSizeCompat(); final Rect activityBounds = new Rect(mActivity.getBounds()); mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */); @@ -896,8 +816,6 @@ public class SizeCompatTests extends WindowTestsBase { assertScaled(); assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity); assertEquals(activityBounds, mActivity.getBounds()); - // Activity max bounds are sandboxed due to size compat. - assertActivityMaxBoundsSandboxedForSizeCompat(); } @Test @@ -913,7 +831,6 @@ public class SizeCompatTests extends WindowTestsBase { // In Task letterbox assertTrue(mTask.isTaskLetterboxed()); assertFalse(mActivity.inSizeCompatMode()); - assertTaskMaxBoundsSandboxed(); // Rotate display to portrait. rotateDisplay(display, ROTATION_90); @@ -921,7 +838,6 @@ public class SizeCompatTests extends WindowTestsBase { // App should be in size compat. assertFalse(mTask.isTaskLetterboxed()); assertScaled(); - assertActivityMaxBoundsSandboxedForSizeCompat(); // Rotate display to landscape. rotateDisplay(display, ROTATION_180); @@ -929,7 +845,6 @@ public class SizeCompatTests extends WindowTestsBase { // In Task letterbox assertTrue(mTask.isTaskLetterboxed()); assertFalse(mActivity.inSizeCompatMode()); - assertTaskMaxBoundsSandboxed(); } @Test @@ -947,26 +862,20 @@ public class SizeCompatTests extends WindowTestsBase { // In Task letterbox assertTrue(mTask.isTaskLetterboxed()); assertFalse(mActivity.inSizeCompatMode()); - // Task is letterboxed due to mismatched orientation request. - assertTaskMaxBoundsSandboxed(); - // Rotate display to landscape. + // Rotate display to portrait. rotateDisplay(display, ROTATION_90); // App should be in size compat. assertFalse(mTask.isTaskLetterboxed()); assertScaled(); - // Activity max bounds are sandboxed due to unresizable app. - assertActivityMaxBoundsSandboxedForSizeCompat(); - // Rotate display to portrait. + // Rotate display to landscape. rotateDisplay(display, ROTATION_180); // In Task letterbox assertTrue(mTask.isTaskLetterboxed()); assertFalse(mActivity.inSizeCompatMode()); - // Task is letterboxed, as in first case. - assertTaskMaxBoundsSandboxed(); } @Test @@ -983,18 +892,12 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation); assertEquals(2800, displayBounds.width()); assertEquals(1400, displayBounds.height()); - Rect displayAreaBounds = new Rect(0, 0, 2400, 1000); - taskDisplayArea.setBounds(displayAreaBounds); + taskDisplayArea.setBounds(0, 0, 2400, 1000); final Rect activityBounds = new Rect(mActivity.getBounds()); assertFalse(mActivity.inSizeCompatMode()); assertEquals(2400, activityBounds.width()); assertEquals(1000, activityBounds.height()); - // Task and activity maximum bounds inherit from TaskDisplayArea bounds. - assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds()) - .isEqualTo(displayAreaBounds); - assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds()) - .isEqualTo(displayAreaBounds); } @Test @@ -1142,48 +1045,6 @@ public class SizeCompatTests extends WindowTestsBase { assertFalse(mActivity.hasSizeCompatBounds()); } - /** Asserts both the activity and task max bounds inherit from the TaskDisplayArea. */ - private void assertMaxBoundsInheritDisplayAreaBounds() { - final Rect taskDisplayAreaBounds = mTask.getDisplayArea().getBounds(); - assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds()) - .isEqualTo(taskDisplayAreaBounds); - assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds()) - .isEqualTo(taskDisplayAreaBounds); - } - - /** - * Asserts task-level letterboxing, so both activity and task max bounds - * are sandboxed to the letterbox bounds. - */ - private void assertTaskMaxBoundsSandboxed() { - // Activity inherits max bounds from task, since sandboxing applied to task. - assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds()) - .isEqualTo(mTask.getBounds()); - // Task max bounds are sandboxed due to letterbox. - assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds()) - .isEqualTo(mTask.getBounds()); - } - - /** Asserts activity-level size compat mode, so only activity max bounds are sandboxed. */ - private void assertActivityMaxBoundsSandboxedForSizeCompat() { - // Activity max bounds are sandboxed due to size compat mode. - assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds()) - .isEqualTo(mActivity.getWindowConfiguration().getBounds()); - // Task inherits max bounds from display. - assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds()) - .isEqualTo(mTask.getDisplayContent().getBounds()); - } - - /** Asserts activity-level letterboxing, so only activity max bounds are sandboxed. */ - private void assertActivityMaxBoundsSandboxedForLetterbox() { - // Activity is sandboxed due to fixed aspect ratio. - assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds()) - .isEqualTo(mActivity.getBounds()); - // Task inherits bounds from display. - assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds()) - .isEqualTo(mTask.getDisplayContent().getBounds()); - } - static Configuration rotateDisplay(DisplayContent display, int rotation) { final Configuration c = new Configuration(); display.getDisplayRotation().setRotation(rotation); diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java index b8d44f605bca..6c722499da4b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java +++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java @@ -152,12 +152,6 @@ public class StubTransaction extends SurfaceControl.Transaction { } @Override - public SurfaceControl.Transaction reparentChildren(SurfaceControl sc, - SurfaceControl newParent) { - return this; - } - - @Override public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) { return this; } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index d83e9c21fae7..e22cda60d423 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -156,6 +156,9 @@ public class TaskDisplayAreaTests extends WindowTestsBase { spyOn(mDisplayContent); doReturn(true).when(mDisplayContent).isTrusted(); + // Allow child stack to move to top. + mDisplayContent.mDontMoveToTop = false; + // The display contains pinned stack that was added in {@link #setUp}. final Task stack = createTaskStackOnDisplay(mDisplayContent); final Task task = createTaskInStack(stack, 0 /* userId */); @@ -173,6 +176,65 @@ public class TaskDisplayAreaTests extends WindowTestsBase { } @Test + public void testMovingChildTaskOnTop() { + // Make sure the display is trusted display which capable to move the stack to top. + spyOn(mDisplayContent); + doReturn(true).when(mDisplayContent).isTrusted(); + + // Allow child stack to move to top. + mDisplayContent.mDontMoveToTop = false; + + // The display contains pinned stack that was added in {@link #setUp}. + Task stack = createTaskStackOnDisplay(mDisplayContent); + Task task = createTaskInStack(stack, 0 /* userId */); + + // Add another display at top. + mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(), + false /* includingParents */); + + // Ensure that original display ({@code mDisplayContent}) is not on top. + assertEquals("Testing DisplayContent should not be on the top", + mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent)); + + // Move the task of {@code mDisplayContent} to top. + stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); + + // Ensure that original display ({@code mDisplayContent}) is now on the top. + assertEquals("The testing DisplayContent should be moved to top with task", + mWm.mRoot.getChildCount() - 1, mWm.mRoot.mChildren.indexOf(mDisplayContent)); + } + + @Test + public void testDontMovingChildTaskOnTop() { + // Make sure the display is trusted display which capable to move the stack to top. + spyOn(mDisplayContent); + doReturn(true).when(mDisplayContent).isTrusted(); + + // Allow child stack to move to top. + mDisplayContent.mDontMoveToTop = true; + + // The display contains pinned stack that was added in {@link #setUp}. + Task stack = createTaskStackOnDisplay(mDisplayContent); + Task task = createTaskInStack(stack, 0 /* userId */); + + // Add another display at top. + mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(), + false /* includingParents */); + + // Ensure that original display ({@code mDisplayContent}) is not on top. + assertEquals("Testing DisplayContent should not be on the top", + mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent)); + + // Try moving the task of {@code mDisplayContent} to top. + stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); + + // Ensure that original display ({@code mDisplayContent}) hasn't moved and is not + // on the top. + assertEquals("The testing DisplayContent should not be moved to top with task", + mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent)); + } + + @Test public void testReuseTaskAsRootTask() { final Task candidateTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); @@ -233,7 +295,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { homeActivity.mVisibleRequested = true; assertFalse(rootHomeTask.isVisible()); - assertEquals(rootWindowContainer.getOrientation(), rootHomeTask.getOrientation()); + assertEquals(defaultTaskDisplayArea.getOrientation(), rootHomeTask.getOrientation()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index f20513dd369f..0eb8c8d2e58a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -1004,6 +1004,26 @@ public class TaskRecordTests extends WindowTestsBase { } @Test + public void testNotSaveLaunchingStateForNonLeafTask() { + LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; + spyOn(persister); + + final Task task = getTestTask(); + task.setHasBeenVisible(false); + task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); + task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + final Task leafTask = createTaskInStack(task, 0 /* userId */); + + leafTask.setHasBeenVisible(true); + task.setHasBeenVisible(true); + task.onConfigurationChanged(task.getParent().getConfiguration()); + + verify(persister, never()).saveTask(same(task), any()); + verify(persister).saveTask(same(leafTask), any()); + } + + @Test public void testNotSpecifyOrientationByFloatingTask() { final Task task = new TaskBuilder(mSupervisor) .setCreateActivity(true).setCreateParentTask(true).build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java index d71993df8602..ae85ceb72958 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -136,11 +136,6 @@ class TestDisplayContent extends DisplayContent { final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, mInfo, DEFAULT_DISPLAY_ADJUSTMENTS); final TestDisplayContent newDisplay = createInternal(display); - // Ensure letterbox aspect ratio is not overridden on any device target. - // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by - // the below method, is set on some device form factors. - mService.mWindowManager.setTaskLetterboxAspectRatio(0); - // disable the normal system decorations final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy(); spyOn(displayPolicy); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index df5b48a038f3..99c96bd0de1b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -66,6 +66,7 @@ import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceSession; +import android.view.WindowManager; import androidx.test.filters.SmallTest; @@ -905,8 +906,10 @@ public class WindowContainerTests extends WindowTestsBase { final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( new IRemoteAnimationRunner.Stub() { @Override - public void onAnimationStart(RemoteAnimationTarget[] apps, + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) { try { finishedCallback.onAnimationFinished(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index f8a89c6f89aa..6d0e510ba626 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -20,7 +20,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.os.Process.INVALID_UID; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; @@ -37,12 +36,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.content.pm.PackageManager; import android.os.IBinder; @@ -75,7 +72,7 @@ public class WindowManagerServiceTests extends WindowTestsBase { @Test public void testAddWindowToken() { IBinder token = mock(IBinder.class); - mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId()); + mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId(), null /* options */); WindowToken windowToken = mWm.mRoot.getWindowToken(token); assertFalse(windowToken.mRoundedCornerOverlay); @@ -83,32 +80,6 @@ public class WindowManagerServiceTests extends WindowTestsBase { } @Test - public void testAddWindowTokenWithOptions() { - IBinder token = mock(IBinder.class); - mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(), - null /* options */, null /* options */); - - WindowToken windowToken = mWm.mRoot.getWindowToken(token); - assertFalse(windowToken.mRoundedCornerOverlay); - assertTrue(windowToken.isFromClient()); - } - - @Test(expected = SecurityException.class) - public void testRemoveWindowToken_ownerUidNotMatch_throwException() { - IBinder token = mock(IBinder.class); - mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(), - null /* options */, null /* options */); - - spyOn(mWm); - when(mWm.checkCallingPermission(anyString(), anyString())).thenReturn(false); - WindowToken windowToken = mWm.mRoot.getWindowToken(token); - spyOn(windowToken); - when(windowToken.getOwnerUid()).thenReturn(INVALID_UID); - - mWm.removeWindowToken(token, mDisplayContent.getDisplayId()); - } - - @Test public void testTaskFocusChange_stackNotHomeType_focusChanges() throws RemoteException { DisplayContent display = createNewDisplay(); // Current focused window diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 3492d90cc0f3..eb6c6ed349de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -890,6 +890,7 @@ class WindowTestsBase extends SystemServiceTestsBase { mTask.moveToFront("createActivity"); } // Make visible by default... + activity.mVisibleRequested = true; activity.setVisible(true); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index 2d273312ab81..e2585e5ebfba 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; @@ -201,7 +200,7 @@ public class WindowTokenTests extends WindowTestsBase { token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */, - INVALID_UID, true /* roundedCornerOverlay */, true /* fromClientToken */); + true /* roundedCornerOverlay */, true /* fromClientToken */, null /* options */); assertTrue(token.mRoundedCornerOverlay); assertTrue(token.isFromClient()); } @@ -215,8 +214,8 @@ public class WindowTokenTests extends WindowTestsBase { public void testSurfaceCreatedForWindowToken() { final WindowToken fromClientToken = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_APPLICATION_OVERLAY, true /* persistOnEmpty */, - mDisplayContent, true /* ownerCanManageAppTokens */, INVALID_UID, - true /* roundedCornerOverlay */, true /* fromClientToken */); + mDisplayContent, true /* ownerCanManageAppTokens */, + true /* roundedCornerOverlay */, true /* fromClientToken */, null /* options */); assertNull(fromClientToken.mSurfaceControl); createWindow(null, TYPE_APPLICATION_OVERLAY, fromClientToken, "window"); @@ -224,8 +223,8 @@ public class WindowTokenTests extends WindowTestsBase { final WindowToken nonClientToken = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, - false /* fromClientToken */); + true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */, + false /* fromClientToken */, null /* options */); assertNotNull(nonClientToken.mSurfaceControl); } @@ -238,7 +237,7 @@ public class WindowTokenTests extends WindowTestsBase { final WindowToken token1 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */); verify(selectFunc).apply(token1.windowType, null); @@ -246,7 +245,7 @@ public class WindowTokenTests extends WindowTestsBase { final Bundle options = new Bundle(); final WindowToken token2 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */, false /* fromClientToken */, options /* options */); verify(selectFunc).apply(token2.windowType, options); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index 9e8f8eaa855c..04dea3f7a2c6 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -163,8 +163,13 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne mValid = true; mSessionComponentName = new ComponentName(service.getPackageName(), mInfo.getSessionService()); - // TODO : Need to get the hotword detection service from the xml metadata - mHotwordDetectionComponentName = null; + final String hotwordDetectionServiceName = mInfo.getHotwordDetectionService(); + if (hotwordDetectionServiceName != null) { + mHotwordDetectionComponentName = new ComponentName(service.getPackageName(), + hotwordDetectionServiceName); + } else { + mHotwordDetectionComponentName = null; + } mIWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService(Context.WINDOW_SERVICE)); IntentFilter filter = new IntentFilter(); @@ -388,7 +393,11 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne if (DEBUG) { Slog.d(TAG, "setHotwordDetectionConfigLocked"); } - + if (mHotwordDetectionComponentName == null) { + Slog.e(TAG, "Calling setHotwordDetectionConfigLocked, but hotword detection service" + + " name not found"); + return VoiceInteractionService.HOTWORD_CONFIG_FAILURE; + } if (!isIsolatedProcessLocked(mHotwordDetectionComponentName)) { return VoiceInteractionService.HOTWORD_CONFIG_FAILURE; } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java index cfdc5682a117..84f4f6a017d1 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java @@ -165,7 +165,8 @@ final class VoiceInteractionSessionConnection implements ServiceConnection, | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser)); if (mBound) { try { - mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY); + mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY, + null /* options */); } catch (RemoteException e) { Slog.w(TAG, "Failed adding window token", e); } diff --git a/telephony/java/android/telephony/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java index 0059ad6c2426..ae7d20929d58 100644 --- a/telephony/java/android/telephony/SignalThresholdInfo.java +++ b/telephony/java/android/telephony/SignalThresholdInfo.java @@ -402,29 +402,27 @@ public final class SignalThresholdInfo implements Parcelable { * @see #getThresholds() for more details on signal strength thresholds */ public @NonNull Builder setThresholds(@NonNull int[] thresholds) { - Objects.requireNonNull(thresholds, "thresholds must not be null"); - if (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED - || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED) { - throw new IllegalArgumentException( - "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED - + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED); - } - mThresholds = thresholds.clone(); - Arrays.sort(mThresholds); - return this; + return setThresholds(thresholds, false /*isSystem*/); } /** - * Set the signal strength thresholds for the corresponding signal measurement type without - * the length limitation. + * Set the signal strength thresholds for the corresponding signal measurement type. * * @param thresholds array of integer as the signal threshold values + * @param isSystem true is the caller is system which does not have restrictions on + * the length of thresholds array. * @return the builder to facilitate the chaining * * @hide */ - public @NonNull Builder setThresholdsUnlimited(@NonNull int[] thresholds) { + public @NonNull Builder setThresholds(@NonNull int[] thresholds, boolean isSystem) { Objects.requireNonNull(thresholds, "thresholds must not be null"); + if (!isSystem && (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED + || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED)) { + throw new IllegalArgumentException( + "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED + + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED); + } mThresholds = thresholds.clone(); Arrays.sort(mThresholds); return this; diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 2734ad1194d3..1473b7a8873d 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -47,6 +47,7 @@ import android.net.NetworkPolicyManager; import android.net.Uri; import android.os.Binder; import android.os.Build; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; @@ -150,6 +151,22 @@ public class SubscriptionManager { public static final String CACHE_KEY_SLOT_INDEX_PROPERTY = "cache_key.telephony.get_slot_index"; + /** @hide */ + public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings"; + + /** @hide */ + public static final String RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME = + "restoreSimSpecificSettings"; + + /** + * Key to the backup & restore data byte array in the Bundle that is returned by {@link + * #getAllSimSpecificSettingsForBackup()} or to be pass in to {@link + * #restoreAllSimSpecificSettings()}. + * + * @hide + */ + public static final String KEY_SIM_SPECIFIC_SETTINGS_DATA = "KEY_SIM_SPECIFIC_SETTINGS_DATA"; + private static final int MAX_CACHE_SIZE = 4; private static class VoidPropertyInvalidatedCache<T> @@ -372,6 +389,28 @@ public class SubscriptionManager { public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath( CONTENT_URI, "wfc_roaming_enabled"); + + /** + * A content {@link uri} used to call the appropriate backup or restore method for sim-specific + * settings + * <p> + * See {@link #GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} and {@link + * #RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for information on what method to call. + * @hide + */ + @NonNull + public static final Uri SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI = Uri.withAppendedPath( + CONTENT_URI, "backup_and_restore"); + + /** + * A content {@link uri} used to notify contentobservers listening to siminfo restore during + * SuW. + * @hide + */ + @NonNull + public static final Uri SIM_INFO_SUW_RESTORE_CONTENT_URI = Uri.withAppendedPath( + SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, "suw_restore"); + /** * A content {@link Uri} used to receive updates on cross sim enabled user setting. * <p> @@ -3455,4 +3494,71 @@ public class SubscriptionManager { sSlotIndexCache.clear(); sPhoneIdCache.clear(); } + + /** + * Called to retrieve SIM-specific settings data to be backed up. + * + * @return data in byte[] to be backed up. + * + * @hide + */ + @NonNull + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public byte[] getAllSimSpecificSettingsForBackup() { + Bundle bundle = mContext.getContentResolver().call( + SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, + GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME, null, null); + return bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA); + } + + /** + * Called to attempt to restore the backed up sim-specific configs to device for specific sim. + * This will try to restore the data that was stored internally when {@link + * #restoreAllSimSpecificSettingsFromBackup(byte[] data)} was called during setup wizard. + * End result is SimInfoDB is modified to match any backed up configs for the requested + * inserted sim. + * + * <p> + * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB + * entry is updated as the result of this method call. + * + * @param iccId of the sim that a restore is requested for. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void restoreSimSpecificSettingsForIccIdFromBackup(@NonNull String iccId) { + mContext.getContentResolver().call( + SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, + RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME, + iccId, null); + } + + /** + * Called during setup wizard restore flow to attempt to restore the backed up sim-specific + * configs to device for all existing SIMs in SimInfoDB. Internally, it will store the backup + * data in an internal file. This file will persist on device for device's lifetime and will be + * used later on when a SIM is inserted to restore that specific SIM's settings by calling + * {@link #restoreSimSpecificSettingsForIccIdFromBackup(String iccId)}. End result is + * SimInfoDB is modified to match any backed up configs for the appropriate inserted SIMs. + * + * <p> + * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB + * entry is updated as the result of this method call. + * + * @param data with the sim specific configs to be backed up. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) { + Bundle bundle = new Bundle(); + bundle.putByteArray(KEY_SIM_SPECIFIC_SETTINGS_DATA, data); + mContext.getContentResolver().call( + SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, + RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME, + null, bundle); + } } diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index f48ddb0aaca6..bd4bf0740ca1 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -136,7 +136,7 @@ public final class DataCallResponse implements Parcelable { private final @HandoverFailureMode int mHandoverFailureMode; private final int mPduSessionId; private final Qos mDefaultQos; - private final List<QosSession> mQosSessions; + private final List<QosBearerSession> mQosBearerSessions; private final SliceInfo mSliceInfo; /** @@ -187,7 +187,7 @@ public final class DataCallResponse implements Parcelable { mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY; mPduSessionId = PDU_SESSION_ID_NOT_SET; mDefaultQos = null; - mQosSessions = new ArrayList<>(); + mQosBearerSessions = new ArrayList<>(); mSliceInfo = null; } @@ -197,7 +197,7 @@ public final class DataCallResponse implements Parcelable { @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses, @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6, @HandoverFailureMode int handoverFailureMode, int pduSessionId, - @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions, + @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions, @Nullable SliceInfo sliceInfo) { mCause = cause; mSuggestedRetryTime = suggestedRetryTime; @@ -219,7 +219,7 @@ public final class DataCallResponse implements Parcelable { mHandoverFailureMode = handoverFailureMode; mPduSessionId = pduSessionId; mDefaultQos = defaultQos; - mQosSessions = qosSessions; + mQosBearerSessions = qosBearerSessions; mSliceInfo = sliceInfo; } @@ -246,8 +246,8 @@ public final class DataCallResponse implements Parcelable { mHandoverFailureMode = source.readInt(); mPduSessionId = source.readInt(); mDefaultQos = source.readParcelable(Qos.class.getClassLoader()); - mQosSessions = new ArrayList<>(); - source.readList(mQosSessions, QosSession.class.getClassLoader()); + mQosBearerSessions = new ArrayList<>(); + source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader()); mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader()); } @@ -394,8 +394,8 @@ public final class DataCallResponse implements Parcelable { * @hide */ @NonNull - public List<QosSession> getQosSessions() { - return mQosSessions; + public List<QosBearerSession> getQosBearerSessions() { + return mQosBearerSessions; } /** @@ -427,7 +427,7 @@ public final class DataCallResponse implements Parcelable { .append(" handoverFailureMode=").append(failureModeToString(mHandoverFailureMode)) .append(" pduSessionId=").append(getPduSessionId()) .append(" defaultQos=").append(mDefaultQos) - .append(" qosSessions=").append(mQosSessions) + .append(" qosBearerSessions=").append(mQosBearerSessions) .append(" sliceInfo=").append(mSliceInfo) .append("}"); return sb.toString(); @@ -447,10 +447,10 @@ public final class DataCallResponse implements Parcelable { mDefaultQos == other.mDefaultQos : mDefaultQos.equals(other.mDefaultQos); - final boolean isQosSessionsSame = (mQosSessions == null || mQosSessions == null) ? - mQosSessions == other.mQosSessions : - mQosSessions.size() == other.mQosSessions.size() - && mQosSessions.containsAll(other.mQosSessions); + final boolean isQosBearerSessionsSame = (mQosBearerSessions == null || mQosBearerSessions == null) ? + mQosBearerSessions == other.mQosBearerSessions : + mQosBearerSessions.size() == other.mQosBearerSessions.size() + && mQosBearerSessions.containsAll(other.mQosBearerSessions); return mCause == other.mCause && mSuggestedRetryTime == other.mSuggestedRetryTime @@ -472,7 +472,7 @@ public final class DataCallResponse implements Parcelable { && mHandoverFailureMode == other.mHandoverFailureMode && mPduSessionId == other.mPduSessionId && isQosSame - && isQosSessionsSame + && isQosBearerSessionsSame && Objects.equals(mSliceInfo, other.mSliceInfo); } @@ -481,7 +481,7 @@ public final class DataCallResponse implements Parcelable { return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos, - mQosSessions, mSliceInfo); + mQosBearerSessions, mSliceInfo); } @Override @@ -515,7 +515,7 @@ public final class DataCallResponse implements Parcelable { } else { dest.writeParcelable(null, flags); } - dest.writeList(mQosSessions); + dest.writeList(mQosBearerSessions); dest.writeParcelable(mSliceInfo, flags); } @@ -598,7 +598,7 @@ public final class DataCallResponse implements Parcelable { private Qos mDefaultQos; - private List<QosSession> mQosSessions = new ArrayList<>(); + private List<QosBearerSession> mQosBearerSessions = new ArrayList<>(); private SliceInfo mSliceInfo; @@ -812,15 +812,16 @@ public final class DataCallResponse implements Parcelable { /** * Set the dedicated bearer QOS sessions for this data connection. * - * @param qosSessions Dedicated bearer QOS (Quality Of Service) sessions received + * @param qosBearerSessions Dedicated bearer QOS (Quality Of Service) sessions received * from network. * * @return The same instance of the builder. * * @hide */ - public @NonNull Builder setQosSessions(@NonNull List<QosSession> qosSessions) { - mQosSessions = qosSessions; + public @NonNull Builder setQosBearerSessions( + @NonNull List<QosBearerSession> qosBearerSessions) { + mQosBearerSessions = qosBearerSessions; return this; } @@ -848,7 +849,7 @@ public final class DataCallResponse implements Parcelable { return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, - mDefaultQos, mQosSessions, mSliceInfo); + mDefaultQos, mQosBearerSessions, mSliceInfo); } } } diff --git a/telephony/java/android/telephony/data/EpsQos.java b/telephony/java/android/telephony/data/EpsQos.java index ad43068b2f11..22c8b0a0a74f 100644 --- a/telephony/java/android/telephony/data/EpsQos.java +++ b/telephony/java/android/telephony/data/EpsQos.java @@ -48,6 +48,10 @@ public final class EpsQos extends Qos implements Parcelable { qosClassId = source.readInt(); } + public int getQci() { + return qosClassId; + } + public static @NonNull EpsQos createFromParcelBody(@NonNull Parcel in) { return new EpsQos(in); } diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java index c8bb91e28bf2..c286c6217450 100644 --- a/telephony/java/android/telephony/data/Qos.java +++ b/telephony/java/android/telephony/data/Qos.java @@ -56,7 +56,15 @@ public abstract class Qos { this.uplink = new QosBandwidth(uplink.maxBitrateKbps, uplink.guaranteedBitrateKbps); } - static class QosBandwidth implements Parcelable { + public QosBandwidth getDownlinkBandwidth() { + return downlink; + } + + public QosBandwidth getUplinkBandwidth() { + return uplink; + } + + public static class QosBandwidth implements Parcelable { int maxBitrateKbps; int guaranteedBitrateKbps; @@ -73,6 +81,14 @@ public abstract class Qos { guaranteedBitrateKbps = source.readInt(); } + public int getMaxBitrateKbps() { + return maxBitrateKbps; + } + + public int getGuaranteedBitrateKbps() { + return guaranteedBitrateKbps; + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(maxBitrateKbps); diff --git a/telephony/java/android/telephony/data/QosFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java index 69277445634d..6c1c653f13d0 100644 --- a/telephony/java/android/telephony/data/QosFilter.java +++ b/telephony/java/android/telephony/data/QosBearerFilter.java @@ -38,7 +38,7 @@ import java.util.Objects; * * @hide */ -public final class QosFilter implements Parcelable { +public final class QosBearerFilter implements Parcelable { private List<LinkAddress> localAddresses; private List<LinkAddress> remoteAddresses; @@ -74,7 +74,7 @@ public final class QosFilter implements Parcelable { @IntDef(prefix = "QOS_FILTER_DIRECTION_", value = {QOS_FILTER_DIRECTION_DOWNLINK, QOS_FILTER_DIRECTION_UPLINK, QOS_FILTER_DIRECTION_BIDIRECTIONAL}) - public @interface QosFilterDirection {} + public @interface QosBearerFilterDirection {} public static final int QOS_FILTER_DIRECTION_DOWNLINK = android.hardware.radio.V1_6.QosFilterDirection.DOWNLINK; @@ -83,7 +83,7 @@ public final class QosFilter implements Parcelable { public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL = android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL; - @QosFilterDirection + @QosBearerFilterDirection private int filterDirection; /** @@ -92,7 +92,7 @@ public final class QosFilter implements Parcelable { */ private int precedence; - QosFilter() { + QosBearerFilter() { localAddresses = new ArrayList<>(); remoteAddresses = new ArrayList<>(); localPort = new PortRange(); @@ -101,7 +101,7 @@ public final class QosFilter implements Parcelable { filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL; } - public QosFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses, + public QosBearerFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses, PortRange localPort, PortRange remotePort, int protocol, int tos, long flowLabel, long spi, int direction, int precedence) { this.localAddresses = localAddresses; @@ -116,10 +116,30 @@ public final class QosFilter implements Parcelable { this.precedence = precedence; } + public List<LinkAddress> getLocalAddresses() { + return localAddresses; + } + + public List<LinkAddress> getRemoteAddresses() { + return remoteAddresses; + } + + public PortRange getLocalPortRange() { + return localPort; + } + + public PortRange getRemotePortRange() { + return remotePort; + } + + public int getPrecedence() { + return precedence; + } + /** @hide */ - public static @NonNull QosFilter create( + public static @NonNull QosBearerFilter create( @NonNull android.hardware.radio.V1_6.QosFilter qosFilter) { - QosFilter ret = new QosFilter(); + QosBearerFilter ret = new QosBearerFilter(); String[] localAddresses = qosFilter.localAddresses.stream().toArray(String[]::new); if (localAddresses != null) { @@ -202,6 +222,14 @@ public final class QosFilter implements Parcelable { this.end = end; } + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } + @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(start); @@ -254,7 +282,7 @@ public final class QosFilter implements Parcelable { @Override public String toString() { - return "QosFilter {" + return "QosBearerFilter {" + " localAddresses=" + localAddresses + " remoteAddresses=" + remoteAddresses + " localPort=" + localPort @@ -278,11 +306,11 @@ public final class QosFilter implements Parcelable { public boolean equals(Object o) { if (this == o) return true; - if (o == null || !(o instanceof QosFilter)) { + if (o == null || !(o instanceof QosBearerFilter)) { return false; } - QosFilter other = (QosFilter) o; + QosBearerFilter other = (QosBearerFilter) o; return localAddresses.size() == other.localAddresses.size() && localAddresses.containsAll(other.localAddresses) @@ -324,7 +352,7 @@ public final class QosFilter implements Parcelable { LinkAddress.LIFETIME_UNKNOWN, LinkAddress.LIFETIME_UNKNOWN); } - private QosFilter(Parcel source) { + private QosBearerFilter(Parcel source) { localAddresses = new ArrayList<>(); source.readList(localAddresses, LinkAddress.class.getClassLoader()); remoteAddresses = new ArrayList<>(); @@ -358,16 +386,16 @@ public final class QosFilter implements Parcelable { return 0; } - public static final @NonNull Parcelable.Creator<QosFilter> CREATOR = - new Parcelable.Creator<QosFilter>() { + public static final @NonNull Parcelable.Creator<QosBearerFilter> CREATOR = + new Parcelable.Creator<QosBearerFilter>() { @Override - public QosFilter createFromParcel(Parcel source) { - return new QosFilter(source); + public QosBearerFilter createFromParcel(Parcel source) { + return new QosBearerFilter(source); } @Override - public QosFilter[] newArray(int size) { - return new QosFilter[size]; + public QosBearerFilter[] newArray(int size) { + return new QosBearerFilter[size]; } }; } diff --git a/telephony/java/android/telephony/data/QosBearerSession.java b/telephony/java/android/telephony/data/QosBearerSession.java new file mode 100644 index 000000000000..30effc9273d7 --- /dev/null +++ b/telephony/java/android/telephony/data/QosBearerSession.java @@ -0,0 +1,137 @@ +/** + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + + +/** + * Class that stores information specific to QOS session. + * + * @hide + */ +public final class QosBearerSession implements Parcelable{ + + final int qosBearerSessionId; + final Qos qos; + final List<QosBearerFilter> qosBearerFilterList; + + public QosBearerSession(int qosBearerSessionId, @NonNull Qos qos, @NonNull List<QosBearerFilter> qosBearerFilterList) { + this.qosBearerSessionId = qosBearerSessionId; + this.qos = qos; + this.qosBearerFilterList = qosBearerFilterList; + } + + private QosBearerSession(Parcel source) { + qosBearerSessionId = source.readInt(); + qos = source.readParcelable(Qos.class.getClassLoader()); + qosBearerFilterList = new ArrayList<>(); + source.readList(qosBearerFilterList, QosBearerFilter.class.getClassLoader()); + } + + public int getQosBearerSessionId() { + return qosBearerSessionId; + } + + public Qos getQos() { + return qos; + } + + public List<QosBearerFilter> getQosBearerFilterList() { + return qosBearerFilterList; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(qosBearerSessionId); + if (qos.getType() == Qos.QOS_TYPE_EPS) { + dest.writeParcelable((EpsQos)qos, flags); + } else { + dest.writeParcelable((NrQos)qos, flags); + } + dest.writeList(qosBearerFilterList); + } + + public static @NonNull QosBearerSession create( + @NonNull android.hardware.radio.V1_6.QosSession qosSession) { + List<QosBearerFilter> qosBearerFilters = new ArrayList<>(); + + if (qosSession.qosFilters != null) { + for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) { + qosBearerFilters.add(QosBearerFilter.create(filter)); + } + } + + return new QosBearerSession( + qosSession.qosSessionId, + Qos.create(qosSession.qos), + qosBearerFilters); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "QosBearerSession {" + + " qosBearerSessionId=" + qosBearerSessionId + + " qos=" + qos + + " qosBearerFilterList=" + qosBearerFilterList + "}"; + } + + @Override + public int hashCode() { + return Objects.hash(qosBearerSessionId, qos, qosBearerFilterList); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof QosBearerSession)) { + return false; + } + + QosBearerSession other = (QosBearerSession) o; + return this.qosBearerSessionId == other.qosBearerSessionId + && this.qos.equals(other.qos) + && this.qosBearerFilterList.size() == other.qosBearerFilterList.size() + && this.qosBearerFilterList.containsAll(other.qosBearerFilterList); + } + + + public static final @NonNull Parcelable.Creator<QosBearerSession> CREATOR = + new Parcelable.Creator<QosBearerSession>() { + @Override + public QosBearerSession createFromParcel(Parcel source) { + return new QosBearerSession(source); + } + + @Override + public QosBearerSession[] newArray(int size) { + return new QosBearerSession[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/QosSession.java b/telephony/java/android/telephony/data/QosSession.java deleted file mode 100644 index f07b6a9f6725..000000000000 --- a/telephony/java/android/telephony/data/QosSession.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.telephony.data; - -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - - -/** - * Class that stores information specific to QOS session. - * - * @hide - */ -public final class QosSession implements Parcelable{ - - final int qosSessionId; - final Qos qos; - final List<QosFilter> qosFilterList; - - public QosSession(int qosSessionId, @NonNull Qos qos, @NonNull List<QosFilter> qosFilterList) { - this.qosSessionId = qosSessionId; - this.qos = qos; - this.qosFilterList = qosFilterList; - } - - private QosSession(Parcel source) { - qosSessionId = source.readInt(); - qos = source.readParcelable(Qos.class.getClassLoader()); - qosFilterList = new ArrayList<>(); - source.readList(qosFilterList, QosFilter.class.getClassLoader()); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(qosSessionId); - if (qos.getType() == Qos.QOS_TYPE_EPS) { - dest.writeParcelable((EpsQos)qos, flags); - } else { - dest.writeParcelable((NrQos)qos, flags); - } - dest.writeList(qosFilterList); - } - - public static @NonNull QosSession create( - @NonNull android.hardware.radio.V1_6.QosSession qosSession) { - List<QosFilter> qosFilters = new ArrayList<>(); - - if (qosSession.qosFilters != null) { - for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) { - qosFilters.add(QosFilter.create(filter)); - } - } - - return new QosSession( - qosSession.qosSessionId, - Qos.create(qosSession.qos), - qosFilters); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public String toString() { - return "QosSession {" - + " qosSessionId=" + qosSessionId - + " qos=" + qos - + " qosFilterList=" + qosFilterList + "}"; - } - - @Override - public int hashCode() { - return Objects.hash(qosSessionId, qos, qosFilterList); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || !(o instanceof QosSession)) { - return false; - } - - QosSession other = (QosSession) o; - return this.qosSessionId == other.qosSessionId - && this.qos.equals(other.qos) - && this.qosFilterList.size() == other.qosFilterList.size() - && this.qosFilterList.containsAll(other.qosFilterList); - } - - - public static final @NonNull Parcelable.Creator<QosSession> CREATOR = - new Parcelable.Creator<QosSession>() { - @Override - public QosSession createFromParcel(Parcel source) { - return new QosSession(source); - } - - @Override - public QosSession[] newArray(int size) { - return new QosSession[size]; - } - }; -} diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index f39e30b6d61f..814ce18c51e3 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -21,6 +21,7 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; +import android.annotation.SystemApi; import android.content.Context; import android.content.Intent; import android.os.Binder; @@ -39,6 +40,8 @@ import android.util.Log; import com.android.internal.telephony.IIntegerConsumer; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -77,31 +80,49 @@ public class ImsRcsManager { "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN"; /** - * Receives RCS Feature availability status updates from the ImsService. - * - * @see #isAvailable(int) - * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback) - * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback) + * An application can use {@link #addOnAvailabilityChangedListener} to register a + * {@link OnAvailabilityChangedListener}, which will notify the user when the RCS feature + * availability status updates from the ImsService. * @hide */ - public static class AvailabilityCallback { + @SystemApi + public interface OnAvailabilityChangedListener { + /** + * The availability of the feature's capabilities has changed to either available or + * unavailable. + * <p> + * If unavailable, the feature does not support the capability at the current time. This may + * be due to network or subscription provisioning changes, such as the IMS registration + * being lost, network type changing, or OMA-DM provisioning updates. + * + * @param capabilities The new availability of the capabilities. + */ + void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities); + } - private static class CapabilityBinder extends IImsCapabilityCallback.Stub { + /** + * Receive the availability status changed from the ImsService and pass the status change to + * the associated {@link OnAvailabilityChangedListener} + */ + private static class AvailabilityCallbackAdapter { - private final AvailabilityCallback mLocalCallback; - private Executor mExecutor; + private static class CapabilityBinder extends IImsCapabilityCallback.Stub { + private final OnAvailabilityChangedListener mOnAvailabilityChangedListener; + private final Executor mExecutor; - CapabilityBinder(AvailabilityCallback c) { - mLocalCallback = c; + CapabilityBinder(OnAvailabilityChangedListener listener, Executor executor) { + mExecutor = executor; + mOnAvailabilityChangedListener = listener; } @Override public void onCapabilitiesStatusChanged(int config) { - if (mLocalCallback == null) return; + if (mOnAvailabilityChangedListener == null) return; final long callingIdentity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged(config)); + mExecutor.execute(() -> + mOnAvailabilityChangedListener.onAvailabilityChanged(config)); } finally { restoreCallingIdentity(callingIdentity); } @@ -110,48 +131,34 @@ public class ImsRcsManager { @Override public void onQueryCapabilityConfiguration(int capability, int radioTech, boolean isEnabled) { - // This is not used for public interfaces. + // This is not used. } @Override public void onChangeCapabilityConfigurationError(int capability, int radioTech, @ImsFeature.ImsCapabilityError int reason) { - // This is not used for public interfaces - } - - private void setExecutor(Executor executor) { - mExecutor = executor; + // This is not used. } } - private final CapabilityBinder mBinder = new CapabilityBinder(this); + private final CapabilityBinder mBinder; - /** - * The availability of the feature's capabilities has changed to either available or - * unavailable. - * <p> - * If unavailable, the feature does not support the capability at the current time. This may - * be due to network or subscription provisioning changes, such as the IMS registration - * being lost, network type changing, or OMA-DM provisioning updates. - * - * @param capabilities The new availability of the capabilities. - */ - public void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) { + AvailabilityCallbackAdapter(@NonNull Executor executor, + @NonNull OnAvailabilityChangedListener listener) { + mBinder = new CapabilityBinder(listener, executor); } /**@hide*/ public final IImsCapabilityCallback getBinder() { return mBinder; } - - private void setExecutor(Executor executor) { - mBinder.setExecutor(executor); - } } private final int mSubId; private final Context mContext; private final BinderCacheManager<IImsRcsController> mBinderCache; + private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter> + mAvailabilityChangedCallbacks; /** * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class. @@ -162,6 +169,7 @@ public class ImsRcsManager { mSubId = subId; mContext = context; mBinderCache = binderCache; + mAvailabilityChangedCallbacks = new HashMap<>(); } /** @@ -174,10 +182,23 @@ public class ImsRcsManager { } /** - * @hide + * Registers a {@link RegistrationManager.RegistrationCallback} with the system. When the + * callback is registered, it will initiate the callback c to be called with the current + * registration state. + * + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor The executor the callback events should be run on. + * @param c The {@link RegistrationManager.RegistrationCallback} to be added. + * @see #unregisterImsRegistrationCallback(RegistrationManager.RegistrationCallback) + * @throws ImsException if the subscription associated with this callback is valid, but + * the {@link ImsService} associated with the subscription is not available. This can happen if + * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed + * reason. */ - // @Override add back to RegistrationManager interface once public. - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback( @NonNull @CallbackExecutor Executor executor, @NonNull RegistrationManager.RegistrationCallback c) @@ -191,7 +212,7 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Register registration callback: IImsRcsController is null"); + Log.w(TAG, "Register registration callback: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -207,10 +228,21 @@ public class ImsRcsManager { } /** - * @hide + * Removes an existing {@link RegistrationManager.RegistrationCallback}. + * + * When the subscription associated with this callback is removed (SIM removed, ESIM swap, + * etc...), this callback will automatically be removed. If this method is called for an + * inactive subscription, it will result in a no-op. + * + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param c The {@link RegistrationManager.RegistrationCallback} to be removed. + * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener + * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) */ - // @Override add back to RegistrationManager interface once public. - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback( @NonNull RegistrationManager.RegistrationCallback c) { if (c == null) { @@ -219,7 +251,7 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Unregister registration callback: IImsRcsController is null"); + Log.w(TAG, "Unregister registration callback: IImsRcsController is null"); throw new IllegalStateException("Cannot find remote IMS service"); } @@ -231,10 +263,21 @@ public class ImsRcsManager { } /** - * @hide + * Gets the registration state of the IMS service. + * + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor The {@link Executor} that will be used to call the IMS registration state + * callback. + * @param stateCallback A callback called on the supplied {@link Executor} that will contain the + * registration state of the IMS service, which will be one of the + * following: {@link RegistrationManager#REGISTRATION_STATE_NOT_REGISTERED}, + * {@link RegistrationManager#REGISTRATION_STATE_REGISTERING}, or + * {@link RegistrationManager#REGISTRATION_STATE_REGISTERED}. */ - // @Override add back to RegistrationManager interface once public. - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationState(@NonNull @CallbackExecutor Executor executor, @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) { if (stateCallback == null) { @@ -246,7 +289,7 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Get registration state error: IImsRcsController is null"); + Log.w(TAG, "Get registration state error: IImsRcsController is null"); throw new IllegalStateException("Cannot find remote IMS service"); } @@ -263,9 +306,20 @@ public class ImsRcsManager { } /** - * @hide + * Gets the Transport Type associated with the current IMS registration. + * + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor The {@link Executor} that will be used to call the transportTypeCallback. + * @param transportTypeCallback The transport type associated with the current IMS registration, + * which will be one of following: + * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN}, + * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or + * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}. */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor, @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback) { @@ -278,7 +332,7 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Get registration transport type error: IImsRcsController is null"); + Log.w(TAG, "Get registration transport type error: IImsRcsController is null"); throw new IllegalStateException("Cannot find remote IMS service"); } @@ -296,31 +350,33 @@ public class ImsRcsManager { } /** - * Registers an {@link AvailabilityCallback} with the system, which will provide RCS + * Add an {@link OnAvailabilityChangedListener} with the system, which will provide RCS * availability updates for the subscription specified. * * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to * subscription changed events and call - * {@link #unregisterRcsAvailabilityCallback(AvailabilityCallback)} to clean up after a - * subscription is removed. + * {@link #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)} to clean up + * after a subscription is removed. * <p> - * When the callback is registered, it will initiate the callback c to be called with the - * current capabilities. + * When the listener is registered, it will initiate the callback listener to be called with + * the current capabilities. * * @param executor The executor the callback events should be run on. - * @param c The RCS {@link AvailabilityCallback} to be registered. - * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback) + * @param listener The RCS {@link OnAvailabilityChangedListener} to be registered. + * @see #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener) * @throws ImsException if the subscription associated with this instance of * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void registerRcsAvailabilityCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull AvailabilityCallback c) throws ImsException { - if (c == null) { - throw new IllegalArgumentException("Must include a non-null AvailabilityCallback."); + public void addOnAvailabilityChangedListener(@NonNull @CallbackExecutor Executor executor, + @NonNull OnAvailabilityChangedListener listener) throws ImsException { + if (listener == null) { + throw new IllegalArgumentException("Must include a non-null" + + "OnAvailabilityChangedListener."); } if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); @@ -328,56 +384,61 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Register availability callback: IImsRcsController is null"); + Log.w(TAG, "Add availability changed listener: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } - c.setExecutor(executor); + AvailabilityCallbackAdapter adapter = + addAvailabilityChangedListenerToCollection(executor, listener); try { - imsRcsController.registerRcsAvailabilityCallback(mSubId, c.getBinder()); - + imsRcsController.registerRcsAvailabilityCallback(mSubId, adapter.getBinder()); } catch (ServiceSpecificException e) { throw new ImsException(e.toString(), e.errorCode); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e); + Log.w(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } - /** - * Removes an existing RCS {@link AvailabilityCallback}. + /** + * Removes an existing RCS {@link OnAvailabilityChangedListener}. * <p> * When the subscription associated with this callback is removed (SIM removed, ESIM swap, * etc...), this callback will automatically be unregistered. If this method is called for an * inactive subscription, it will result in a no-op. - * @param c The RCS {@link AvailabilityCallback} to be removed. - * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback) + * @param listener The RCS {@link OnAvailabilityChangedListener} to be removed. + * @see #addOnAvailabilityChangedListener(Executor, OnAvailabilityChangedListener) * @throws ImsException if the IMS service is not available when calling this method. * See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c) - throws ImsException { - if (c == null) { - throw new IllegalArgumentException("Must include a non-null AvailabilityCallback."); + public void removeOnAvailabilityChangedListener( + @NonNull OnAvailabilityChangedListener listener) { + if (listener == null) { + throw new IllegalArgumentException("Must include a non-null" + + "OnAvailabilityChangedListener."); } IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Unregister availability callback: IImsRcsController is null"); - throw new ImsException("Cannot find remote IMS service", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + Log.w(TAG, "Remove availability changed listener: IImsRcsController is null"); + return; + } + + AvailabilityCallbackAdapter callback = + removeAvailabilityChangedListenerFromCollection(listener); + if (callback == null) { + return; } try { - imsRcsController.unregisterRcsAvailabilityCallback(mSubId, c.getBinder()); + imsRcsController.unregisterRcsAvailabilityCallback(mSubId, callback.getBinder()); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e); - throw new ImsException("Remote IMS Service is not available", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + Log.w(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e); } } @@ -388,26 +449,24 @@ public class ImsRcsManager { * RCS capabilities provided over-the-top by applications. * * @param capability The RCS capability to query. - * @param radioTech The radio tech that this capability failed for, defined as - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} or - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}. + * @param radioTech The radio technology type that we are querying. * @return true if the RCS capability is capable for this subscription, false otherwise. This * does not necessarily mean that we are registered for IMS and the capability is available, but * rather the subscription is capable of this service over IMS. - * @see #isAvailable(int) + * @see #isAvailable(int, int) * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL * @throws ImsException if the IMS service is not available when calling this method. * See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "isCapable: IImsRcsController is null"); + Log.w(TAG, "isCapable: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -415,7 +474,7 @@ public class ImsRcsManager { try { return imsRcsController.isCapable(mSubId, capability, radioTech); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#isCapable", e); + Log.w(TAG, "Error calling IImsRcsController#isCapable", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -428,6 +487,7 @@ public class ImsRcsManager { * RCS capabilities provided by over-the-top by applications. * * @param capability the RCS capability to query. + * @param radioTech The radio technology type that we are querying. * @return true if the RCS capability is currently available for the associated subscription, * false otherwise. If the capability is available, IMS is registered and the service is * currently available over IMS. @@ -436,25 +496,57 @@ public class ImsRcsManager { * See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability) + public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "isAvailable: IImsRcsController is null"); + Log.w(TAG, "isAvailable: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { - return imsRcsController.isAvailable(mSubId, capability); + return imsRcsController.isAvailable(mSubId, capability, radioTech); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#isAvailable", e); + Log.w(TAG, "Error calling IImsRcsController#isAvailable", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } + /** + * Add the {@link OnAvailabilityChangedListener} to collection for tracking. + * @param executor The executor that will be used when the publish state is changed and the + * {@link OnAvailabilityChangedListener} is called. + * @param listener The {@link OnAvailabilityChangedListener} to call the publish state changed. + * @return The {@link AvailabilityCallbackAdapter} to wrapper the + * {@link OnAvailabilityChangedListener} + */ + private AvailabilityCallbackAdapter addAvailabilityChangedListenerToCollection( + @NonNull Executor executor, @NonNull OnAvailabilityChangedListener listener) { + AvailabilityCallbackAdapter adapter = new AvailabilityCallbackAdapter(executor, listener); + synchronized (mAvailabilityChangedCallbacks) { + mAvailabilityChangedCallbacks.put(listener, adapter); + } + return adapter; + } + + /** + * Remove the existing {@link OnAvailabilityChangedListener} from the collection. + * @param listener The {@link OnAvailabilityChangedListener} to remove from the collection. + * @return The wrapper class {@link AvailabilityCallbackAdapter} associated with the + * {@link OnAvailabilityChangedListener}. + */ + private AvailabilityCallbackAdapter removeAvailabilityChangedListenerFromCollection( + @NonNull OnAvailabilityChangedListener listener) { + synchronized (mAvailabilityChangedCallbacks) { + return mAvailabilityChangedCallbacks.remove(listener); + } + } + private IImsRcsController getIImsRcsController() { IBinder binder = TelephonyFrameworkInitializer .getTelephonyServiceManager() diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl index 7a6c28bddd09..8931a78709ed 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl @@ -47,7 +47,7 @@ interface IImsRcsController { void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback c); void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback c); boolean isCapable(int subId, int capability, int radioTech); - boolean isAvailable(int subId, int capability); + boolean isAvailable(int subId, int capability, int radioTech); // ImsUceAdapter specific void requestCapabilities(int subId, String callingPackage, String callingFeatureId, diff --git a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java index c5b1c90ced12..f3791d1c6f96 100644 --- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java +++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java @@ -36,12 +36,9 @@ import java.util.Set; public final class CapabilityChangeRequest implements Parcelable { /** - * Contains a feature capability, defined as - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}, - * along with an associated technology, defined as + * Contains a MMTEL feature capability {@link MmTelFeature.MmTelCapabilities} and RCS feature + * capability {@link RcsFeature.RcsImsCapabilities}, along with an associated technology, + * defined as * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} @@ -50,7 +47,7 @@ public final class CapabilityChangeRequest implements Parcelable { private final int mCapability; private final int radioTech; - public CapabilityPair(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, + public CapabilityPair(int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { this.mCapability = capability; this.radioTech = radioTech; @@ -81,13 +78,10 @@ public final class CapabilityChangeRequest implements Parcelable { } /** - * @return The stored capability, defined as - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS} + * @return The stored capability, defined as {@link MmTelFeature.MmTelCapabilities} and + * {@link RcsFeature.RcsImsCapabilities} */ - public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() { + public int getCapability() { return mCapability; } @@ -125,12 +119,11 @@ public final class CapabilityChangeRequest implements Parcelable { * Add one or many capabilities to the request to be enabled. * * @param capabilities A bitfield of capabilities to enable, valid values are defined in - * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. + * {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}. * @param radioTech the radio tech that these capabilities should be enabled for, valid * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}. */ - public void addCapabilitiesToEnableForTech( - @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities, + public void addCapabilitiesToEnableForTech(int capabilities, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech); } @@ -138,12 +131,11 @@ public final class CapabilityChangeRequest implements Parcelable { /** * Add one or many capabilities to the request to be disabled. * @param capabilities A bitfield of capabilities to diable, valid values are defined in - * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. + * {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}. * @param radioTech the radio tech that these capabilities should be disabled for, valid * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}. */ - public void addCapabilitiesToDisableForTech( - @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities, + public void addCapabilitiesToDisableForTech(int capabilities, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech); } diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index 22df921c4214..85703f8de5e5 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -194,7 +194,6 @@ public class RcsFeature extends ImsFeature { * of the capability and notify the capability status as true using * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the * framework that the capability is available for usage. - * @hide */ public static class RcsImsCapabilities extends Capabilities { /** @hide*/ @@ -226,12 +225,21 @@ public class RcsFeature extends ImsFeature { */ public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; + /** + * Create a new {@link RcsImsCapabilities} instance with the provided capabilities. + * @param capabilities The capabilities that are supported for RCS in the form of a + * bitfield. + */ public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) { super(capabilities); } - private RcsImsCapabilities(Capabilities c) { - super(c.getMask()); + /** + * Create a new {@link RcsImsCapabilities} instance with the provided capabilities. + * @param capabilities The capabilities instance that are supported for RCS + */ + private RcsImsCapabilities(Capabilities capabilities) { + super(capabilities.getMask()); } @Override @@ -307,7 +315,7 @@ public class RcsFeature extends ImsFeature { * set, the {@link RcsFeature} has brought up the capability and is ready for framework * requests. To change the status of the capabilities * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called. - * @hide + * @return A copy of the current RcsFeature capability status. */ @Override public @NonNull final RcsImsCapabilities queryCapabilityStatus() { @@ -318,13 +326,13 @@ public class RcsFeature extends ImsFeature { * Notify the framework that the capabilities status has changed. If a capability is enabled, * this signals to the framework that the capability has been initialized and is ready. * Call {@link #queryCapabilityStatus()} to return the current capability status. - * @hide + * @param capabilities The current capability status of the RcsFeature. */ - public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) { - if (c == null) { + public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) { + if (capabilities == null) { throw new IllegalArgumentException("RcsImsCapabilities must be non-null!"); } - super.notifyCapabilitiesStatusChanged(c); + super.notifyCapabilitiesStatusChanged(capabilities); } /** @@ -333,7 +341,9 @@ public class RcsFeature extends ImsFeature { * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to * enable or disable capability A, this method should return the correct configuration for * capability A afterwards (until it has changed). - * @hide + * @param capability The capability that we are querying the configuration for. + * @param radioTech The radio technology type that we are querying. + * @return true if the capability is enabled, false otherwise. */ public boolean queryCapabilityConfiguration( @RcsUceAdapter.RcsImsCapabilityFlag int capability, @@ -355,11 +365,12 @@ public class RcsFeature extends ImsFeature { * If for some reason one or more of these capabilities can not be enabled/disabled, * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should * be called for each capability change that resulted in an error. - * @hide + * @param request The request to change the capability. + * @param callback To notify the framework that the result of the capability changes. */ @Override public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request, - @NonNull CapabilityCallbackProxy c) { + @NonNull CapabilityCallbackProxy callback) { // Base Implementation - Override to provide functionality } diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml index e68fbd8724de..b2719fbcac82 100644 --- a/tests/FlickerTests/AndroidTest.xml +++ b/tests/FlickerTests/AndroidTest.xml @@ -25,7 +25,6 @@ </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest"> <option name="package" value="com.android.server.wm.flicker"/> - <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" /> <option name="shell-timeout" value="6600s" /> <option name="test-timeout" value="6600s" /> <option name="hidden-api-checks" value="false" /> diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 1a940c75cfa4..c6c67feeed72 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -753,6 +753,15 @@ </intent-filter> </activity> + <activity android:name="RenderEffectShaderActivity" + android:label="RenderEffect/Shader" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="com.android.test.hwui.TEST"/> + </intent-filter> + </activity> + <activity android:name="TextActivity" android:label="Text/Simple Text" android:theme="@android:style/Theme.NoTitleBar" diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java new file mode 100644 index 000000000000..661d48a84768 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java @@ -0,0 +1,107 @@ +/* + * 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.test.hwui; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.RenderEffect; +import android.graphics.RenderNode; +import android.graphics.Shader; +import android.os.Bundle; +import android.view.Gravity; +import android.view.View; +import android.widget.LinearLayout; + +@SuppressWarnings({"UnusedDeclaration"}) +public class RenderEffectShaderActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setClipChildren(false); + layout.setGravity(Gravity.CENTER); + layout.setOrientation(LinearLayout.VERTICAL); + + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(500, 500); + params.bottomMargin = 100; + + layout.addView(new ShaderRenderEffectView(this), params); + + setContentView(layout); + } + + public static class ShaderRenderEffectView extends View { + + private final Paint mPaint; + private final RenderNode mRenderNode; + + public ShaderRenderEffectView(Context c) { + super(c); + + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mRenderNode = new RenderNode("blurNode"); + + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (changed) { + LinearGradient gradient = new LinearGradient( + 0f, 0f, + 0f, bottom - top, + new int[]{Color.CYAN, Color.MAGENTA}, + null, + Shader.TileMode.CLAMP + ); + mRenderNode.setRenderEffect( + RenderEffect.createShaderEffect(gradient) + ); + + int width = right - left; + int height = bottom - top; + mRenderNode.setPosition(0, 0, width, height); + Canvas canvas = mRenderNode.beginRecording(width, height); + mPaint.setColor(Color.BLUE); + + canvas.drawRect( + 0, + 0, + width, + height, + mPaint + ); + + mPaint.setColor(Color.RED); + canvas.drawCircle((right - left) / 2f, (bottom - top) / 2f, 50f, mPaint); + + mRenderNode.endRecording(); + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.drawRenderNode(mRenderNode); + } + } +} diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java index cade5ba3771f..d232a507454d 100644 --- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java +++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,22 +20,20 @@ import static com.android.testutils.MiscAsserts.assertThrows; import static com.android.testutils.ParcelUtils.assertParcelSane; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.os.Build; -import android.util.SparseArray; import androidx.test.filters.SmallTest; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; import com.android.testutils.DevSdkIgnoreRunner; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.ArrayList; -import java.util.List; +import java.util.Map; @IgnoreUpTo(Build.VERSION_CODES.R) @RunWith(DevSdkIgnoreRunner.class) @@ -45,51 +43,51 @@ public class OemNetworkPreferencesTest { private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_DEFAULT; private static final String TEST_PACKAGE = "com.google.apps.contacts"; - private final List<String> mPackages = new ArrayList<>(); private final OemNetworkPreferences.Builder mBuilder = new OemNetworkPreferences.Builder(); - @Before - public void beforeEachTestMethod() { - mPackages.add(TEST_PACKAGE); + @Test + public void testBuilderAddNetworkPreferenceRequiresNonNullPackageName() { + assertThrows(NullPointerException.class, + () -> mBuilder.addNetworkPreference(null, TEST_PREF)); } @Test - public void builderAddNetworkPreferenceRequiresNonNullPackages() { + public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() { assertThrows(NullPointerException.class, - () -> mBuilder.addNetworkPreference(TEST_PREF, null)); + () -> mBuilder.removeNetworkPreference(null)); } @Test - public void getNetworkPreferencesReturnsCorrectValue() { + public void testGetNetworkPreferenceReturnsCorrectValue() { final int expectedNumberOfMappings = 1; - mBuilder.addNetworkPreference(TEST_PREF, mPackages); + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); - final SparseArray<List<String>> networkPreferences = + final Map<String, Integer> networkPreferences = mBuilder.build().getNetworkPreferences(); assertEquals(expectedNumberOfMappings, networkPreferences.size()); - assertEquals(mPackages.size(), networkPreferences.get(TEST_PREF).size()); - assertEquals(mPackages.get(0), networkPreferences.get(TEST_PREF).get(0)); + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); } @Test - public void getNetworkPreferencesReturnsUnmodifiableValue() { + public void testGetNetworkPreferenceReturnsUnmodifiableValue() { final String newPackage = "new.com.google.apps.contacts"; - mBuilder.addNetworkPreference(TEST_PREF, mPackages); + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); - final SparseArray<List<String>> networkPreferences = + final Map<String, Integer> networkPreferences = mBuilder.build().getNetworkPreferences(); assertThrows(UnsupportedOperationException.class, - () -> networkPreferences.get(TEST_PREF).set(mPackages.size() - 1, newPackage)); + () -> networkPreferences.put(newPackage, TEST_PREF)); assertThrows(UnsupportedOperationException.class, - () -> networkPreferences.get(TEST_PREF).add(newPackage)); + () -> networkPreferences.remove(TEST_PACKAGE)); + } @Test - public void toStringReturnsCorrectValue() { - mBuilder.addNetworkPreference(TEST_PREF, mPackages); + public void testToStringReturnsCorrectValue() { + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); final String networkPreferencesString = mBuilder.build().getNetworkPreferences().toString(); @@ -99,10 +97,56 @@ public class OemNetworkPreferencesTest { @Test public void testOemNetworkPreferencesParcelable() { - mBuilder.addNetworkPreference(TEST_PREF, mPackages); + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); final OemNetworkPreferences prefs = mBuilder.build(); assertParcelSane(prefs, 1 /* fieldCount */); } + + @Test + public void testAddNetworkPreferenceOverwritesPriorPreference() { + final int newPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + Map<String, Integer> networkPreferences = + mBuilder.build().getNetworkPreferences(); + + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF); + + mBuilder.addNetworkPreference(TEST_PACKAGE, newPref); + networkPreferences = mBuilder.build().getNetworkPreferences(); + + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), newPref); + } + + @Test + public void testRemoveNetworkPreferenceRemovesValue() { + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + Map<String, Integer> networkPreferences = + mBuilder.build().getNetworkPreferences(); + + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + + mBuilder.removeNetworkPreference(TEST_PACKAGE); + networkPreferences = mBuilder.build().getNetworkPreferences(); + + assertFalse(networkPreferences.containsKey(TEST_PACKAGE)); + } + + @Test + public void testConstructorByOemNetworkPreferencesSetsValue() { + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + OemNetworkPreferences networkPreference = mBuilder.build(); + + final Map<String, Integer> networkPreferences = + new OemNetworkPreferences + .Builder(networkPreference) + .build() + .getNetworkPreferences(); + + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF); + } } diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java index dc9e587332cb..e1da3d0ae2b3 100644 --- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java +++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java @@ -17,6 +17,7 @@ package com.android.server; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; @@ -84,6 +85,7 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { final String typeName = ConnectivityManager.getNetworkTypeName(type); mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities(); mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); mNetworkCapabilities.addTransportType(transport); switch (transport) { case TRANSPORT_ETHERNET: diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java index c2fddf3d9e82..fcfb4aa9b864 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -345,15 +345,17 @@ public class ConnectivityManagerTest { @Test public void testRequestType() throws Exception { final String testPkgName = "MyPackage"; + final String testAttributionTag = "MyTag"; final ConnectivityManager manager = new ConnectivityManager(mCtx, mService); when(mCtx.getOpPackageName()).thenReturn(testPkgName); + when(mCtx.getAttributionTag()).thenReturn(testAttributionTag); final NetworkRequest request = makeRequest(1); final NetworkCallback callback = new ConnectivityManager.NetworkCallback(); manager.requestNetwork(request, callback); verify(mService).requestNetwork(eq(request.networkCapabilities), eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), - eq(testPkgName), eq(null)); + eq(testPkgName), eq(testAttributionTag)); reset(mService); // Verify that register network callback does not calls requestNetwork at all. @@ -361,19 +363,19 @@ public class ConnectivityManagerTest { verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), any(), any()); verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), - eq(testPkgName)); + eq(testPkgName), eq(testAttributionTag)); reset(mService); manager.registerDefaultNetworkCallback(callback); verify(mService).requestNetwork(eq(null), eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), - eq(testPkgName), eq(null)); + eq(testPkgName), eq(testAttributionTag)); reset(mService); manager.requestBackgroundNetwork(request, null, callback); verify(mService).requestNetwork(eq(request.networkCapabilities), eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), - eq(testPkgName), eq(null)); + eq(testPkgName), eq(testAttributionTag)); reset(mService); } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index c7554f6b79b6..bf6163438ba2 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -331,11 +331,12 @@ public class ConnectivityServiceTest { private static final String TAG = "ConnectivityServiceTest"; private static final int TIMEOUT_MS = 500; - private static final int TEST_LINGER_DELAY_MS = 300; - // Chosen to be less than the linger timeout. This ensures that we can distinguish between a - // LOST callback that arrives immediately and a LOST callback that arrives after the linger - // timeout. For this, our assertions should run fast enough to leave less than - // (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are + private static final int TEST_LINGER_DELAY_MS = 400; + private static final int TEST_NASCENT_DELAY_MS = 300; + // Chosen to be less than the linger and nascent timeout. This ensures that we can distinguish + // between a LOST callback that arrives immediately and a LOST callback that arrives after + // the linger/nascent timeout. For this, our assertions should run fast enough to leave + // less than (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are // supposedly fired, and the time we call expectCallback. private static final int TEST_CALLBACK_TIMEOUT_MS = 250; // Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to @@ -1262,22 +1263,35 @@ public class ConnectivityServiceTest { } } - private void updateUidNetworkingBlocked() { - doAnswer(i -> NetworkPolicyManagerInternal.isUidNetworkingBlocked( - i.getArgument(0) /* uid */, mUidRules, i.getArgument(1) /* metered */, - mRestrictBackground) + private void processBroadcastForVpn(Intent intent) { + // The BroadcastReceiver for this broadcast checks it is being run on the handler thread. + final Handler handler = new Handler(mCsHandlerThread.getLooper()); + handler.post(() -> mServiceContext.sendBroadcast(intent)); + waitForIdle(); + } + + private void mockUidNetworkingBlocked() { + doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class) + .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules, + i.getArgument(1) /* metered */, mRestrictBackground) ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean()); + + doAnswer(inv -> mContext.getSystemService(NetworkPolicyManager.class) + .checkUidNetworkingBlocked(inv.getArgument(0) /* uid */, + inv.getArgument(1) /* uidRules */, + inv.getArgument(2) /* isNetworkMetered */, + inv.getArgument(3) /* isBackgroundRestricted */) + ).when(mNetworkPolicyManager).checkUidNetworkingBlocked( + anyInt(), anyInt(), anyBoolean(), anyBoolean()); } private void setUidRulesChanged(int uidRules) throws RemoteException { mUidRules = uidRules; - updateUidNetworkingBlocked(); mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules); } private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException { mRestrictBackground = restrictBackground; - updateUidNetworkingBlocked(); mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground); } @@ -1389,6 +1403,7 @@ public class ConnectivityServiceTest { mMockNetd, mDeps); mService.mLingerDelayMs = TEST_LINGER_DELAY_MS; + mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS; verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any()); final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor = @@ -1731,6 +1746,108 @@ public class ConnectivityServiceTest { verifyNoNetwork(); } + /** + * Verify a newly created network will be inactive instead of torn down even if no one is + * requesting. + */ + @Test + public void testNewNetworkInactive() throws Exception { + // Create a callback that monitoring the testing network. + final TestNetworkCallback listenCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), listenCallback); + + // 1. Create a network that is not requested by anyone, and does not satisfy any of the + // default requests. Verify that the network will be inactive instead of torn down. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithoutInternet(); + listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + listenCallback.assertNoCallback(); + + // Verify that the network will be torn down after nascent expiry. A small period of time + // is added in case of flakiness. + final int nascentTimeoutMs = + mService.mNascentDelayMs + mService.mNascentDelayMs / 4; + listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, nascentTimeoutMs); + + // 2. Create a network that is satisfied by a request comes later. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithoutInternet(); + listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + final TestNetworkCallback wifiCallback = new TestNetworkCallback(); + mCm.requestNetwork(wifiRequest, wifiCallback); + wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + // Verify that the network will be kept since the request is still satisfied. And is able + // to get disconnected as usual if the request is released after the nascent timer expires. + listenCallback.assertNoCallback(nascentTimeoutMs); + mCm.unregisterNetworkCallback(wifiCallback); + listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // 3. Create a network that is satisfied by a request comes later. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithoutInternet(); + listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + mCm.requestNetwork(wifiRequest, wifiCallback); + wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + // Verify that the network will still be torn down after the request gets removed. + mCm.unregisterNetworkCallback(wifiCallback); + listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // There is no need to ensure that LOSING is never sent in the common case that the + // network immediately satisfies a request that was already present, because it is already + // verified anywhere whenever {@code TestNetworkCallback#expectAvailable*} is called. + + mCm.unregisterNetworkCallback(listenCallback); + } + + /** + * Verify a newly created network will be inactive and switch to background if only background + * request is satisfied. + */ + @Test + public void testNewNetworkInactive_bgNetwork() throws Exception { + // Create a callback that monitoring the wifi network. + final TestNetworkCallback wifiListenCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(), wifiListenCallback); + + // Create callbacks that can monitor background and foreground mobile networks. + // This is done by granting using background networks permission before registration. Thus, + // the service will not add {@code NET_CAPABILITY_FOREGROUND} by default. + grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid()); + final TestNetworkCallback bgMobileListenCallback = new TestNetworkCallback(); + final TestNetworkCallback fgMobileListenCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(), bgMobileListenCallback); + mCm.registerNetworkCallback(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_FOREGROUND).build(), fgMobileListenCallback); + + // Connect wifi, which satisfies default request. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + wifiListenCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); + + // Connect a cellular network, verify that satisfies only the background callback. + setAlwaysOnNetworks(true); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + bgMobileListenCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + fgMobileListenCallback.assertNoCallback(); + assertFalse(isForegroundNetwork(mCellNetworkAgent)); + + mCellNetworkAgent.disconnect(); + bgMobileListenCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + fgMobileListenCallback.assertNoCallback(); + + mCm.unregisterNetworkCallback(wifiListenCallback); + mCm.unregisterNetworkCallback(bgMobileListenCallback); + mCm.unregisterNetworkCallback(fgMobileListenCallback); + } + @Test public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception { // Test bringing up unvalidated WiFi @@ -2590,6 +2707,10 @@ public class ConnectivityServiceTest { NetworkCapabilities filter = new NetworkCapabilities(); filter.addCapability(capability); + // Add NOT_VCN_MANAGED capability into filter unconditionally since some request will add + // NOT_VCN_MANAGED automatically but not for NetworkCapabilities, + // see {@code NetworkCapabilities#deduceNotVcnManagedCapability} for more details. + filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests"); handlerThread.start(); final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), @@ -3841,6 +3962,7 @@ public class ConnectivityServiceTest { handlerThread.start(); NetworkCapabilities filter = new NetworkCapabilities() .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) .addCapability(NET_CAPABILITY_INTERNET); final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), mServiceContext, "testFactory", filter); @@ -3888,8 +4010,9 @@ public class ConnectivityServiceTest { setAlwaysOnNetworks(false); testFactory.expectRequestRemove(); - // ... and cell data to be torn down. - cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + // ... and cell data to be torn down after nascent network timeout. + cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, + mService.mNascentDelayMs + TEST_CALLBACK_TIMEOUT_MS); assertLength(1, mCm.getAllNetworks()); } finally { testFactory.terminate(); @@ -5294,20 +5417,20 @@ public class ConnectivityServiceTest { // MOBILE_IFNAME even though the default network is wifi. // TODO: fix this to pass in the actual default network interface. Whether or not the VPN // applies to the system server UID should not have any bearing on network stats. - mService.setUnderlyingNetworksForVpn(onlyCell); + mMockVpn.setUnderlyingNetworks(onlyCell); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, new String[]{MOBILE_IFNAME}); reset(mStatsService); - mService.setUnderlyingNetworksForVpn(cellAndWifi); + mMockVpn.setUnderlyingNetworks(cellAndWifi); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, new String[]{MOBILE_IFNAME, WIFI_IFNAME}); reset(mStatsService); // Null underlying networks are ignored. - mService.setUnderlyingNetworksForVpn(cellNullAndWifi); + mMockVpn.setUnderlyingNetworks(cellNullAndWifi); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, new String[]{MOBILE_IFNAME, WIFI_IFNAME}); @@ -5356,25 +5479,25 @@ public class ConnectivityServiceTest { // is probably a performance improvement (though it's very unlikely that a VPN would declare // no underlying networks). // Also, for the same reason as above, the active interface passed in is null. - mService.setUnderlyingNetworksForVpn(new Network[0]); + mMockVpn.setUnderlyingNetworks(new Network[0]); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, null); reset(mStatsService); // Specifying only a null underlying network is the same as no networks. - mService.setUnderlyingNetworksForVpn(onlyNull); + mMockVpn.setUnderlyingNetworks(onlyNull); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, null); reset(mStatsService); // Specifying networks that are all disconnected is the same as specifying no networks. - mService.setUnderlyingNetworksForVpn(onlyCell); + mMockVpn.setUnderlyingNetworks(onlyCell); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, null); reset(mStatsService); // Passing in null again means follow the default network again. - mService.setUnderlyingNetworksForVpn(null); + mMockVpn.setUnderlyingNetworks(null); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, new String[]{WIFI_IFNAME}); @@ -5743,6 +5866,7 @@ public class ConnectivityServiceTest { .addTransportType(TRANSPORT_CELLULAR) .addCapability(NET_CAPABILITY_INTERNET) .addCapability(NET_CAPABILITY_NOT_CONGESTED) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) .setLinkDownstreamBandwidthKbps(10); final NetworkCapabilities wifiNc = new NetworkCapabilities() .addTransportType(TRANSPORT_WIFI) @@ -5751,6 +5875,7 @@ public class ConnectivityServiceTest { .addCapability(NET_CAPABILITY_NOT_ROAMING) .addCapability(NET_CAPABILITY_NOT_CONGESTED) .addCapability(NET_CAPABILITY_NOT_SUSPENDED) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) .setLinkUpstreamBandwidthKbps(20); mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */); mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */); @@ -5849,7 +5974,7 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(false, true, false); assertUidRangesUpdatedForMyUid(true); final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId()); - mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork}); + mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork}); callback.expectAvailableCallbacksUnvalidated(mMockVpn); assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) .hasTransport(TRANSPORT_VPN)); @@ -6043,7 +6168,7 @@ public class ConnectivityServiceTest { final Set<UidRange> ranges = uidRangesForUid(uid); mMockVpn.registerAgent(ranges); - mService.setUnderlyingNetworksForVpn(new Network[0]); + mMockVpn.setUnderlyingNetworks(new Network[0]); // VPN networks do not satisfy the default request and are automatically validated // by NetworkMonitor @@ -6247,7 +6372,7 @@ public class ConnectivityServiceTest { private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) { final NetworkCapabilities[] defaultCaps = mService.getDefaultNetworkCapabilitiesForUser( - userId, "com.android.calling.package"); + userId, "com.android.calling.package", "com.test"); final String defaultCapsString = Arrays.toString(defaultCaps); assertEquals(defaultCapsString, defaultCaps.length, networks.length); final Set<NetworkCapabilities> defaultCapsSet = new ArraySet<>(defaultCaps); @@ -6291,7 +6416,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); mCellNetworkAgent.connect(true); - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6306,7 +6431,7 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); mWiFiNetworkAgent.connect(true); - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6317,7 +6442,7 @@ public class ConnectivityServiceTest { assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); // Don't disconnect, but note the VPN is not using wifi any more. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6348,7 +6473,7 @@ public class ConnectivityServiceTest { vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn); // Use Wifi but not cell. Note the VPN is now unmetered and not suspended. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mWiFiNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6359,7 +6484,7 @@ public class ConnectivityServiceTest { assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent); // Use both again. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6374,7 +6499,7 @@ public class ConnectivityServiceTest { vpnNetworkCallback.assertNoCallback(); // Stop using WiFi. The VPN is suspended again. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) @@ -6385,7 +6510,7 @@ public class ConnectivityServiceTest { assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); // Use both again. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6520,9 +6645,7 @@ public class ConnectivityServiceTest { addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); // Send a USER_ADDED broadcast for it. - // The BroadcastReceiver for this broadcast checks that is being run on the handler thread. - final Handler handler = new Handler(mCsHandlerThread.getLooper()); - handler.post(() -> mServiceContext.sendBroadcast(addedIntent)); + processBroadcastForVpn(addedIntent); // Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added // restricted user. @@ -6546,7 +6669,7 @@ public class ConnectivityServiceTest { // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. final Intent removedIntent = new Intent(ACTION_USER_REMOVED); removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - handler.post(() -> mServiceContext.sendBroadcast(removedIntent)); + processBroadcastForVpn(removedIntent); // Expect that the VPN gains the UID range for the restricted user, and that the capability // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved. @@ -6603,9 +6726,7 @@ public class ConnectivityServiceTest { // TODO: check that VPN app within restricted profile still has access, etc. final Intent addedIntent = new Intent(ACTION_USER_ADDED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - final Handler handler = new Handler(mCsHandlerThread.getLooper()); - handler.post(() -> mServiceContext.sendBroadcast(addedIntent)); - waitForIdle(); + processBroadcastForVpn(addedIntent); assertNull(mCm.getActiveNetworkForUid(uid)); assertNull(mCm.getActiveNetworkForUid(restrictedUid)); @@ -6615,8 +6736,7 @@ public class ConnectivityServiceTest { // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. final Intent removedIntent = new Intent(ACTION_USER_REMOVED); removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - handler.post(() -> mServiceContext.sendBroadcast(removedIntent)); - waitForIdle(); + processBroadcastForVpn(removedIntent); assertNull(mCm.getActiveNetworkForUid(uid)); assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); @@ -6718,7 +6838,7 @@ public class ConnectivityServiceTest { // Ensure VPN is now the active network. assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); // VPN is using Cell - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork() }); waitForIdle(); @@ -6726,7 +6846,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); // VPN is now using WiFi - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mWiFiNetworkAgent.getNetwork() }); waitForIdle(); @@ -6734,7 +6854,7 @@ public class ConnectivityServiceTest { assertFalse(mCm.isActiveNetworkMetered()); // VPN is using Cell | WiFi. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); waitForIdle(); @@ -6742,7 +6862,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); // VPN is using WiFi | Cell. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() }); waitForIdle(); @@ -6750,7 +6870,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); // VPN is not using any underlying networks. - mService.setUnderlyingNetworksForVpn(new Network[0]); + mMockVpn.setUnderlyingNetworks(new Network[0]); waitForIdle(); // VPN without underlying networks is treated as metered. @@ -6777,7 +6897,7 @@ public class ConnectivityServiceTest { assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); // VPN is tracking current platform default (WiFi). - mService.setUnderlyingNetworksForVpn(null); + mMockVpn.setUnderlyingNetworks(null); waitForIdle(); // Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered. @@ -6785,7 +6905,7 @@ public class ConnectivityServiceTest { // VPN explicitly declares WiFi as its underlying network. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mWiFiNetworkAgent.getNetwork() }); waitForIdle(); @@ -6809,6 +6929,7 @@ public class ConnectivityServiceTest { .addTransportType(TRANSPORT_CELLULAR) .build(); mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); + mockUidNetworkingBlocked(); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); @@ -6891,6 +7012,7 @@ public class ConnectivityServiceTest { public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception { final TestNetworkCallback defaultCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(defaultCallback); + mockUidNetworkingBlocked(); // No Networkcallbacks invoked before any network is active. setUidRulesChanged(RULE_REJECT_ALL); @@ -7160,6 +7282,13 @@ public class ConnectivityServiceTest { when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile); } + private void establishLegacyLockdownVpn() throws Exception { + // The legacy lockdown VPN only supports userId 0. + final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + mMockVpn.registerAgent(ranges); + mMockVpn.connect(true); + } + @Test public void testLegacyLockdownVpn() throws Exception { mServiceContext.setPermission( @@ -7184,9 +7313,7 @@ public class ConnectivityServiceTest { final int userId = UserHandle.getUserId(Process.myUid()); final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - final Handler handler = new Handler(mCsHandlerThread.getLooper()); - handler.post(() -> mServiceContext.sendBroadcast(addedIntent)); - waitForIdle(); + processBroadcastForVpn(addedIntent); // Lockdown VPN disables teardown and enables lockdown. assertFalse(mMockVpn.getEnableTeardown()); @@ -7254,22 +7381,30 @@ public class ConnectivityServiceTest { mMockVpn.expectStartLegacyVpnRunner(); b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED); ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); - mMockVpn.establishForMyUid(); + establishLegacyLockdownVpn(); callback.expectAvailableThenValidatedCallbacks(mMockVpn); defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); b1.expectBroadcast(); b2.expectBroadcast(); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertTrue(vpnNc.hasTransport(TRANSPORT_VPN)); + assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR)); + assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI)); + assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED)); // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect. final LinkProperties wifiLp = new LinkProperties(); wifiLp.setInterfaceName("wlan0"); wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25")); wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0")); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp); + final NetworkCapabilities wifiNc = new NetworkCapabilities(); + wifiNc.addTransportType(TRANSPORT_WIFI); + wifiNc.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc); b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); // Wifi is CONNECTING because the VPN isn't up yet. @@ -7302,16 +7437,20 @@ public class ConnectivityServiceTest { // The VPN comes up again on wifi. b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED); b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); - mMockVpn.establishForMyUid(); + establishLegacyLockdownVpn(); callback.expectAvailableThenValidatedCallbacks(mMockVpn); defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); b1.expectBroadcast(); b2.expectBroadcast(); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); + assertTrue(vpnNc.hasTransport(TRANSPORT_VPN)); + assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI)); + assertFalse(vpnNc.hasTransport(TRANSPORT_CELLULAR)); + assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED)); // Disconnect cell. Nothing much happens since it's not the default network. // Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any @@ -7382,19 +7521,13 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.removeCapability(testCap); callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent); callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent); - // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has - // it. - if (testCap == NET_CAPABILITY_TRUSTED) { - verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); - reset(mMockNetd); - } + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); mCellNetworkAgent.removeCapability(testCap); callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); callbackWithoutCap.assertNoCallback(); - if (testCap == NET_CAPABILITY_TRUSTED) { - verify(mMockNetd).networkClearDefault(); - } + verify(mMockNetd).networkClearDefault(); mCm.unregisterNetworkCallback(callbackWithCap); mCm.unregisterNetworkCallback(callbackWithoutCap); @@ -8245,7 +8378,8 @@ public class ConnectivityServiceTest { when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle); if (op != null) { - when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName()))) + when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), + eq(mContext.getPackageName()), eq(getAttributionTag()), anyString())) .thenReturn(AppOpsManager.MODE_ALLOWED); } @@ -8258,7 +8392,7 @@ public class ConnectivityServiceTest { final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid); return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, callerUid, mContext.getPackageName()).getOwnerUid(); + netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid(); } private void verifyWifiInfoCopyNetCapsForCallerPermission( @@ -8268,7 +8402,7 @@ public class ConnectivityServiceTest { final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo); mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, callerUid, mContext.getPackageName()); + netCap, callerUid, mContext.getPackageName(), getAttributionTag()); verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable)); } @@ -8616,7 +8750,7 @@ public class ConnectivityServiceTest { setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); - assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {naiWithoutUid.network})); + assertTrue(mMockVpn.setUnderlyingNetworks(new Network[] {naiWithoutUid.network})); waitForIdle(); assertTrue( "Active VPN permission not applied", @@ -8624,7 +8758,7 @@ public class ConnectivityServiceTest { Process.myPid(), Process.myUid(), naiWithoutUid, mContext.getOpPackageName())); - assertTrue(mService.setUnderlyingNetworksForVpn(null)); + assertTrue(mMockVpn.setUnderlyingNetworks(null)); waitForIdle(); assertFalse( "VPN shouldn't receive callback on non-underlying network", diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index f4782829cff7..73cc9f129e79 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -49,6 +49,7 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -119,6 +120,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -213,6 +215,8 @@ public class VpnTest { when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG); when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG); + when(mContext.getSystemServiceName(UserManager.class)) + .thenReturn(Context.USER_SERVICE); when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager); when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps); when(mContext.getSystemServiceName(NotificationManager.class)) @@ -253,12 +257,14 @@ public class VpnTest { @Test public void testRestrictedProfilesAreAddedToVpn() { - if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB); final Vpn vpn = createVpn(primaryUser.id); - final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, - null, null); + + // Assume the user can have restricted profiles. + doReturn(true).when(mUserManager).canHaveRestrictedProfile(); + final Set<UidRange> ranges = + vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null); assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id) @@ -267,7 +273,6 @@ public class VpnTest { @Test public void testManagedProfilesAreNotAddedToVpn() { - if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. setMockedUsers(primaryUser, managedProfileA); final Vpn vpn = createVpn(primaryUser.id); @@ -290,7 +295,6 @@ public class VpnTest { @Test public void testUidAllowAndDenylist() throws Exception { - if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. final Vpn vpn = createVpn(primaryUser.id); final UidRange user = PRI_USER_RANGE; final String[] packages = {PKGS[0], PKGS[1], PKGS[2]}; @@ -316,7 +320,6 @@ public class VpnTest { @Test public void testGetAlwaysAndOnGetLockDown() throws Exception { - if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. final Vpn vpn = createVpn(primaryUser.id); // Default state. @@ -341,7 +344,6 @@ public class VpnTest { @Test public void testLockdownChangingPackage() throws Exception { - if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. final Vpn vpn = createVpn(primaryUser.id); final UidRange user = PRI_USER_RANGE; @@ -369,7 +371,6 @@ public class VpnTest { @Test public void testLockdownAllowlist() throws Exception { - if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. final Vpn vpn = createVpn(primaryUser.id); final UidRange user = PRI_USER_RANGE; @@ -444,7 +445,6 @@ public class VpnTest { @Test public void testLockdownRuleRepeatability() throws Exception { - if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. final Vpn vpn = createVpn(primaryUser.id); final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] { new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)}; @@ -477,7 +477,6 @@ public class VpnTest { @Test public void testLockdownRuleReversibility() throws Exception { - if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. final Vpn vpn = createVpn(primaryUser.id); final UidRangeParcel[] entireUser = { new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop) @@ -997,14 +996,12 @@ public class VpnTest { profile.ipsecIdentifier = "id"; profile.ipsecSecret = "secret"; profile.l2tpSecret = "l2tpsecret"; + when(mConnectivityManager.getAllNetworks()) .thenReturn(new Network[] { new Network(101) }); + when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(), - anyInt(), any(), anyInt())).thenAnswer(invocation -> { - // The runner has registered an agent and is now ready. - legacyRunnerReady.open(); - return new Network(102); - }); + anyInt(), any(), anyInt())).thenReturn(new Network(102)); final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile); final TestDeps deps = (TestDeps) vpn.mDeps; try { @@ -1020,14 +1017,20 @@ public class VpnTest { "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270" }, deps.mtpdArgs.get(10, TimeUnit.SECONDS)); + // Now wait for the runner to be ready before testing for the route. - legacyRunnerReady.block(10_000); - // In this test the expected address is always v4 so /32 + ArgumentCaptor<LinkProperties> lpCaptor = ArgumentCaptor.forClass(LinkProperties.class); + verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(), + lpCaptor.capture(), any(), anyInt(), any(), anyInt()); + + // In this test the expected address is always v4 so /32. + // Note that the interface needs to be specified because RouteInfo objects stored in + // LinkProperties objects always acquire the LinkProperties' interface. final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"), - RouteInfo.RTN_THROW); - assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : " - + vpn.mConfig.routes, - vpn.mConfig.routes.contains(expectedRoute)); + null, EGRESS_IFACE, RouteInfo.RTN_THROW); + final List<RouteInfo> actualRoutes = lpCaptor.getValue().getRoutes(); + assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes, + actualRoutes.contains(expectedRoute)); } finally { // Now interrupt the thread, unblock the runner and clean up. vpn.mVpnRunner.exitVpnRunner(); @@ -1083,6 +1086,11 @@ public class VpnTest { } @Override + public PendingIntent getIntentForStatusPanel(Context context) { + return null; + } + + @Override public void sendArgumentsToDaemon( final String daemon, final LocalSocket socket, final String[] arguments, final Vpn.RetryScheduler interruptChecker) throws IOException { @@ -1179,11 +1187,6 @@ public class VpnTest { final int id = (int) invocation.getArguments()[0]; return userMap.get(id); }).when(mUserManager).getUserInfo(anyInt()); - - doAnswer(invocation -> { - final int id = (int) invocation.getArguments()[0]; - return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0; - }).when(mUserManager).canHaveRestrictedProfile(); } /** diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index aeb142b901d2..1fe13fe97fbe 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -28,8 +28,6 @@ import android.view.IWindowManager; import junit.framework.TestCase; -import org.junit.Test; - /** * TODO: Remove this. This is only a placeholder, need to implement this. */ @@ -56,7 +54,7 @@ public class WindowManagerPermissionTests extends TestCase { } try { - mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY); + mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null /* options */); fail("IWindowManager.addWindowToken did not throw SecurityException as" + " expected"); } catch (SecurityException e) { @@ -155,29 +153,4 @@ public class WindowManagerPermissionTests extends TestCase { fail("Unexpected remote exception"); } } - - @Test - public void testADD_WINDOW_TOKEN_WITH_OPTIONS() { - // Verify if addWindowTokenWithOptions throw SecurityException for privileged window type. - try { - mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, ""); - fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as" - + " expected"); - } catch (SecurityException e) { - // expected - } catch (RemoteException e) { - fail("Unexpected remote exception"); - } - - // Verify if addWindowTokenWithOptions throw SecurityException for null packageName. - try { - mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, null); - fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as" - + " expected"); - } catch (SecurityException e) { - // expected - } catch (RemoteException e) { - fail("Unexpected remote exception"); - } - } } 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 a7d0860210b4..a07bce34c737 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 @@ -46,6 +46,8 @@ public final class FrameworksTestsFilter extends SelectTest { "android.view.InsetsSourceTest", "android.view.InsetsSourceConsumerTest", "android.view.InsetsStateTest", + "android.view.RoundedCornerTest", + "android.view.RoundedCornersTest", "android.view.WindowMetricsTest", "android.view.PendingInsetsControllerTest", "android.app.WindowContextTest", diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java index 86a15912b6b4..3e659d0bc128 100644 --- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java @@ -59,12 +59,17 @@ public class VcnGatewayConnectionConfigTest { // Public for use in VcnGatewayConnectionTest public static VcnGatewayConnectionConfig buildTestConfig() { + return buildTestConfigWithExposedCaps(EXPOSED_CAPS); + } + + // Public for use in VcnGatewayConnectionTest + public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) { final VcnGatewayConnectionConfig.Builder builder = new VcnGatewayConnectionConfig.Builder() .setRetryInterval(RETRY_INTERVALS_MS) .setMaxMtu(MAX_MTU); - for (int caps : EXPOSED_CAPS) { + for (int caps : exposedCaps) { builder.addExposedCapability(caps); } diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index e32e1e831f83..485964487fda 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -66,6 +66,7 @@ import android.telephony.TelephonyManager; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.VcnManagementService.VcnSafemodeCallback; import com.android.server.vcn.TelephonySubscriptionTracker; import com.android.server.vcn.Vcn; import com.android.server.vcn.VcnContext; @@ -142,6 +143,9 @@ public class VcnManagementServiceTest { private final TelephonySubscriptionTracker mSubscriptionTracker = mock(TelephonySubscriptionTracker.class); + private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor = + ArgumentCaptor.forClass(VcnSafemodeCallback.class); + private final VcnManagementService mVcnMgmtSvc; private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener = @@ -184,7 +188,7 @@ public class VcnManagementServiceTest { doAnswer((invocation) -> { // Mock-within a doAnswer is safe, because it doesn't actually run nested. return mock(Vcn.class); - }).when(mMockDeps).newVcn(any(), any(), any(), any()); + }).when(mMockDeps).newVcn(any(), any(), any(), any(), any()); final PersistableBundle bundle = PersistableBundleUtils.fromMap( @@ -307,7 +311,7 @@ public class VcnManagementServiceTest { TelephonySubscriptionSnapshot snapshot = triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1)); verify(mMockDeps) - .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot)); + .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any()); } @Test @@ -485,7 +489,8 @@ public class VcnManagementServiceTest { eq(mVcnContext), eq(TEST_UUID_2), eq(TEST_VCN_CONFIG), - eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT)); + eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT), + any()); // Verify Vcn is updated if it was previously started mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); @@ -634,4 +639,25 @@ public class VcnManagementServiceTest { verify(mMockPolicyListener).onPolicyChanged(); } + + @Test + public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception { + TelephonySubscriptionSnapshot snapshot = + triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1)); + verify(mMockDeps) + .newVcn( + eq(mVcnContext), + eq(TEST_UUID_1), + eq(TEST_VCN_CONFIG), + eq(snapshot), + mSafemodeCallbackCaptor.capture()); + + mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + + VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue(); + safemodeCallback.onEnteredSafemode(); + + assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive()); + verify(mMockPolicyListener).onPolicyChanged(); + } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index e20070ee4f07..278d93a1b17b 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -18,22 +18,35 @@ package com.android.server.vcn; import static android.net.IpSecManager.DIRECTION_IN; import static android.net.IpSecManager.DIRECTION_OUT; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration; import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; + import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +import java.util.Collections; /** Tests for VcnGatewayConnection.ConnectedState */ @RunWith(AndroidJUnit4.class) @@ -107,6 +120,51 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection } @Test + public void testChildOpenedRegistersNetwork() throws Exception { + final VcnChildSessionConfiguration mMockChildSessionConfig = + mock(VcnChildSessionConfiguration.class); + doReturn(Collections.singletonList(TEST_INTERNAL_ADDR)) + .when(mMockChildSessionConfig) + .getInternalAddresses(); + doReturn(Collections.singletonList(TEST_DNS_ADDR)) + .when(mMockChildSessionConfig) + .getInternalDnsServers(); + + getChildSessionCallback().onOpened(mMockChildSessionConfig); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); + + final ArgumentCaptor<LinkProperties> lpCaptor = + ArgumentCaptor.forClass(LinkProperties.class); + final ArgumentCaptor<NetworkCapabilities> ncCaptor = + ArgumentCaptor.forClass(NetworkCapabilities.class); + verify(mConnMgr) + .registerNetworkAgent( + any(), + any(), + lpCaptor.capture(), + ncCaptor.capture(), + anyInt(), + any(), + anyInt()); + verify(mIpSecSvc) + .addAddressToTunnelInterface( + eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any()); + + final LinkProperties lp = lpCaptor.getValue(); + assertEquals(Collections.singletonList(TEST_INTERNAL_ADDR), lp.getLinkAddresses()); + assertEquals(Collections.singletonList(TEST_DNS_ADDR), lp.getDnsServers()); + + final NetworkCapabilities nc = ncCaptor.getValue(); + assertTrue(nc.hasTransport(TRANSPORT_CELLULAR)); + assertFalse(nc.hasTransport(TRANSPORT_WIFI)); + for (int cap : mConfig.getAllExposedCapabilities()) { + assertTrue(nc.hasCapability(cap)); + } + } + + @Test public void testChildSessionClosedTriggersDisconnect() throws Exception { getChildSessionCallback().onClosed(); mTestLooper.dispatchAll(); @@ -122,6 +180,4 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); } - - // TODO: Add tests for childOpened() when ChildSessionConfiguration can be mocked or created } diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java index fbaae6f534a9..8643d8a2ea8a 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java @@ -45,7 +45,12 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect public void testEnterWhileNotRunningTriggersQuit() throws Exception { final VcnGatewayConnection vgc = new VcnGatewayConnection( - mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps); + mVcnContext, + TEST_SUB_GRP, + TEST_SUBSCRIPTION_SNAPSHOT, + mConfig, + mGatewayStatusCallback, + mDeps); vgc.setIsRunning(false); vgc.transitionTo(vgc.mDisconnectedState); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java new file mode 100644 index 000000000000..3f2b47cd58fd --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn; + +import static org.junit.Assert.assertEquals; + +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 VcnGatewayConnection.RetryTimeoutState */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnectionTestBase { + @Before + public void setUp() throws Exception { + super.setUp(); + + mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1); + mGatewayConnection.transitionTo(mGatewayConnection.mRetryTimeoutState); + mTestLooper.dispatchAll(); + } + + @Test + public void testNewNetworkTriggerRetry() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); + } + + @Test + public void testSameNetworkDoesNotTriggerRetry() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); + } + + @Test + public void testNullNetworkTriggersDisconnect() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(null); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState()); + } + + @Test + public void testTimeoutElapsingTriggersRetry() throws Exception { + mTestLooper.moveTimeForward(mConfig.getRetryIntervalsMs()[0]); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index df1341cce20f..d449eab494f6 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -27,10 +27,13 @@ import static org.mockito.Mockito.verify; import android.annotation.NonNull; import android.content.Context; +import android.net.ConnectivityManager; +import android.net.InetAddresses; import android.net.IpSecConfig; import android.net.IpSecManager; import android.net.IpSecTransform; import android.net.IpSecTunnelInterfaceResponse; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; @@ -43,15 +46,23 @@ import android.os.test.TestLooper; import com.android.server.IpSecService; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; +import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback; import org.junit.Before; import org.mockito.ArgumentCaptor; +import java.net.InetAddress; import java.util.Collections; import java.util.UUID; public class VcnGatewayConnectionTestBase { protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID()); + protected static final InetAddress TEST_DNS_ADDR = + InetAddresses.parseNumericAddress("2001:DB8:0:1::"); + protected static final LinkAddress TEST_INTERNAL_ADDR = + new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:0:2::"), 64); + protected static final int TEST_IPSEC_SPI_VALUE = 0x1234; protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1; protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2; @@ -80,10 +91,12 @@ public class VcnGatewayConnectionTestBase { @NonNull protected final VcnNetworkProvider mVcnNetworkProvider; @NonNull protected final VcnContext mVcnContext; @NonNull protected final VcnGatewayConnectionConfig mConfig; + @NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback; @NonNull protected final VcnGatewayConnection.Dependencies mDeps; @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker; @NonNull protected final IpSecService mIpSecSvc; + @NonNull protected final ConnectivityManager mConnMgr; protected VcnIkeSession mMockIkeSession; protected VcnGatewayConnection mGatewayConnection; @@ -94,12 +107,17 @@ public class VcnGatewayConnectionTestBase { mVcnNetworkProvider = mock(VcnNetworkProvider.class); mVcnContext = mock(VcnContext.class); mConfig = VcnGatewayConnectionConfigTest.buildTestConfig(); + mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class); mDeps = mock(VcnGatewayConnection.Dependencies.class); mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class); mIpSecSvc = mock(IpSecService.class); setupIpSecManager(mContext, mIpSecSvc); + mConnMgr = mock(ConnectivityManager.class); + VcnTestUtils.setupSystemService( + mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); + doReturn(mContext).when(mVcnContext).getContext(); doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper(); doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider(); @@ -123,7 +141,12 @@ public class VcnGatewayConnectionTestBase { mGatewayConnection = new VcnGatewayConnection( - mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps); + mVcnContext, + TEST_SUB_GRP, + TEST_SUBSCRIPTION_SNAPSHOT, + mConfig, + mGatewayStatusCallback, + mDeps); } protected IpSecTransform makeDummyIpSecTransform() throws Exception { @@ -137,10 +160,10 @@ public class VcnGatewayConnectionTestBase { return captor.getValue(); } - protected ChildSessionCallback getChildSessionCallback() { + protected VcnChildSessionCallback getChildSessionCallback() { ArgumentCaptor<ChildSessionCallback> captor = ArgumentCaptor.forClass(ChildSessionCallback.class); verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture()); - return captor.getValue(); + return (VcnChildSessionCallback) captor.getValue(); } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java index 0c1df763a08e..66cbf84619ab 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java @@ -16,22 +16,27 @@ package com.android.server.vcn; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.content.Context; import android.net.NetworkRequest; import android.net.vcn.VcnConfig; +import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnGatewayConnectionConfigTest; import android.os.ParcelUuid; import android.os.test.TestLooper; +import com.android.server.VcnManagementService.VcnSafemodeCallback; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener; import org.junit.Before; @@ -51,9 +56,13 @@ public class VcnTest { private VcnContext mVcnContext; private TelephonySubscriptionSnapshot mSubscriptionSnapshot; private VcnNetworkProvider mVcnNetworkProvider; + private VcnSafemodeCallback mVcnSafemodeCallback; private Vcn.Dependencies mDeps; + private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor; + private TestLooper mTestLooper; + private VcnGatewayConnectionConfig mGatewayConnectionConfig; private VcnConfig mConfig; private Vcn mVcn; @@ -63,6 +72,7 @@ public class VcnTest { mVcnContext = mock(VcnContext.class); mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class); mVcnNetworkProvider = mock(VcnNetworkProvider.class); + mVcnSafemodeCallback = mock(VcnSafemodeCallback.class); mDeps = mock(Vcn.Dependencies.class); mTestLooper = new TestLooper(); @@ -76,15 +86,26 @@ public class VcnTest { doAnswer((invocation) -> { // Mock-within a doAnswer is safe, because it doesn't actually run nested. return mock(VcnGatewayConnection.class); - }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any()); + }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any()); - mConfig = - new VcnConfig.Builder(mContext) - .addGatewayConnectionConfig( - VcnGatewayConnectionConfigTest.buildTestConfig()) - .build(); + mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class); - mVcn = new Vcn(mVcnContext, TEST_SUB_GROUP, mConfig, mSubscriptionSnapshot, mDeps); + final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext); + for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) { + configBuilder.addGatewayConnectionConfig( + VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability)); + } + configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig()); + mConfig = configBuilder.build(); + + mVcn = + new Vcn( + mVcnContext, + TEST_SUB_GROUP, + mConfig, + mSubscriptionSnapshot, + mVcnSafemodeCallback, + mDeps); } private NetworkRequestListener verifyAndGetRequestListener() { @@ -95,23 +116,22 @@ public class VcnTest { return mNetworkRequestListenerCaptor.getValue(); } - private NetworkRequest getNetworkRequestWithCapabilities(int[] networkCapabilities) { - final NetworkRequest.Builder builder = new NetworkRequest.Builder(); - for (final int netCapability : networkCapabilities) { - builder.addCapability(netCapability); + private void startVcnGatewayWithCapabilities( + NetworkRequestListener requestListener, int... netCapabilities) { + final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder(); + for (final int netCapability : netCapabilities) { + requestBuilder.addCapability(netCapability); } - return builder.build(); + + requestListener.onNetworkRequested(requestBuilder.build(), NETWORK_SCORE, PROVIDER_ID); + mTestLooper.dispatchAll(); } @Test public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() { final NetworkRequestListener requestListener = verifyAndGetRequestListener(); - - requestListener.onNetworkRequested( - getNetworkRequestWithCapabilities(VcnGatewayConnectionConfigTest.EXPOSED_CAPS), - NETWORK_SCORE, - PROVIDER_ID); - mTestLooper.dispatchAll(); + startVcnGatewayWithCapabilities( + requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS); final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections(); assertFalse(gatewayConnections.isEmpty()); @@ -126,4 +146,38 @@ public class VcnTest { verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot)); } } + + @Test + public void testGatewayEnteringSafemodeNotifiesVcn() { + final NetworkRequestListener requestListener = verifyAndGetRequestListener(); + for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) { + startVcnGatewayWithCapabilities(requestListener, capability); + } + + // Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp. + // Expect one VcnGatewayConnection per capability. + final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length; + + final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections(); + assertEquals(numExpectedGateways, gatewayConnections.size()); + verify(mDeps, times(numExpectedGateways)) + .newVcnGatewayConnection( + eq(mVcnContext), + eq(TEST_SUB_GROUP), + eq(mSubscriptionSnapshot), + any(), + mGatewayStatusCallbackCaptor.capture()); + + // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down + // all Gateways + final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue(); + statusCallback.onEnteredSafemode(); + mTestLooper.dispatchAll(); + + for (final VcnGatewayConnection gatewayConnection : gatewayConnections) { + verify(gatewayConnection).teardownAsynchronously(); + } + verify(mVcnNetworkProvider).unregisterListener(requestListener); + verify(mVcnSafemodeCallback).onEnteredSafemode(); + } } |