diff options
617 files changed, 14618 insertions, 5330 deletions
diff --git a/Android.bp b/Android.bp index 685c69df6823..8db5589d2ef6 100644 --- a/Android.bp +++ b/Android.bp @@ -325,6 +325,7 @@ java_defaults { "tv_tuner_resource_manager_aidl_interface-java", "soundtrigger_middleware-aidl-java", "modules-utils-os", + "framework-permission-aidl-java", ], } diff --git a/StubLibraries.bp b/StubLibraries.bp index b995f9570ec5..e6f50b7c4210 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -403,16 +403,6 @@ java_library_static { } java_defaults { - name: "android_defaults_stubs_current", - sdk_version: "none", - system_modules: "none", - java_version: "1.8", - compile_dex: true, - defaults_visibility: ["//visibility:private"], - visibility: ["//visibility:public"], -} - -java_defaults { name: "android_stubs_dists_default", dist: { targets: [ @@ -431,7 +421,7 @@ java_library_static { "android-non-updatable.stubs", "private-stub-annotations-jar", ], - defaults: ["android_defaults_stubs_current"], + defaults: ["android.jar_defaults"], } java_library_static { @@ -441,7 +431,7 @@ java_library_static { "private-stub-annotations-jar", ], defaults: [ - "android_defaults_stubs_current", + "android.jar_defaults", "android_stubs_dists_default", ], dist: { @@ -469,7 +459,7 @@ java_library_static { "private-stub-annotations-jar", ], defaults: [ - "android_defaults_stubs_current", + "android.jar_defaults", "android_stubs_dists_default", ], dist: { @@ -480,7 +470,7 @@ java_library_static { java_library_static { name: "android_module_lib_stubs_current", defaults: [ - "android_defaults_stubs_current", + "android.jar_defaults", "android_stubs_dists_default", ], static_libs: [ diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp index 57ee1ec482d8..566a18fbfbc1 100644 --- a/apex/appsearch/service/Android.bp +++ b/apex/appsearch/service/Android.bp @@ -28,6 +28,7 @@ java_library { "framework", "framework-appsearch", "services.core", + "services.usage", ], static_libs: [ "icing-java-proto-lite", diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index ec81ed25025a..3f6e8a5a7906 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -16,6 +16,7 @@ package com.android.server.appsearch; import static android.app.appsearch.AppSearchResult.throwableToFailedResult; +import static android.os.Process.INVALID_UID; import static android.os.UserHandle.USER_NULL; import android.annotation.ElapsedRealtimeLong; @@ -40,7 +41,9 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.content.pm.PackageStats; import android.os.Binder; import android.os.Bundle; import android.os.ParcelFileDescriptor; @@ -51,7 +54,6 @@ import android.os.UserManager; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; -import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; @@ -60,6 +62,8 @@ import com.android.server.appsearch.external.localstorage.AppSearchImpl; import com.android.server.appsearch.external.localstorage.stats.CallStats; import com.android.server.appsearch.stats.LoggerInstanceManager; import com.android.server.appsearch.stats.PlatformLogger; +import com.android.server.usage.StorageStatsManagerInternal; +import com.android.server.usage.StorageStatsManagerInternal.StorageStatsAugmenter; import com.google.android.icing.proto.PersistType; @@ -82,6 +86,7 @@ import java.util.concurrent.TimeUnit; public class AppSearchManagerService extends SystemService { private static final String TAG = "AppSearchManagerService"; private final Context mContext; + private PackageManager mPackageManager; private PackageManagerInternal mPackageManagerInternal; private ImplInstanceManager mImplInstanceManager; private UserManager mUserManager; @@ -109,17 +114,31 @@ public class AppSearchManagerService extends SystemService { @Override public void onStart() { publishBinderService(Context.APP_SEARCH_SERVICE, new Stub()); + mPackageManager = getContext().getPackageManager(); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mImplInstanceManager = ImplInstanceManager.getInstance(mContext); mUserManager = mContext.getSystemService(UserManager.class); mLoggerInstanceManager = LoggerInstanceManager.getInstance(); registerReceivers(); + LocalServices.getService(StorageStatsManagerInternal.class) + .registerStorageStatsAugmenter(new AppSearchStorageStatsAugmenter(), TAG); } private void registerReceivers() { mContext.registerReceiverAsUser(new UserActionReceiver(), UserHandle.ALL, new IntentFilter(Intent.ACTION_USER_REMOVED), /*broadcastPermission=*/ null, /*scheduler=*/ null); + + //TODO(b/145759910) Add a direct callback when user clears the data instead of relying on + // broadcasts + IntentFilter packageChangedFilter = new IntentFilter(); + packageChangedFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); + packageChangedFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); + packageChangedFilter.addDataScheme("package"); + packageChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + mContext.registerReceiverAsUser(new PackageChangedReceiver(), UserHandle.ALL, + packageChangedFilter, /*broadcastPermission=*/ null, + /*scheduler=*/ null); } private class UserActionReceiver extends BroadcastReceiver { @@ -127,15 +146,15 @@ public class AppSearchManagerService extends SystemService { public void onReceive(@NonNull Context context, @NonNull Intent intent) { switch (intent.getAction()) { case Intent.ACTION_USER_REMOVED: - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); if (userId == USER_NULL) { - Slog.e(TAG, "userId is missing in the intent: " + intent); + Log.e(TAG, "userId is missing in the intent: " + intent); return; } handleUserRemoved(userId); break; default: - Slog.e(TAG, "Received unknown intent: " + intent); + Log.e(TAG, "Received unknown intent: " + intent); } } } @@ -155,9 +174,52 @@ public class AppSearchManagerService extends SystemService { try { mImplInstanceManager.removeAppSearchImplForUser(userId); mLoggerInstanceManager.removePlatformLoggerForUser(userId); - Slog.i(TAG, "Removed AppSearchImpl instance for user: " + userId); + Log.i(TAG, "Removed AppSearchImpl instance for user: " + userId); } catch (Throwable t) { - Slog.e(TAG, "Unable to remove data for user: " + userId, t); + Log.e(TAG, "Unable to remove data for user: " + userId, t); + } + } + + private class PackageChangedReceiver extends BroadcastReceiver { + @Override + public void onReceive(@NonNull Context context, @NonNull Intent intent) { + switch (intent.getAction()) { + case Intent.ACTION_PACKAGE_FULLY_REMOVED: + case Intent.ACTION_PACKAGE_DATA_CLEARED: + String packageName = intent.getData().getSchemeSpecificPart(); + if (packageName == null) { + Log.e(TAG, "Package name is missing in the intent: " + intent); + return; + } + int uid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID); + if (uid == INVALID_UID) { + Log.e(TAG, "uid is missing in the intent: " + intent); + return; + } + handlePackageRemoved(packageName, uid); + break; + default: + Log.e(TAG, "Received unknown intent: " + intent); + } + } + } + + private void handlePackageRemoved(String packageName, int uid) { + int userId = UserHandle.getUserId(uid); + try { + if (isUserLocked(userId)) { + //TODO(b/186151459) clear the uninstalled package data when user is unlocked. + return; + } + if (ImplInstanceManager.getAppSearchDir(userId).exists()) { + // Only clear the package's data if AppSearch exists for this user. + AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext, + userId); + //TODO(b/145759910) clear visibility setting for package. + impl.clearPackageData(packageName); + } + } catch (Throwable t) { + Log.e(TAG, "Unable to remove data for package: " + packageName, t); } } @@ -168,6 +230,24 @@ public class AppSearchManagerService extends SystemService { } } + private void verifyUserUnlocked(int callingUserId) { + if (isUserLocked(callingUserId)) { + throw new IllegalStateException("User " + callingUserId + " is locked or not running."); + } + } + + private boolean isUserLocked(int callingUserId) { + synchronized (mUnlockedUserIdsLocked) { + // First, check the local copy. + if (mUnlockedUserIdsLocked.contains(callingUserId)) { + return false; + } + // If the local copy says the user is locked, check with UM for the actual state, + // since the user might just have been unlocked. + return !mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(callingUserId)); + } + } + private class Stub extends IAppSearchManager.Stub { @Override public void setSchema( @@ -769,25 +849,10 @@ public class AppSearchManagerService extends SystemService { }); } - private void verifyUserUnlocked(int callingUserId) { - synchronized (mUnlockedUserIdsLocked) { - // First, check the local copy. - if (mUnlockedUserIdsLocked.contains(callingUserId)) { - return; - } - // If the local copy says the user is locked, check with UM for the actual state, - // since the user might just have been unlocked. - if (!mUserManager.isUserUnlockingOrUnlocked(UserHandle.of(callingUserId))) { - throw new IllegalStateException( - "User " + callingUserId + " is locked or not running."); - } - } - } - private void verifyCallingPackage(int callingUid, @NonNull String callingPackage) { Objects.requireNonNull(callingPackage); if (mPackageManagerInternal.getPackageUid( - callingPackage, /*flags=*/ 0, UserHandle.getUserId(callingUid)) + callingPackage, /*flags=*/ 0, UserHandle.getUserId(callingUid)) != callingUid) { throw new SecurityException( "Specified calling package [" @@ -859,4 +924,52 @@ public class AppSearchManagerService extends SystemService { /*name=*/ null, /*callerPackage=*/ null); } + + // TODO(b/179160886): Cache the previous storage stats. + private class AppSearchStorageStatsAugmenter implements StorageStatsAugmenter { + @Override + public void augmentStatsForPackage( + @NonNull PackageStats stats, + @NonNull String packageName, + @UserIdInt int userId, + boolean callerHasStatsPermission) { + Objects.requireNonNull(stats); + Objects.requireNonNull(packageName); + try { + verifyUserUnlocked(userId); + AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext, + userId); + stats.dataSize += impl.getStorageInfoForPackage(packageName).getSizeBytes(); + } catch (Throwable t) { + Log.e( + TAG, + "Unable to augment storage stats for userId " + + userId + + " packageName " + + packageName, + t); + } + } + + @Override + public void augmentStatsForUid( + @NonNull PackageStats stats, int uid, boolean callerHasStatsPermission) { + Objects.requireNonNull(stats); + int userId = UserHandle.getUserId(uid); + try { + verifyUserUnlocked(userId); + String[] packagesForUid = mPackageManager.getPackagesForUid(uid); + if (packagesForUid == null) { + return; + } + AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext, + userId); + for (String packageName : packagesForUid) { + stats.dataSize += impl.getStorageInfoForPackage(packageName).getSizeBytes(); + } + } catch (Throwable t) { + Log.e(TAG, "Unable to augment storage stats for uid " + uid, t); + } + } + } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java index af39b790168c..45023f966ad2 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java @@ -73,6 +73,15 @@ public final class ImplInstanceManager { } /** + * Returns AppSearch directory in the credential encrypted system directory for the given user. + * + * <p>This folder should only be accessed after unlock. + */ + public static File getAppSearchDir(@UserIdInt int userId) { + return new File(Environment.getDataSystemCeDirectory(userId), APP_SEARCH_DIR); + } + + /** * Gets an instance of AppSearchImpl for the given user, or creates one if none exists. * * <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and @@ -139,15 +148,10 @@ public final class ImplInstanceManager { private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId) throws AppSearchException { - File appSearchDir = getAppSearchDir(context, userId); + File appSearchDir = getAppSearchDir(userId); return AppSearchImpl.create(appSearchDir, context, userId, mGlobalQuerierPackage); } - private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) { - // See com.android.internal.app.ChooserActivity::getPinnedSharedPrefs - return new File(Environment.getDataSystemCeDirectory(userId), APP_SEARCH_DIR); - } - /** * Returns the global querier package if it's a system package. Otherwise, empty string. * diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java index e65abcfba3e4..7c67ce424a25 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java @@ -49,7 +49,8 @@ public class BlobStoreIdleJobService extends JobService { public boolean onStopJob(final JobParameters params) { Slog.d(TAG, "Idle maintenance job is stopped; id=" + params.getJobId() + ", reason=" - + JobParameters.getLegacyReasonCodeDescription(params.getLegacyStopReason())); + + JobParameters.getInternalReasonCodeDescription( + params.getInternalStopReasonCode())); return false; } diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java index e1bfde15fa0a..48b62a607a5e 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java @@ -44,36 +44,53 @@ import java.lang.annotation.RetentionPolicy; public class JobParameters implements Parcelable { /** @hide */ - public static final int REASON_CANCELED = JobProtoEnums.STOP_REASON_CANCELLED; // 0. + public static final int INTERNAL_STOP_REASON_CANCELED = + JobProtoEnums.STOP_REASON_CANCELLED; // 0. /** @hide */ - public static final int REASON_CONSTRAINTS_NOT_SATISFIED = - JobProtoEnums.STOP_REASON_CONSTRAINTS_NOT_SATISFIED; //1. + public static final int INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED = + JobProtoEnums.STOP_REASON_CONSTRAINTS_NOT_SATISFIED; // 1. /** @hide */ - public static final int REASON_PREEMPT = JobProtoEnums.STOP_REASON_PREEMPT; // 2. - /** @hide */ - public static final int REASON_TIMEOUT = JobProtoEnums.STOP_REASON_TIMEOUT; // 3. + public static final int INTERNAL_STOP_REASON_PREEMPT = + JobProtoEnums.STOP_REASON_PREEMPT; // 2. + /** + * The job ran for at least its minimum execution limit. + * @hide + */ + public static final int INTERNAL_STOP_REASON_TIMEOUT = + JobProtoEnums.STOP_REASON_TIMEOUT; // 3. /** @hide */ - public static final int REASON_DEVICE_IDLE = JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4. + public static final int INTERNAL_STOP_REASON_DEVICE_IDLE = + JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4. /** @hide */ - public static final int REASON_DEVICE_THERMAL = JobProtoEnums.STOP_REASON_DEVICE_THERMAL; // 5. + public static final int INTERNAL_STOP_REASON_DEVICE_THERMAL = + JobProtoEnums.STOP_REASON_DEVICE_THERMAL; // 5. /** * The job is in the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} * bucket. * * @hide */ - public static final int REASON_RESTRICTED_BUCKET = + public static final int INTERNAL_STOP_REASON_RESTRICTED_BUCKET = JobProtoEnums.STOP_REASON_RESTRICTED_BUCKET; // 6. /** * The app was uninstalled. * @hide */ - public static final int DEBUG_REASON_UNINSTALL = 7; + public static final int INTERNAL_STOP_REASON_UNINSTALL = 7; /** * The app's data was cleared. * @hide */ - public static final int DEBUG_REASON_DATA_CLEARED = 8; + public static final int INTERNAL_STOP_REASON_DATA_CLEARED = 8; + /** + * @hide + */ + public static final int INTERNAL_STOP_REASON_RTC_UPDATED = 9; + /** + * The app called jobFinished() on its own. + * @hide + */ + public static final int INTERNAL_STOP_REASON_SUCCESSFUL_FINISH = 10; /** * All the stop reason codes. This should be regarded as an immutable array at runtime. @@ -85,13 +102,13 @@ public class JobParameters implements Parcelable { * @hide */ public static final int[] JOB_STOP_REASON_CODES = { - REASON_CANCELED, - REASON_CONSTRAINTS_NOT_SATISFIED, - REASON_PREEMPT, - REASON_TIMEOUT, - REASON_DEVICE_IDLE, - REASON_DEVICE_THERMAL, - REASON_RESTRICTED_BUCKET, + INTERNAL_STOP_REASON_CANCELED, + INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED, + INTERNAL_STOP_REASON_PREEMPT, + INTERNAL_STOP_REASON_TIMEOUT, + INTERNAL_STOP_REASON_DEVICE_IDLE, + INTERNAL_STOP_REASON_DEVICE_THERMAL, + INTERNAL_STOP_REASON_RESTRICTED_BUCKET, }; /** @@ -99,21 +116,24 @@ public class JobParameters implements Parcelable { */ // TODO(142420609): make it @SystemApi for mainline @NonNull - public static String getLegacyReasonCodeDescription(int reasonCode) { + public static String getInternalReasonCodeDescription(int reasonCode) { switch (reasonCode) { - case REASON_CANCELED: return "canceled"; - case REASON_CONSTRAINTS_NOT_SATISFIED: return "constraints"; - case REASON_PREEMPT: return "preempt"; - case REASON_TIMEOUT: return "timeout"; - case REASON_DEVICE_IDLE: return "device_idle"; - case REASON_DEVICE_THERMAL: return "thermal"; - case REASON_RESTRICTED_BUCKET: return "restricted_bucket"; + case INTERNAL_STOP_REASON_CANCELED: return "canceled"; + case INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED: return "constraints"; + case INTERNAL_STOP_REASON_PREEMPT: return "preempt"; + case INTERNAL_STOP_REASON_TIMEOUT: return "timeout"; + case INTERNAL_STOP_REASON_DEVICE_IDLE: return "device_idle"; + case INTERNAL_STOP_REASON_DEVICE_THERMAL: return "thermal"; + case INTERNAL_STOP_REASON_RESTRICTED_BUCKET: return "restricted_bucket"; + case INTERNAL_STOP_REASON_UNINSTALL: return "uninstall"; + case INTERNAL_STOP_REASON_DATA_CLEARED: return "data_cleared"; + case INTERNAL_STOP_REASON_RTC_UPDATED: return "rtc_updated"; + case INTERNAL_STOP_REASON_SUCCESSFUL_FINISH: return "successful_finish"; default: return "unknown:" + reasonCode; } } /** @hide */ - // TODO: move current users of legacy reasons to new public reasons @NonNull public static int[] getJobStopReasonCodes() { return JOB_STOP_REASON_CODES; @@ -241,7 +261,7 @@ public class JobParameters implements Parcelable { private final Network network; private int mStopReason = STOP_REASON_UNDEFINED; - private int mLegacyStopReason; // Default value of stopReason is REASON_CANCELED + private int mInternalStopReason; // Default value is REASON_CANCELED private String debugStopReason; // Human readable stop reason for debugging. /** @hide */ @@ -280,8 +300,8 @@ public class JobParameters implements Parcelable { } /** @hide */ - public int getLegacyStopReason() { - return mLegacyStopReason; + public int getInternalStopReasonCode() { + return mInternalStopReason; } /** @@ -502,15 +522,15 @@ public class JobParameters implements Parcelable { network = null; } mStopReason = in.readInt(); - mLegacyStopReason = in.readInt(); + mInternalStopReason = in.readInt(); debugStopReason = in.readString(); } /** @hide */ - public void setStopReason(@StopReason int reason, int legacyStopReason, + public void setStopReason(@StopReason int reason, int internalStopReason, String debugStopReason) { mStopReason = reason; - mLegacyStopReason = legacyStopReason; + mInternalStopReason = internalStopReason; this.debugStopReason = debugStopReason; } @@ -543,7 +563,7 @@ public class JobParameters implements Parcelable { dest.writeInt(0); } dest.writeInt(mStopReason); - dest.writeInt(mLegacyStopReason); + dest.writeInt(mInternalStopReason); dest.writeString(debugStopReason); } diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java index 50f9a58bbe61..f96fc835b064 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java @@ -231,6 +231,11 @@ public class PowerExemptionManager { * @hide */ public static final int REASON_LOCKED_BOOT_COMPLETED = 202; + /** + * All Bluetooth broadcasts. + * @hide + */ + public static final int REASON_BLUETOOTH_BROADCAST = 203; /* Reason code range 300-399 are reserved for other internal reasons */ /** @@ -363,6 +368,7 @@ public class PowerExemptionManager { REASON_BOOT_COMPLETED, REASON_PRE_BOOT_COMPLETED, REASON_LOCKED_BOOT_COMPLETED, + REASON_BLUETOOTH_BROADCAST, REASON_SYSTEM_ALLOW_LISTED, REASON_ALARM_MANAGER_ALARM_CLOCK, REASON_ALARM_MANAGER_WHILE_IDLE, @@ -633,6 +639,8 @@ public class PowerExemptionManager { return "PRE_BOOT_COMPLETED"; case REASON_LOCKED_BOOT_COMPLETED: return "LOCKED_BOOT_COMPLETED"; + case REASON_BLUETOOTH_BROADCAST: + return "BLUETOOTH_BROADCAST"; case REASON_SYSTEM_ALLOW_LISTED: return "SYSTEM_ALLOW_LISTED"; case REASON_ALARM_MANAGER_ALARM_CLOCK: diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 03d9a968f790..e63a7c41ea98 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -59,7 +59,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.PermissionChecker; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.BatteryManager; @@ -123,6 +123,7 @@ import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.SystemServiceManager; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.usage.AppStandbyInternal; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; @@ -198,9 +199,13 @@ public class AlarmManagerService extends SystemService { private UsageStatsManagerInternal mUsageStatsManagerInternal; private ActivityManagerInternal mActivityManagerInternal; private PackageManagerInternal mPackageManagerInternal; + private PermissionManagerServiceInternal mLocalPermissionManager; final Object mLock = new Object(); + /** Immutable set of app ids that have requested SCHEDULE_EXACT_ALARM permission.*/ + @VisibleForTesting + volatile Set<Integer> mExactAlarmCandidates = Collections.emptySet(); // List of alarms per uid deferred due to user applied background restrictions on the source app SparseArray<ArrayList<Alarm>> mPendingBackgroundAlarms = new SparseArray<>(); private long mNextWakeup; @@ -1540,6 +1545,21 @@ public class AlarmManagerService extends SystemService { publishBinderService(Context.ALARM_SERVICE, mService); } + void refreshExactAlarmCandidates() { + final String[] candidates = mLocalPermissionManager.getAppOpPermissionPackages( + Manifest.permission.SCHEDULE_EXACT_ALARM); + final Set<Integer> appIds = new ArraySet<>(candidates.length); + for (final String candidate : candidates) { + final int uid = mPackageManagerInternal.getPackageUid(candidate, + PackageManager.MATCH_ANY_USER, USER_SYSTEM); + if (uid > 0) { + appIds.add(UserHandle.getAppId(uid)); + } + } + // No need to lock. Assignment is always atomic. + mExactAlarmCandidates = Collections.unmodifiableSet(appIds); + } + @Override public void onBootPhase(int phase) { if (phase == PHASE_SYSTEM_SERVICES_READY) { @@ -1569,6 +1589,11 @@ public class AlarmManagerService extends SystemService { LocalServices.getService(DeviceIdleInternal.class); mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class); + + mLocalPermissionManager = LocalServices.getService( + PermissionManagerServiceInternal.class); + refreshExactAlarmCandidates(); + AppStandbyInternal appStandbyInternal = LocalServices.getService(AppStandbyInternal.class); appStandbyInternal.addListener(new AppStandbyTracker()); @@ -2097,17 +2122,21 @@ public class AlarmManagerService extends SystemService { boolean hasScheduleExactAlarmInternal(String packageName, int uid) { final long start = mStatLogger.getTime(); - // No locking needed as EXACT_ALARM_DENY_LIST is immutable. - final boolean isOnDenyList = mConstants.EXACT_ALARM_DENY_LIST.contains(packageName); - if (isOnDenyList && mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid, - packageName) != AppOpsManager.MODE_ALLOWED) { - return false; + final boolean hasPermission; + // No locking needed as all internal containers being queried are immutable. + if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) { + hasPermission = false; + } else { + final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid, + packageName); + if (mode == AppOpsManager.MODE_DEFAULT) { + hasPermission = !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName); + } else { + hasPermission = (mode == AppOpsManager.MODE_ALLOWED); + } } - final boolean has = PermissionChecker.checkPermissionForPreflight(getContext(), - Manifest.permission.SCHEDULE_EXACT_ALARM, -1, uid, packageName) - == PermissionChecker.PERMISSION_GRANTED; mStatLogger.logDurationStat(Stats.HAS_SCHEDULE_EXACT_ALARM, start); - return has; + return hasPermission; } /** @@ -2490,6 +2519,9 @@ public class AlarmManagerService extends SystemService { pw.println(mNumTimeChanged); pw.println(); + pw.println("App ids requesting SCHEDULE_EXACT_ALARM: " + mExactAlarmCandidates); + + pw.println(); pw.println("Next alarm clock information: "); pw.increaseIndent(); final TreeSet<Integer> users = new TreeSet<>(); @@ -3924,6 +3956,7 @@ public class AlarmManagerService extends SystemService { public static final int REMOVE_FOR_CANCELED = 7; public static final int REMOVE_EXACT_ALARMS = 8; public static final int EXACT_ALARM_DENY_LIST_CHANGED = 9; + public static final int REFRESH_EXACT_ALARM_CANDIDATES = 10; AlarmHandler() { super(Looper.myLooper()); @@ -4015,6 +4048,9 @@ public class AlarmManagerService extends SystemService { handlePackagesAddedToExactAlarmsDenyListLocked((ArraySet<String>) msg.obj); } break; + case REFRESH_EXACT_ALARM_CANDIDATES: + refreshExactAlarmCandidates(); + break; default: // nope, just ignore it break; @@ -4135,6 +4171,7 @@ public class AlarmManagerService extends SystemService { public UninstallReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addAction(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); filter.addDataScheme(IntentFilter.SCHEME_PACKAGE); @@ -4179,8 +4216,11 @@ public class AlarmManagerService extends SystemService { case Intent.ACTION_PACKAGE_REMOVED: if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { // This package is being updated; don't kill its alarms. + // We will refresh the exact alarm candidates on subsequent receipt of + // PACKAGE_ADDED. return; } + mHandler.sendEmptyMessage(AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES); // Intentional fall-through. case Intent.ACTION_PACKAGE_RESTARTED: final Uri data = intent.getData(); @@ -4191,6 +4231,9 @@ public class AlarmManagerService extends SystemService { } } break; + case Intent.ACTION_PACKAGE_ADDED: + mHandler.sendEmptyMessage(AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES); + return; } if (pkgList != null && (pkgList.length > 0)) { for (String pkg : pkgList) { 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 e60a5d189ea6..5a13a849a352 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -695,7 +695,7 @@ class JobConcurrencyManager { // preferredUid will be set to uid of currently running job. activeServices.get(i).cancelExecutingJobLocked( preemptReasonCodeForContext[i], - JobParameters.REASON_PREEMPT, preemptReasonForContext[i]); + JobParameters.INTERNAL_STOP_REASON_PREEMPT, preemptReasonForContext[i]); // Only preserve the UID if we're preempting for the same UID. If we're stopping // the job because something is pending (eg. EJs), then we shouldn't preserve // the UID. @@ -727,7 +727,7 @@ class JobConcurrencyManager { if (jobStatus != null && !jsc.isWithinExecutionGuaranteeTime()) { jsc.cancelExecutingJobLocked(JobParameters.STOP_REASON_DEVICE_STATE, - JobParameters.REASON_TIMEOUT, debugReason); + JobParameters.INTERNAL_STOP_REASON_TIMEOUT, debugReason); } } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java index 02f912919878..1f0900d53e7f 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java @@ -374,7 +374,7 @@ public final class JobPackageTracker { pw.print(pe.stopReasons.valueAt(k)); pw.print("x "); pw.print(JobParameters - .getLegacyReasonCodeDescription(pe.stopReasons.keyAt(k))); + .getInternalReasonCodeDescription(pe.stopReasons.keyAt(k))); } pw.println(); } @@ -621,7 +621,7 @@ public final class JobPackageTracker { if (reason != null) { pw.print(mEventReasons[index]); } else { - pw.print(JobParameters.getLegacyReasonCodeDescription( + pw.print(JobParameters.getInternalReasonCodeDescription( (mEventCmds[index] & EVENT_STOP_REASON_MASK) >> EVENT_STOP_REASON_SHIFT)); } 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 1eebae83bc13..313cd208f7fb 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -758,7 +758,7 @@ public class JobSchedulerService extends com.android.server.SystemService // put USER instead of UNINSTALL or DISABLED. cancelJobsForPackageAndUidLocked(pkgName, pkgUid, JobParameters.STOP_REASON_USER, - JobParameters.DEBUG_REASON_UNINSTALL, + JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app disabled"); } } @@ -809,7 +809,7 @@ public class JobSchedulerService extends com.android.server.SystemService // be fine to just put USER instead of UNINSTALL or DISABLED. cancelJobsForPackageAndUidLocked(pkgName, uidRemoved, JobParameters.STOP_REASON_USER, - JobParameters.DEBUG_REASON_UNINSTALL, "app uninstalled"); + JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled"); for (int c = 0; c < mControllers.size(); ++c) { mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid); } @@ -863,7 +863,8 @@ public class JobSchedulerService extends com.android.server.SystemService } synchronized (mLock) { cancelJobsForPackageAndUidLocked(pkgName, pkgUid, - JobParameters.STOP_REASON_USER, JobParameters.REASON_CANCELED, + JobParameters.STOP_REASON_USER, + JobParameters.INTERNAL_STOP_REASON_CANCELED, "app force stopped"); } } @@ -1074,7 +1075,7 @@ public class JobSchedulerService extends com.android.server.SystemService if (toCancel != null) { // Implicitly replaces the existing job record with the new instance cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP, - JobParameters.REASON_CANCELED, "job rescheduled by app"); + JobParameters.INTERNAL_STOP_REASON_CANCELED, "job rescheduled by app"); } else { startTrackingJobLocked(jobStatus, null); } @@ -1155,7 +1156,7 @@ public class JobSchedulerService extends com.android.server.SystemService // but since this is a user-initiated action, it should be fine to just put USER // instead of UNINSTALL or DISABLED. cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER, - JobParameters.DEBUG_REASON_UNINSTALL, "user removed"); + JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed"); } } @@ -1167,7 +1168,7 @@ public class JobSchedulerService extends com.android.server.SystemService } private void cancelJobsForPackageAndUidLocked(String pkgName, int uid, - @JobParameters.StopReason int reason, int debugReasonCode, String debugReason) { + @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { if ("android".equals(pkgName)) { Slog.wtfStack(TAG, "Can't cancel all jobs for system package"); return; @@ -1176,7 +1177,7 @@ public class JobSchedulerService extends com.android.server.SystemService for (int i = jobsForUid.size() - 1; i >= 0; i--) { final JobStatus job = jobsForUid.get(i); if (job.getSourcePackageName().equals(pkgName)) { - cancelJobImplLocked(job, null, reason, debugReasonCode, debugReason); + cancelJobImplLocked(job, null, reason, internalReasonCode, debugReason); } } } @@ -1189,7 +1190,7 @@ public class JobSchedulerService extends com.android.server.SystemService * @param uid Uid to check against for removal of a job. */ public boolean cancelJobsForUid(int uid, @JobParameters.StopReason int reason, - int debugReasonCode, String debugReason) { + int internalReasonCode, String debugReason) { if (uid == Process.SYSTEM_UID) { Slog.wtfStack(TAG, "Can't cancel all jobs for system uid"); return false; @@ -1200,7 +1201,7 @@ public class JobSchedulerService extends com.android.server.SystemService final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid); for (int i = 0; i < jobsForUid.size(); i++) { JobStatus toRemove = jobsForUid.get(i); - cancelJobImplLocked(toRemove, null, reason, debugReasonCode, debugReason); + cancelJobImplLocked(toRemove, null, reason, internalReasonCode, debugReason); jobsCanceled = true; } } @@ -1222,7 +1223,7 @@ public class JobSchedulerService extends com.android.server.SystemService toCancel = mJobs.getJobByUidAndJobId(uid, jobId); if (toCancel != null) { cancelJobImplLocked(toCancel, null, reason, - JobParameters.REASON_CANCELED, + JobParameters.INTERNAL_STOP_REASON_CANCELED, "cancel() called by app, callingUid=" + callingUid + " uid=" + uid + " jobId=" + jobId); } @@ -1237,7 +1238,7 @@ public class JobSchedulerService extends com.android.server.SystemService * currently scheduled jobs. */ private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, - @JobParameters.StopReason int reason, int debugReasonCode, String debugReason) { + @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString()); cancelled.unprepareLocked(); stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */); @@ -1246,7 +1247,7 @@ public class JobSchedulerService extends com.android.server.SystemService mJobPackageTracker.noteNonpending(cancelled); } // Cancel if running. - stopJobOnServiceContextLocked(cancelled, reason, debugReasonCode, debugReason); + stopJobOnServiceContextLocked(cancelled, reason, internalReasonCode, debugReason); // If this is a replacement, bring in the new version of the job if (incomingJob != null) { if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString()); @@ -1297,7 +1298,7 @@ public class JobSchedulerService extends com.android.server.SystemService final JobStatus executing = jsc.getRunningJobLocked(); if (executing != null && !executing.canRunInDoze()) { jsc.cancelExecutingJobLocked(JobParameters.STOP_REASON_DEVICE_STATE, - JobParameters.REASON_DEVICE_IDLE, + JobParameters.INTERNAL_STOP_REASON_DEVICE_IDLE, "cancelled due to doze"); } } @@ -1496,7 +1497,7 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.v(TAG, " replacing " + oldJob + " with " + newJob); } cancelJobImplLocked(oldJob, newJob, JobParameters.STOP_REASON_SYSTEM_PROCESSING, - JobParameters.REASON_CANCELED, "deferred rtc calculation"); + JobParameters.INTERNAL_STOP_REASON_RTC_UPDATED, "deferred rtc calculation"); } } }; @@ -1618,12 +1619,12 @@ public class JobSchedulerService extends com.android.server.SystemService } private boolean stopJobOnServiceContextLocked(JobStatus job, - @JobParameters.StopReason int reason, int legacyReason, String debugReason) { - for (int i=0; i<mActiveServices.size(); i++) { + @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { + for (int i = 0; i < mActiveServices.size(); i++) { JobServiceContext jsc = mActiveServices.get(i); final JobStatus executing = jsc.getRunningJobLocked(); if (executing != null && executing.matches(job.getUid(), job.getJobId())) { - jsc.cancelExecutingJobLocked(reason, legacyReason, debugReason); + jsc.cancelExecutingJobLocked(reason, internalReasonCode, debugReason); return true; } } @@ -1819,8 +1820,8 @@ public class JobSchedulerService extends com.android.server.SystemService mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis(); mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY; - if (debugStopReason == JobParameters.DEBUG_REASON_UNINSTALL - || debugStopReason == JobParameters.DEBUG_REASON_DATA_CLEARED) { + if (debugStopReason == JobParameters.INTERNAL_STOP_REASON_UNINSTALL + || debugStopReason == JobParameters.INTERNAL_STOP_REASON_DATA_CLEARED) { // The job should have already been cleared from the rest of the JS tracking. No need // to go through all that flow again. jobStatus.unprepareLocked(); @@ -1838,8 +1839,8 @@ public class JobSchedulerService extends com.android.server.SystemService final JobStatus rescheduledJob = needsReschedule ? getRescheduleJobForFailureLocked(jobStatus) : null; if (rescheduledJob != null - && (debugStopReason == JobParameters.REASON_TIMEOUT - || debugStopReason == JobParameters.REASON_PREEMPT)) { + && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT + || debugStopReason == JobParameters.INTERNAL_STOP_REASON_PREEMPT)) { rescheduledJob.disallowRunInBatterySaverAndDoze(); } @@ -1944,7 +1945,7 @@ public class JobSchedulerService extends com.android.server.SystemService break; case MSG_STOP_JOB: cancelJobImplLocked((JobStatus) message.obj, null, message.arg1, - JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, + JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED, "app no longer allowed to run"); break; @@ -1961,7 +1962,8 @@ public class JobSchedulerService extends com.android.server.SystemService if (disabled) { cancelJobsForUid(uid, JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, - JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, "uid gone"); + JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED, + "uid gone"); } synchronized (mLock) { mDeviceIdleJobsController.setUidActiveLocked(uid, false); @@ -1981,7 +1983,8 @@ public class JobSchedulerService extends com.android.server.SystemService if (disabled) { cancelJobsForUid(uid, JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, - JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, "app uid idle"); + JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED, + "app uid idle"); } synchronized (mLock) { mDeviceIdleJobsController.setUidActiveLocked(uid, false); @@ -2034,22 +2037,23 @@ public class JobSchedulerService extends com.android.server.SystemService && !running.areDynamicConstraintsSatisfied()) { serviceContext.cancelExecutingJobLocked( running.getStopReason(), - JobParameters.REASON_RESTRICTED_BUCKET, + JobParameters.INTERNAL_STOP_REASON_RESTRICTED_BUCKET, "cancelled due to restricted bucket"); } else { serviceContext.cancelExecutingJobLocked( running.getStopReason(), - JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, + JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED, "cancelled due to unsatisfied constraints"); } } else { final JobRestriction restriction = checkIfRestricted(running); if (restriction != null) { - final int reason = restriction.getReason(); - serviceContext.cancelExecutingJobLocked(reason, - restriction.getLegacyReason(), - "restricted due to " + JobParameters.getLegacyReasonCodeDescription( - reason)); + final int internalReasonCode = restriction.getInternalReason(); + serviceContext.cancelExecutingJobLocked(restriction.getReason(), + internalReasonCode, + "restricted due to " + + JobParameters.getInternalReasonCodeDescription( + internalReasonCode)); } } } @@ -2354,7 +2358,7 @@ public class JobSchedulerService extends com.android.server.SystemService if (restriction != null) { if (DEBUG) { Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString() - + " restricted due to " + restriction.getLegacyReason()); + + " restricted due to " + restriction.getInternalReason()); } return false; } @@ -2446,8 +2450,8 @@ public class JobSchedulerService extends com.android.server.SystemService @Override public void cancelJobsForUid(int uid, @JobParameters.StopReason int reason, - int debugReasonCode, String debugReason) { - JobSchedulerService.this.cancelJobsForUid(uid, reason, debugReasonCode, debugReason); + int internalReasonCode, String debugReason) { + JobSchedulerService.this.cancelJobsForUid(uid, reason, internalReasonCode, debugReason); } @Override @@ -2786,7 +2790,7 @@ public class JobSchedulerService extends com.android.server.SystemService try { JobSchedulerService.this.cancelJobsForUid(uid, JobParameters.STOP_REASON_CANCELLED_BY_APP, - JobParameters.REASON_CANCELED, + JobParameters.INTERNAL_STOP_REASON_CANCELED, "cancelAll() called by app, callingUid=" + uid); } finally { Binder.restoreCallingIdentity(ident); @@ -3007,7 +3011,8 @@ public class JobSchedulerService extends com.android.server.SystemService if (!hasJobId) { pw.println("Canceling all jobs for " + pkgName + " in user " + userId); if (!cancelJobsForUid(pkgUid, JobParameters.STOP_REASON_USER, - JobParameters.REASON_CANCELED, "cancel shell command for package")) { + JobParameters.INTERNAL_STOP_REASON_CANCELED, + "cancel shell command for package")) { pw.println("No matching jobs found."); } } else { @@ -3247,9 +3252,9 @@ public class JobSchedulerService extends com.android.server.SystemService for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { final JobRestriction restriction = mJobRestrictions.get(i); if (restriction.isJobRestricted(job)) { - final int reason = restriction.getLegacyReason(); + final int reason = restriction.getInternalReason(); pw.print(" "); - pw.print(JobParameters.getLegacyReasonCodeDescription(reason)); + pw.print(JobParameters.getInternalReasonCodeDescription(reason)); } } } else { @@ -3534,7 +3539,7 @@ public class JobSchedulerService extends com.android.server.SystemService final long restrictionsToken = proto.start( JobSchedulerServiceDumpProto.RegisteredJob.RESTRICTIONS); proto.write(JobSchedulerServiceDumpProto.JobRestriction.REASON, - restriction.getLegacyReason()); + restriction.getInternalReason()); proto.write(JobSchedulerServiceDumpProto.JobRestriction.IS_RESTRICTING, restriction.isJobRestricted(job)); proto.end(restrictionsToken); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 44b3e3e19232..6ab2be32f5c3 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -156,7 +156,7 @@ public final class JobServiceContext implements ServiceConnection { * {@link JobParameters#STOP_REASON_UNDEFINED}. */ private int mPendingStopReason = JobParameters.STOP_REASON_UNDEFINED; - private int mPendingLegacyStopReason; + private int mPendingInternalStopReason; private String mPendingDebugStopReason; // Debugging: reason this job was last stopped. @@ -366,8 +366,8 @@ public final class JobServiceContext implements ServiceConnection { /** Called externally when a job that was scheduled for execution should be cancelled. */ @GuardedBy("mLock") void cancelExecutingJobLocked(@JobParameters.StopReason int reason, - int legacyStopReason, @NonNull String debugReason) { - doCancelLocked(reason, legacyStopReason, debugReason); + int internalStopReason, @NonNull String debugReason) { + doCancelLocked(reason, internalStopReason, debugReason); } int getPreferredUid() { @@ -400,7 +400,7 @@ public final class JobServiceContext implements ServiceConnection { && (!matchJobId || jobId == executing.getJobId())) { if (mVerb == VERB_EXECUTING) { mParams.setStopReason(JobParameters.STOP_REASON_TIMEOUT, - JobParameters.REASON_TIMEOUT, reason); + JobParameters.INTERNAL_STOP_REASON_TIMEOUT, reason); sendStopMessageLocked("force timeout from shell"); return true; } @@ -409,7 +409,20 @@ public final class JobServiceContext implements ServiceConnection { } void doJobFinished(JobCallback cb, int jobId, boolean reschedule) { - doCallback(cb, reschedule, "app called jobFinished"); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + if (!verifyCallerLocked(cb)) { + return; + } + mParams.setStopReason(JobParameters.STOP_REASON_UNDEFINED, + JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH, + "app called jobFinished"); + doCallbackLocked(reschedule, "app called jobFinished"); + } + } finally { + Binder.restoreCallingIdentity(ident); + } } void doAcknowledgeStopMessage(JobCallback cb, int jobId, boolean reschedule) { @@ -433,6 +446,9 @@ public final class JobServiceContext implements ServiceConnection { } final JobWorkItem work = mRunningJob.dequeueWorkLocked(); if (work == null && !mRunningJob.hasExecutingWorkLocked()) { + mParams.setStopReason(JobParameters.STOP_REASON_UNDEFINED, + JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH, + "last work dequeued"); // This will finish the job. doCallbackLocked(false, "last work dequeued"); } @@ -627,8 +643,8 @@ public final class JobServiceContext implements ServiceConnection { } @GuardedBy("mLock") - private void doCancelLocked(@JobParameters.StopReason int stopReasonCode, int legacyStopReason, - @Nullable String debugReason) { + private void doCancelLocked(@JobParameters.StopReason int stopReasonCode, + int internalStopReasonCode, @Nullable String debugReason) { if (mVerb == VERB_FINISHED) { if (DEBUG) { Slog.d(TAG, @@ -644,15 +660,14 @@ public final class JobServiceContext implements ServiceConnection { final long nowElapsed = sElapsedRealtimeClock.millis(); if (nowElapsed < earliestStopTimeElapsed) { mPendingStopReason = stopReasonCode; - mPendingLegacyStopReason = legacyStopReason; + mPendingInternalStopReason = internalStopReasonCode; mPendingDebugStopReason = debugReason; return; } } - mParams.setStopReason(stopReasonCode, legacyStopReason, debugReason); - if (legacyStopReason == JobParameters.REASON_PREEMPT) { - mPreferredUid = mRunningJob != null ? mRunningJob.getUid() : - NO_PREFERRED_UID; + mParams.setStopReason(stopReasonCode, internalStopReasonCode, debugReason); + if (internalStopReasonCode == JobParameters.INTERNAL_STOP_REASON_PREEMPT) { + mPreferredUid = mRunningJob != null ? mRunningJob.getUid() : NO_PREFERRED_UID; } handleCancelLocked(debugReason); } @@ -807,12 +822,12 @@ public final class JobServiceContext implements ServiceConnection { // the device was temporarily taken off the charger). Ignore the pending // stop and see what the manager says. mPendingStopReason = JobParameters.STOP_REASON_UNDEFINED; - mPendingLegacyStopReason = 0; + mPendingInternalStopReason = 0; mPendingDebugStopReason = null; } else { Slog.i(TAG, "JS was waiting to stop this job." + " Sending onStop: " + getRunningJobNameLocked()); - mParams.setStopReason(mPendingStopReason, mPendingLegacyStopReason, + mParams.setStopReason(mPendingStopReason, mPendingInternalStopReason, mPendingDebugStopReason); sendStopMessageLocked(mPendingDebugStopReason); break; @@ -826,7 +841,7 @@ public final class JobServiceContext implements ServiceConnection { Slog.i(TAG, "Client timed out while executing (no jobFinished received)." + " Sending onStop: " + getRunningJobNameLocked()); mParams.setStopReason(JobParameters.STOP_REASON_TIMEOUT, - JobParameters.REASON_TIMEOUT, "client timed out"); + JobParameters.INTERNAL_STOP_REASON_TIMEOUT, "client timed out"); sendStopMessageLocked("timeout while executing"); } else { // We've given the app the minimum execution time. See if we should stop it or @@ -839,7 +854,7 @@ public final class JobServiceContext implements ServiceConnection { // of timeout since all of the reasons could equate to "the system needs // the resources the app is currently using." mParams.setStopReason(JobParameters.STOP_REASON_DEVICE_STATE, - JobParameters.REASON_TIMEOUT, reason); + JobParameters.INTERNAL_STOP_REASON_TIMEOUT, reason); sendStopMessageLocked(reason); } else { Slog.i(TAG, "Letting " + getRunningJobNameLocked() @@ -893,12 +908,12 @@ public final class JobServiceContext implements ServiceConnection { } applyStoppedReasonLocked(reason); completedJob = mRunningJob; - final int legacyStopReason = mParams.getLegacyStopReason(); - mJobPackageTracker.noteInactive(completedJob, legacyStopReason, reason); + final int internalStopReason = mParams.getInternalStopReasonCode(); + mJobPackageTracker.noteInactive(completedJob, internalStopReason, reason); FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, completedJob.getSourceUid(), null, completedJob.getBatteryName(), FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED, - legacyStopReason, completedJob.getStandbyBucket(), completedJob.getJobId(), + internalStopReason, completedJob.getStandbyBucket(), completedJob.getJobId(), completedJob.hasChargingConstraint(), completedJob.hasBatteryNotLowConstraint(), completedJob.hasStorageNotLowConstraint(), @@ -911,7 +926,7 @@ public final class JobServiceContext implements ServiceConnection { completedJob.startedAsExpeditedJob); try { mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(), - legacyStopReason); + internalStopReason); } catch (RemoteException e) { // Whatever. } @@ -930,10 +945,10 @@ public final class JobServiceContext implements ServiceConnection { service = null; mAvailable = true; mPendingStopReason = JobParameters.STOP_REASON_UNDEFINED; - mPendingLegacyStopReason = 0; + mPendingInternalStopReason = 0; mPendingDebugStopReason = null; removeOpTimeOutLocked(); - mCompletedListener.onJobCompletedLocked(completedJob, legacyStopReason, reschedule); + mCompletedListener.onJobCompletedLocked(completedJob, internalStopReason, reschedule); mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType); } @@ -1023,7 +1038,7 @@ public final class JobServiceContext implements ServiceConnection { pw.print(" Pending stop because "); pw.print(mPendingStopReason); pw.print("/"); - pw.print(mPendingLegacyStopReason); + pw.print(mPendingInternalStopReason); pw.print("/"); pw.print(mPendingDebugStopReason); } 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 2bdf65643d97..7b947fd3ab43 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 @@ -697,7 +697,7 @@ public final class ConnectivityController extends RestrictingController implemen } callback.setUid(uidStats.uid); mCurrentDefaultNetworkCallbacks.append(uidStats.uid, callback); - mConnManager.registerDefaultNetworkCallbackAsUid(uidStats.uid, callback, mHandler); + mConnManager.registerDefaultNetworkCallbackForUid(uidStats.uid, callback, mHandler); } } @@ -794,7 +794,7 @@ public final class ConnectivityController extends RestrictingController implemen } defaultNetworkCallback.setUid(us.uid); mCurrentDefaultNetworkCallbacks.append(us.uid, defaultNetworkCallback); - mConnManager.registerDefaultNetworkCallbackAsUid( + mConnManager.registerDefaultNetworkCallbackForUid( us.uid, defaultNetworkCallback, mHandler); } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java index 2962b1017315..20df3ea2196b 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java +++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java @@ -28,7 +28,7 @@ import com.android.server.job.controllers.JobStatus; * Used by {@link JobSchedulerService} to impose additional restrictions regarding whether jobs * should be scheduled or not based on the state of the system/device. * Every restriction is associated with exactly one stop reason, which could be retrieved using - * {@link #getReason()} (and the legacy reason via {@link #getLegacyReason()}). + * {@link #getReason()} (and the internal reason via {@link #getInternalReason()}). * Note, that this is not taken into account for the jobs that have priority * {@link JobInfo#PRIORITY_FOREGROUND_APP} or higher. */ @@ -36,13 +36,13 @@ public abstract class JobRestriction { final JobSchedulerService mService; private final int mReason; - private final int mLegacyReason; + private final int mInternalReason; JobRestriction(JobSchedulerService service, @JobParameters.StopReason int reason, - int legacyReason) { + int internalReason) { mService = service; mReason = reason; - mLegacyReason = legacyReason; + mInternalReason = internalReason; } /** @@ -74,7 +74,7 @@ public abstract class JobRestriction { return mReason; } - public final int getLegacyReason() { - return mLegacyReason; + public final int getInternalReason() { + return mInternalReason; } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java index 8b699e9b04c3..3069db32c548 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java +++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java @@ -34,7 +34,8 @@ public class ThermalStatusRestriction extends JobRestriction { private PowerManager mPowerManager; public ThermalStatusRestriction(JobSchedulerService service) { - super(service, JobParameters.STOP_REASON_DEVICE_STATE, JobParameters.REASON_DEVICE_THERMAL); + super(service, JobParameters.STOP_REASON_DEVICE_STATE, + JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); } @Override diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 97ee0e131ef2..ebf4ed0e7cab 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -58,7 +58,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; -import android.app.AppGlobals; import android.app.usage.AppStandbyInfo; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager.StandbyBuckets; @@ -75,7 +74,6 @@ import android.content.pm.CrossProfileAppsInternal; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; -import android.content.pm.ParceledListSlice; import android.database.ContentObserver; import android.hardware.display.DisplayManager; import android.net.NetworkScoreManager; @@ -101,7 +99,7 @@ import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseIntArray; +import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.view.Display; import android.widget.Toast; @@ -118,6 +116,8 @@ import com.android.server.LocalServices; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.usage.AppIdleHistory.AppUsageHistory; +import libcore.util.EmptyArray; + import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; @@ -1249,71 +1249,55 @@ public class AppStandbyController @Override public int[] getIdleUidsForUser(int userId) { if (!mAppIdleEnabled) { - return new int[0]; + return EmptyArray.INT; } Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "getIdleUidsForUser"); final long elapsedRealtime = mInjector.elapsedRealtime(); - List<ApplicationInfo> apps; - try { - ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager() - .getInstalledApplications(/* flags= */ 0, userId); - if (slice == null) { - return new int[0]; - } - apps = slice.getList(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + final PackageManagerInternal pmi = mInjector.getPackageManagerInternal(); + final List<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, Process.myUid()); + if (apps == null) { + return EmptyArray.INT; } - // State of each uid. Key is the uid. Value lower 16 bits is the number of apps - // associated with that uid, upper 16 bits is the number of those apps that is idle. - SparseIntArray uidStates = new SparseIntArray(); - - // Now resolve all app state. Iterating over all apps, keeping track of how many - // we find for each uid and how many of those are idle. + // State of each uid: Key is the uid, value is whether all the apps in that uid are idle. + final SparseBooleanArray uidIdleStates = new SparseBooleanArray(); + int notIdleCount = 0; for (int i = apps.size() - 1; i >= 0; i--) { - ApplicationInfo ai = apps.get(i); + final ApplicationInfo ai = apps.get(i); + final int index = uidIdleStates.indexOfKey(ai.uid); - // Check whether this app is idle. - boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid), - userId, elapsedRealtime); + final boolean currentIdle = (index < 0) ? true : uidIdleStates.valueAt(index); + + final boolean newIdle = currentIdle && isAppIdleFiltered(ai.packageName, + UserHandle.getAppId(ai.uid), userId, elapsedRealtime); - int index = uidStates.indexOfKey(ai.uid); + if (currentIdle && !newIdle) { + // This transition from true to false can happen at most once per uid in this loop. + notIdleCount++; + } if (index < 0) { - uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0)); + uidIdleStates.put(ai.uid, newIdle); } else { - int value = uidStates.valueAt(index); - uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0)); + uidIdleStates.setValueAt(index, newIdle); } } - if (DEBUG) { - Slog.d(TAG, "getIdleUids took " + (mInjector.elapsedRealtime() - elapsedRealtime)); - } - int numIdle = 0; - for (int i = uidStates.size() - 1; i >= 0; i--) { - int value = uidStates.valueAt(i); - if ((value&0x7fff) == (value>>16)) { - numIdle++; + int numIdleUids = uidIdleStates.size() - notIdleCount; + final int[] idleUids = new int[numIdleUids]; + for (int i = uidIdleStates.size() - 1; i >= 0; i--) { + if (uidIdleStates.valueAt(i)) { + idleUids[--numIdleUids] = uidIdleStates.keyAt(i); } } - - int[] res = new int[numIdle]; - numIdle = 0; - for (int i = uidStates.size() - 1; i >= 0; i--) { - int value = uidStates.valueAt(i); - if ((value&0x7fff) == (value>>16)) { - res[numIdle] = uidStates.keyAt(i); - numIdle++; - } + if (DEBUG) { + Slog.d(TAG, "getIdleUids took " + (mInjector.elapsedRealtime() - elapsedRealtime)); } - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - return res; + return idleUids; } @Override diff --git a/cmds/bootanimation/BootAnimationUtil.cpp b/cmds/bootanimation/BootAnimationUtil.cpp index 1e417e938359..4f56e5a88c4c 100644 --- a/cmds/bootanimation/BootAnimationUtil.cpp +++ b/cmds/bootanimation/BootAnimationUtil.cpp @@ -49,7 +49,14 @@ bool bootAnimationDisabled() { } property_get("ro.boot.quiescent", value, "0"); - return atoi(value) > 0; + if (atoi(value) > 0) { + // Only show the bootanimation for quiescent boots if this system property is set to enabled + if (!property_get_bool("ro.bootanim.quiescent.enabled", false)) { + return true; + } + } + + return false; } void waitForSurfaceFlinger() { diff --git a/core/api/current.txt b/core/api/current.txt index 499eb9bd7db3..4cdc519f35ab 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9,6 +9,7 @@ package android { ctor public Manifest.permission(); field public static final String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER"; field public static final String ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION"; + field public static final String ACCESS_BLOBS_ACROSS_USERS = "android.permission.ACCESS_BLOBS_ACROSS_USERS"; field public static final String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES"; field public static final String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION"; field public static final String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION"; @@ -8633,7 +8634,7 @@ package android.bluetooth { method public android.bluetooth.le.BluetoothLeAdvertiser getBluetoothLeAdvertiser(); method public android.bluetooth.le.BluetoothLeScanner getBluetoothLeScanner(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices(); - method public static android.bluetooth.BluetoothAdapter getDefaultAdapter(); + method @Deprecated public static android.bluetooth.BluetoothAdapter getDefaultAdapter(); method public int getLeMaximumAdvertisingDataLength(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getProfileConnectionState(int); @@ -18046,7 +18047,7 @@ package android.hardware.camera2 { } public final class CameraExtensionCharacteristics { - method @Nullable public android.util.Range<java.lang.Long> getEstimatedCaptureLatencyRange(int, @NonNull android.util.Size, int); + method @Nullable public android.util.Range<java.lang.Long> getEstimatedCaptureLatencyRangeMillis(int, @NonNull android.util.Size, int); method @NonNull public <T> java.util.List<android.util.Size> getExtensionSupportedSizes(int, @NonNull Class<T>); method @NonNull public java.util.List<android.util.Size> getExtensionSupportedSizes(int, int); method @NonNull public java.util.List<java.lang.Integer> getSupportedExtensions(); @@ -52812,6 +52813,7 @@ package android.view.translation { field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationCapability> CREATOR; field public static final int STATE_AVAILABLE_TO_DOWNLOAD = 1; // 0x1 field public static final int STATE_DOWNLOADING = 2; // 0x2 + field public static final int STATE_NOT_AVAILABLE = 4; // 0x4 field public static final int STATE_ON_DEVICE = 3; // 0x3 } @@ -52834,7 +52836,8 @@ package android.view.translation { } public final class TranslationManager { - method public void addOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); + method public void addOnDeviceTranslationCapabilityUpdateListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.translation.TranslationCapability>); + method @Deprecated public void addOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); method @Deprecated public void addTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); method @Nullable @WorkerThread public android.view.translation.Translator createOnDeviceTranslator(@NonNull android.view.translation.TranslationContext); method @Deprecated @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationContext); @@ -52842,7 +52845,8 @@ package android.view.translation { method @Nullable public android.app.PendingIntent getOnDeviceTranslationSettingsActivityIntent(); method @Deprecated @NonNull @WorkerThread public java.util.Set<android.view.translation.TranslationCapability> getTranslationCapabilities(int, int); method @Deprecated @Nullable public android.app.PendingIntent getTranslationSettingsActivityIntent(); - method public void removeOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); + method public void removeOnDeviceTranslationCapabilityUpdateListener(@NonNull java.util.function.Consumer<android.view.translation.TranslationCapability>); + method @Deprecated public void removeOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); method @Deprecated public void removeTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); } @@ -56791,6 +56795,7 @@ package android.window { public interface SplashScreen { method public void clearOnExitAnimationListener(); method public void setOnExitAnimationListener(@NonNull android.window.SplashScreen.OnExitAnimationListener); + method public void setSplashScreenTheme(@StyleRes int); } public static interface SplashScreen.OnExitAnimationListener { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index d1cc864493d4..fede5a5f505a 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3,7 +3,6 @@ package android { public static final class Manifest.permission { field public static final String ACCESS_AMBIENT_LIGHT_STATS = "android.permission.ACCESS_AMBIENT_LIGHT_STATS"; - field public static final String ACCESS_BLOBS_ACROSS_USERS = "android.permission.ACCESS_BLOBS_ACROSS_USERS"; field public static final String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO"; field public static final String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM"; field public static final String ACCESS_CONTEXT_HUB = "android.permission.ACCESS_CONTEXT_HUB"; @@ -1901,10 +1900,10 @@ package android.apphibernation { package android.bluetooth { public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile { - method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public android.bluetooth.BufferConstraints getBufferConstraints(); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getDynamicBufferSupport(); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferLengthMillis(int, int); + method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BufferConstraints getBufferConstraints(); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getDynamicBufferSupport(); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setBufferLengthMillis(int, int); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1; // 0x1 field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; // 0x2 @@ -1919,8 +1918,8 @@ package android.bluetooth { public final class BluetoothA2dpSink implements android.bluetooth.BluetoothProfile { method public void finalize(); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isAudioPlaying(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isAudioPlaying(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; } @@ -2008,13 +2007,13 @@ package android.bluetooth { public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile { method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean connect(android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(android.bluetooth.BluetoothDevice); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); } public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile { - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public long getHiSyncId(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public long getHiSyncId(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); } @@ -2024,9 +2023,9 @@ package android.bluetooth { public final class BluetoothHidHost implements android.bluetooth.BluetoothProfile { method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; } @@ -2045,7 +2044,7 @@ package android.bluetooth { public final class BluetoothPan implements android.bluetooth.BluetoothProfile { method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isTetheringOn(); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.TETHER_PRIVILEGED}) public void setBluetoothTethering(boolean); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); @@ -2063,7 +2062,7 @@ package android.bluetooth { } public class BluetoothPbap implements android.bluetooth.BluetoothProfile { - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; } @@ -10407,6 +10406,7 @@ package android.service.translation { method public abstract void onFinishTranslationSession(int); method public abstract void onTranslationCapabilitiesRequest(int, int, @NonNull java.util.function.Consumer<java.util.Set<android.view.translation.TranslationCapability>>); method public abstract void onTranslationRequest(@NonNull android.view.translation.TranslationRequest, int, @Nullable android.os.CancellationSignal, @NonNull android.service.translation.TranslationService.OnTranslationResultCallback); + method public final void updateTranslationCapability(@NonNull android.view.translation.TranslationCapability); field public static final String SERVICE_INTERFACE = "android.service.translation.TranslationService"; field public static final String SERVICE_META_DATA = "android.translation_service"; } @@ -10513,6 +10513,7 @@ package android.service.voice { method public static int getMaxBundleSize(); method public static int getMaxHotwordPhraseId(); method public static int getMaxScore(); + method @Nullable public android.media.MediaSyncEvent getMediaSyncEvent(); method public int getPersonalizedScore(); method public int getScore(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -10527,6 +10528,7 @@ package android.service.voice { method @NonNull public android.service.voice.HotwordDetectedResult.Builder setConfidenceLevel(int); method @NonNull public android.service.voice.HotwordDetectedResult.Builder setExtras(@NonNull android.os.PersistableBundle); method @NonNull public android.service.voice.HotwordDetectedResult.Builder setHotwordPhraseId(int); + method @NonNull public android.service.voice.HotwordDetectedResult.Builder setMediaSyncEvent(@NonNull android.media.MediaSyncEvent); method @NonNull public android.service.voice.HotwordDetectedResult.Builder setPersonalizedScore(int); method @NonNull public android.service.voice.HotwordDetectedResult.Builder setScore(int); } @@ -10534,8 +10536,10 @@ package android.service.voice { public abstract class HotwordDetectionService extends android.app.Service { ctor public HotwordDetectionService(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); - method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, long, @NonNull android.service.voice.HotwordDetectionService.Callback); - method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.service.voice.HotwordDetectionService.Callback); + method @Deprecated public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, long, @NonNull android.service.voice.HotwordDetectionService.Callback); + method public void onDetect(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload, long, @NonNull android.service.voice.HotwordDetectionService.Callback); + method @Deprecated public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.service.voice.HotwordDetectionService.Callback); + method public void onDetect(@NonNull android.service.voice.HotwordDetectionService.Callback); method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle, @NonNull android.service.voice.HotwordDetectionService.Callback); method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer); field public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_1 = 1; // 0x1 @@ -10581,7 +10585,8 @@ package android.service.voice { public class VoiceInteractionService extends android.app.Service { method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, android.service.voice.AlwaysOnHotwordDetector.Callback); - method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull android.service.voice.HotwordDetector.Callback); + method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull android.service.voice.HotwordDetector.Callback); + method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull android.service.voice.HotwordDetector.Callback); method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager(); } @@ -14519,7 +14524,21 @@ package android.view.translation { method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void finishTranslation(@NonNull android.app.assist.ActivityId); method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void pauseTranslation(@NonNull android.app.assist.ActivityId); method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void resumeTranslation(@NonNull android.app.assist.ActivityId); - method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void startTranslation(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.autofill.AutofillId>, @NonNull android.app.assist.ActivityId); + method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void startTranslation(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.autofill.AutofillId>, @NonNull android.app.assist.ActivityId); + method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void startTranslation(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.autofill.AutofillId>, @NonNull android.app.assist.ActivityId, @NonNull android.view.translation.UiTranslationSpec); + } + + public final class UiTranslationSpec implements android.os.Parcelable { + method public int describeContents(); + method public boolean shouldPadContentForCompat(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.UiTranslationSpec> CREATOR; + } + + public static final class UiTranslationSpec.Builder { + ctor public UiTranslationSpec.Builder(); + method @NonNull public android.view.translation.UiTranslationSpec build(); + method @NonNull public android.view.translation.UiTranslationSpec.Builder setShouldPadContentForCompat(boolean); } } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index d8c138759cf0..4e929a8067b0 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2102,6 +2102,7 @@ package android.provider { field public static final String AUTOMATIC_POWER_SAVE_MODE = "automatic_power_save_mode"; field public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants"; field public static final String DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW = "enable_non_resizable_multi_window"; + field public static final String DISABLE_WINDOW_BLURS = "disable_window_blurs"; field public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold"; field public static final String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled"; field public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions"; @@ -2779,7 +2780,6 @@ package android.view { method public default void holdLock(android.os.IBinder, int); method public default boolean isTaskSnapshotSupported(); method public default void setDisplayImePolicy(int, int); - method public default void setForceCrossWindowBlurDisabled(boolean); method public default void setShouldShowSystemDecors(int, boolean); method public default void setShouldShowWithInsecureKeyguard(int, boolean); method public default boolean shouldShowSystemDecors(int); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 6e0b83f7e33c..59f794b2bf36 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5968,20 +5968,6 @@ public final class ActivityThread extends ClientTransactionHandler // Update all affected Resources objects to use new ResourcesImpl mResourcesManager.applyNewResourceDirsLocked(ai, oldResDirs); } - - ApplicationPackageManager.configurationChanged(); - - // Trigger a regular Configuration change event, only with a different assetsSeq number - // so that we actually call through to all components. - // TODO(adamlesinski): Change this to make use of ActivityManager's upcoming ability to - // store configurations per-process. - final Configuration config = mConfigurationController.getConfiguration(); - Configuration newConfig = new Configuration(); - newConfig.assetsSeq = (config != null ? config.assetsSeq : 0) + 1; - mConfigurationController.handleConfigurationChanged(newConfig, null /* compat */); - - // Preserve windows to avoid black flickers when overlays change. - relaunchAllActivities(true /* preserveWindows */, "handleApplicationInfoChanged"); } /** diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 9753b6748c78..656942d315cc 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1218,6 +1218,23 @@ class ContextImpl extends Context { } @Override + public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions, + Bundle options) { + warnIfCallingFromSystemProcess(); + String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); + try { + intent.prepareToLeaveProcess(this); + ActivityManager.getService().broadcastIntentWithFeature( + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, receiverPermissions, + null /*excludedPermissions=*/, AppOpsManager.OP_NONE, options, false, false, + getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user, String[] receiverPermissions) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 1765849c8314..232b077538ef 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -74,6 +74,9 @@ public class StatusBarManager { public static final int DISABLE_SEARCH = View.STATUS_BAR_DISABLE_SEARCH; /** @hide */ + public static final int DISABLE_ONGOING_CALL_CHIP = View.STATUS_BAR_DISABLE_ONGOING_CALL_CHIP; + + /** @hide */ @Deprecated public static final int DISABLE_NAVIGATION = View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT; @@ -85,7 +88,7 @@ public class StatusBarManager { public static final int DISABLE_MASK = DISABLE_EXPAND | DISABLE_NOTIFICATION_ICONS | DISABLE_NOTIFICATION_ALERTS | DISABLE_NOTIFICATION_TICKER | DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK - | DISABLE_SEARCH; + | DISABLE_SEARCH | DISABLE_ONGOING_CALL_CHIP; /** @hide */ @IntDef(flag = true, prefix = {"DISABLE_"}, value = { @@ -99,7 +102,8 @@ public class StatusBarManager { DISABLE_RECENT, DISABLE_BACK, DISABLE_CLOCK, - DISABLE_SEARCH + DISABLE_SEARCH, + DISABLE_ONGOING_CALL_CHIP }) @Retention(RetentionPolicy.SOURCE) public @interface DisableFlags {} diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java index 759597ce36dc..4c1a36340d5d 100644 --- a/core/java/android/app/admin/PasswordMetrics.java +++ b/core/java/android/app/admin/PasswordMetrics.java @@ -531,7 +531,7 @@ public final class PasswordMetrics implements Parcelable { } final PasswordMetrics enteredMetrics = computeForPasswordOrPin(password, isPin); - return validatePasswordMetrics(adminMetrics, minComplexity, isPin, enteredMetrics); + return validatePasswordMetrics(adminMetrics, minComplexity, enteredMetrics); } /** @@ -539,15 +539,13 @@ public final class PasswordMetrics implements Parcelable { * * @param adminMetrics - minimum metrics to satisfy admin requirements. * @param minComplexity - minimum complexity imposed by the requester. - * @param isPin - whether it is PIN that should be only digits * @param actualMetrics - metrics for password to validate. * @return a list of password validation errors. An empty list means the password is OK. * * TODO: move to PasswordPolicy */ public static List<PasswordValidationError> validatePasswordMetrics( - PasswordMetrics adminMetrics, int minComplexity, boolean isPin, - PasswordMetrics actualMetrics) { + PasswordMetrics adminMetrics, int minComplexity, PasswordMetrics actualMetrics) { final ComplexityBucket bucket = ComplexityBucket.forComplexity(minComplexity); // Make sure credential type is satisfactory. @@ -561,7 +559,7 @@ public final class PasswordMetrics implements Parcelable { return Collections.emptyList(); // Nothing to check for pattern or none. } - if (isPin && actualMetrics.nonNumeric > 0) { + if (actualMetrics.credType == CREDENTIAL_TYPE_PIN && actualMetrics.nonNumeric > 0) { return Collections.singletonList( new PasswordValidationError(CONTAINS_INVALID_CHARACTERS, 0)); } diff --git a/core/java/android/app/people/ConversationStatus.java b/core/java/android/app/people/ConversationStatus.java index d351683386e2..8038158b1f97 100644 --- a/core/java/android/app/people/ConversationStatus.java +++ b/core/java/android/app/people/ConversationStatus.java @@ -142,32 +142,52 @@ public final class ConversationStatus implements Parcelable { dest.writeLong(mEndTimeMs); } + /** + * Returns the unique identifier for the status. + */ public @NonNull String getId() { return mId; } + /** + * Returns the type of activity represented by this status + */ public @ActivityType int getActivity() { return mActivity; } - public @Availability - int getAvailability() { + /** + * Returns the availability of the people behind this conversation while this activity is + * happening. + */ + public @Availability int getAvailability() { return mAvailability; } - public @Nullable - CharSequence getDescription() { + /** + * Returns the description for this activity. + */ + public @Nullable CharSequence getDescription() { return mDescription; } + /** + * Returns the image for this activity. + */ public @Nullable Icon getIcon() { return mIcon; } + /** + * Returns the time at which this status started + */ public long getStartTimeMillis() { return mStartTimeMs; } + /** + * Returns the time at which this status should be expired. + */ public long getEndTimeMillis() { return mEndTimeMs; } @@ -242,26 +262,51 @@ public final class ConversationStatus implements Parcelable { } + /** + * Sets the availability of the conversation to provide a hint about how likely + * it is that the user would receive a timely response if they sent a message. + */ public @NonNull Builder setAvailability(@Availability int availability) { mAvailability = availability; return this; } + /** + * Sets a user visible description expanding on the conversation user(s)'s activity. + * + * <p>Examples include: what media someone is watching or listening to, their approximate + * location, or what type of anniversary they are celebrating.</p> + */ public @NonNull Builder setDescription(@Nullable CharSequence description) { mDescription = description; return this; } + /** + * Sets an image representing the conversation user(s)'s activity. + * + * <p>Examples include: A still from a new story update, album art, or a map showing + * approximate location.</p> + */ public @NonNull Builder setIcon(@Nullable Icon icon) { mIcon = icon; return this; } + /** + * Sets the time at which this status became valid. + */ public @NonNull Builder setStartTimeMillis(long startTimeMs) { mStartTimeMs = startTimeMs; return this; } + /** + * Sets an expiration time for this status. + * + * <p>The system will remove the status at this time if it hasn't already been withdrawn. + * </p> + */ public @NonNull Builder setEndTimeMillis(long endTimeMs) { mEndTimeMs = endTimeMs; return this; diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java index e645831a31b1..de3eeee281ac 100644 --- a/core/java/android/app/people/PeopleSpaceTile.java +++ b/core/java/android/app/people/PeopleSpaceTile.java @@ -54,6 +54,7 @@ public class PeopleSpaceTile implements Parcelable { private boolean mIsImportantConversation; private String mNotificationKey; private CharSequence mNotificationContent; + private CharSequence mNotificationSender; private String mNotificationCategory; private Uri mNotificationDataUri; private int mMessagesCount; @@ -73,6 +74,7 @@ public class PeopleSpaceTile implements Parcelable { mIsImportantConversation = b.mIsImportantConversation; mNotificationKey = b.mNotificationKey; mNotificationContent = b.mNotificationContent; + mNotificationSender = b.mNotificationSender; mNotificationCategory = b.mNotificationCategory; mNotificationDataUri = b.mNotificationDataUri; mMessagesCount = b.mMessagesCount; @@ -134,6 +136,10 @@ public class PeopleSpaceTile implements Parcelable { return mNotificationContent; } + public CharSequence getNotificationSender() { + return mNotificationSender; + } + public String getNotificationCategory() { return mNotificationCategory; } @@ -170,7 +176,7 @@ public class PeopleSpaceTile implements Parcelable { /** Converts a {@link PeopleSpaceTile} into a {@link PeopleSpaceTile.Builder}. */ public Builder toBuilder() { Builder builder = - new Builder(mId, mUserName.toString(), mUserIcon, mIntent); + new Builder(mId, mUserName, mUserIcon, mIntent); builder.setContactUri(mContactUri); builder.setUserHandle(mUserHandle); builder.setPackageName(mPackageName); @@ -179,6 +185,7 @@ public class PeopleSpaceTile implements Parcelable { builder.setIsImportantConversation(mIsImportantConversation); builder.setNotificationKey(mNotificationKey); builder.setNotificationContent(mNotificationContent); + builder.setNotificationSender(mNotificationSender); builder.setNotificationCategory(mNotificationCategory); builder.setNotificationDataUri(mNotificationDataUri); builder.setMessagesCount(mMessagesCount); @@ -201,6 +208,7 @@ public class PeopleSpaceTile implements Parcelable { private boolean mIsImportantConversation; private String mNotificationKey; private CharSequence mNotificationContent; + private CharSequence mNotificationSender; private String mNotificationCategory; private Uri mNotificationDataUri; private int mMessagesCount; @@ -209,7 +217,7 @@ public class PeopleSpaceTile implements Parcelable { private List<ConversationStatus> mStatuses; /** Builder for use only if a shortcut is not available for the tile. */ - public Builder(String id, String userName, Icon userIcon, Intent intent) { + public Builder(String id, CharSequence userName, Icon userIcon, Intent intent) { mId = id; mUserName = userName; mUserIcon = userIcon; @@ -316,6 +324,12 @@ public class PeopleSpaceTile implements Parcelable { return this; } + /** Sets the associated notification's sender. */ + public Builder setNotificationSender(CharSequence notificationSender) { + mNotificationSender = notificationSender; + return this; + } + /** Sets the associated notification's category. */ public Builder setNotificationCategory(String notificationCategory) { mNotificationCategory = notificationCategory; @@ -371,6 +385,7 @@ public class PeopleSpaceTile implements Parcelable { mIsImportantConversation = in.readBoolean(); mNotificationKey = in.readString(); mNotificationContent = in.readCharSequence(); + mNotificationSender = in.readCharSequence(); mNotificationCategory = in.readString(); mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader()); mMessagesCount = in.readInt(); @@ -398,6 +413,7 @@ public class PeopleSpaceTile implements Parcelable { dest.writeBoolean(mIsImportantConversation); dest.writeString(mNotificationKey); dest.writeCharSequence(mNotificationContent); + dest.writeCharSequence(mNotificationSender); dest.writeString(mNotificationCategory); dest.writeParcelable(mNotificationDataUri, flags); dest.writeInt(mMessagesCount); diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index c2718621a378..1fb7638a6f05 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -23,13 +23,13 @@ import android.annotation.Nullable; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; -import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -265,7 +265,8 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp", IBluetoothA2dp.class.getName()) { @@ -279,8 +280,10 @@ public final class BluetoothA2dp implements BluetoothProfile { * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. */ - /*package*/ BluetoothA2dp(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothA2dp(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -386,7 +389,8 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); @@ -407,7 +411,8 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); @@ -468,7 +473,7 @@ public final class BluetoothA2dp implements BluetoothProfile { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && ((device == null) || isValidDevice(device))) { - return service.setActiveDevice(device); + return service.setActiveDevice(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -495,7 +500,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getActiveDevice(); + return service.getActiveDevice(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return null; @@ -555,7 +560,7 @@ public final class BluetoothA2dp implements BluetoothProfile { && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -585,7 +590,8 @@ public final class BluetoothA2dp implements BluetoothProfile { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device)); + return BluetoothAdapter.connectionPolicyToPriority( + service.getPriority(device, mAttributionSource)); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.PRIORITY_OFF; @@ -607,14 +613,18 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -659,7 +669,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - service.setAvrcpAbsoluteVolume(volume); + service.setAvrcpAbsoluteVolume(volume, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); } catch (RemoteException e) { @@ -680,7 +690,7 @@ public final class BluetoothA2dp implements BluetoothProfile { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - return service.isA2dpPlaying(device); + return service.isA2dpPlaying(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -732,7 +742,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getCodecStatus(device); + return service.getCodecStatus(device, mAttributionSource); } if (service == null) { Log.w(TAG, "Proxy not attached to service"); @@ -767,7 +777,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - service.setCodecConfigPreference(device, codecConfig); + service.setCodecConfigPreference(device, codecConfig, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return; @@ -824,9 +834,9 @@ public final class BluetoothA2dp implements BluetoothProfile { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { if (enable) { - service.enableOptionalCodecs(device); + service.enableOptionalCodecs(device, mAttributionSource); } else { - service.disableOptionalCodecs(device); + service.disableOptionalCodecs(device, mAttributionSource); } } if (service == null) Log.w(TAG, "Proxy not attached to service"); @@ -855,7 +865,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - return service.supportsOptionalCodecs(device); + return service.supportsOptionalCodecs(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_SUPPORT_UNKNOWN; @@ -883,7 +893,7 @@ public final class BluetoothA2dp implements BluetoothProfile { try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - return service.getOptionalCodecsEnabled(device); + return service.getOptionalCodecsEnabled(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_PREF_UNKNOWN; @@ -919,7 +929,7 @@ public final class BluetoothA2dp implements BluetoothProfile { final IBluetoothA2dp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { - service.setOptionalCodecsEnabled(device, value); + service.setOptionalCodecsEnabled(device, value, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return; @@ -941,13 +951,17 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @Type int getDynamicBufferSupport() { if (VDBG) log("getDynamicBufferSupport()"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getDynamicBufferSupport(); + return service.getDynamicBufferSupport(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return DYNAMIC_BUFFER_SUPPORT_NONE; @@ -968,13 +982,17 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @Nullable BufferConstraints getBufferConstraints() { if (VDBG) log("getBufferConstraints()"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.getBufferConstraints(); + return service.getBufferConstraints(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return null; @@ -994,14 +1012,18 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) { if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.setBufferLengthMillis(codec, value); + return service.setBufferLengthMillis(codec, value, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java index cbbf4c97969e..c0a2aa3db421 100755 --- a/core/java/android/bluetooth/BluetoothA2dpSink.java +++ b/core/java/android/bluetooth/BluetoothA2dpSink.java @@ -27,6 +27,7 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -78,7 +79,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothA2dpSink> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK, "BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) { @@ -92,8 +94,10 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. */ - /*package*/ BluetoothA2dpSink(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothA2dpSink(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -128,13 +132,17 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.connect(device); + return service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -175,7 +183,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -198,7 +206,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -221,7 +230,9 @@ public final class BluetoothA2dpSink implements BluetoothProfile { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -244,7 +255,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -273,7 +284,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getAudioConfig(device); + return service.getAudioConfig(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return null; @@ -332,7 +343,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { return false; } try { - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -352,7 +363,11 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -370,13 +385,17 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -395,12 +414,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean isAudioPlaying(@NonNull BluetoothDevice device) { final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.isA2dpPlaying(device); + return service.isA2dpPlaying(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 2426ead8105c..8afc557ef85e 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -706,11 +706,13 @@ public final class BluetoothAdapter { */ private static BluetoothAdapter sAdapter; - private static BluetoothLeScanner sBluetoothLeScanner; - private static BluetoothLeAdvertiser sBluetoothLeAdvertiser; - private static PeriodicAdvertisingManager sPeriodicAdvertisingManager; + private BluetoothLeScanner mBluetoothLeScanner; + private BluetoothLeAdvertiser mBluetoothLeAdvertiser; + private PeriodicAdvertisingManager mPeriodicAdvertisingManager; private final IBluetoothManager mManagerService; + private final AttributionSource mAttributionSource; + @UnsupportedAppUsage private IBluetooth mService; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); @@ -722,8 +724,6 @@ public final class BluetoothAdapter { private final Map<BluetoothConnectionCallback, Executor> mBluetoothConnectionCallbackExecutorMap = new HashMap<>(); - private AttributionSource mAttributionSource; - /** * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener * implementation. @@ -752,27 +752,33 @@ public final class BluetoothAdapter { /** * Get a handle to the default local Bluetooth adapter. - * <p>Currently Android only supports one Bluetooth adapter, but the API - * could be extended to support more. This will always return the default - * adapter. + * <p> + * Currently Android only supports one Bluetooth adapter, but the API could + * be extended to support more. This will always return the default adapter. * </p> * - * @return the default local adapter, or null if Bluetooth is not supported on this hardware - * platform + * @return the default local adapter, or null if Bluetooth is not supported + * on this hardware platform + * @deprecated this method will continue to work, but developers are + * strongly encouraged to migrate to using + * {@link BluetoothManager#getAdapter()}, since that approach + * enables support for {@link Context#createAttributionContext}. */ + @Deprecated @RequiresNoPermission public static synchronized BluetoothAdapter getDefaultAdapter() { if (sAdapter == null) { - sAdapter = createAdapter(); + sAdapter = createAdapter(BluetoothManager.resolveAttributionSource(null)); } return sAdapter; } /** {@hide} */ - public static BluetoothAdapter createAdapter() { + public static BluetoothAdapter createAdapter(AttributionSource attributionSource) { IBinder binder = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE); if (binder != null) { - return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder)); + return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder), + attributionSource); } else { Log.e(TAG, "Bluetooth binder is null"); return null; @@ -782,7 +788,7 @@ public final class BluetoothAdapter { /** * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. */ - BluetoothAdapter(IBluetoothManager managerService) { + BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) { if (managerService == null) { throw new IllegalArgumentException("bluetooth manager service is null"); } @@ -794,20 +800,12 @@ public final class BluetoothAdapter { } finally { mServiceLock.writeLock().unlock(); } - mManagerService = managerService; + mManagerService = Objects.requireNonNull(managerService); + mAttributionSource = Objects.requireNonNull(attributionSource); mLeScanClients = new HashMap<LeScanCallback, ScanCallback>(); mToken = new Binder(); } - void setAttributionSource(AttributionSource attributionSource) { - mAttributionSource = attributionSource; - } - - private AttributionSource resolveAttributionSource() { - return (mAttributionSource != null) ? mAttributionSource - : ActivityThread.currentAttributionSource(); - } - /** * Get a {@link BluetoothDevice} object for the given Bluetooth hardware * address. @@ -864,11 +862,11 @@ public final class BluetoothAdapter { return null; } synchronized (mLock) { - if (sBluetoothLeAdvertiser == null) { - sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService); + if (mBluetoothLeAdvertiser == null) { + mBluetoothLeAdvertiser = new BluetoothLeAdvertiser(this); } + return mBluetoothLeAdvertiser; } - return sBluetoothLeAdvertiser; } /** @@ -892,11 +890,11 @@ public final class BluetoothAdapter { } synchronized (mLock) { - if (sPeriodicAdvertisingManager == null) { - sPeriodicAdvertisingManager = new PeriodicAdvertisingManager(mManagerService); + if (mPeriodicAdvertisingManager == null) { + mPeriodicAdvertisingManager = new PeriodicAdvertisingManager(this); } + return mPeriodicAdvertisingManager; } - return sPeriodicAdvertisingManager; } /** @@ -908,12 +906,11 @@ public final class BluetoothAdapter { return null; } synchronized (mLock) { - if (sBluetoothLeScanner == null) { - sBluetoothLeScanner = - new BluetoothLeScanner(mManagerService, resolveAttributionSource()); + if (mBluetoothLeScanner == null) { + mBluetoothLeScanner = new BluetoothLeScanner(this); } + return mBluetoothLeScanner; } - return sBluetoothLeScanner; } /** @@ -984,7 +981,7 @@ public final class BluetoothAdapter { } String packageName = ActivityThread.currentPackageName(); try { - return mManagerService.disableBle(packageName, mToken); + return mManagerService.disableBle(mAttributionSource, mToken); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1031,7 +1028,7 @@ public final class BluetoothAdapter { } String packageName = ActivityThread.currentPackageName(); try { - return mManagerService.enableBle(packageName, mToken); + return mManagerService.enableBle(mAttributionSource, mToken); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1196,7 +1193,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enable(ActivityThread.currentPackageName()); + return mManagerService.enable(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1229,7 +1226,7 @@ public final class BluetoothAdapter { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disable() { try { - return mManagerService.disable(ActivityThread.currentPackageName(), true); + return mManagerService.disable(mAttributionSource, true); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1249,7 +1246,7 @@ public final class BluetoothAdapter { public boolean disable(boolean persist) { try { - return mManagerService.disable(ActivityThread.currentPackageName(), persist); + return mManagerService.disable(mAttributionSource, persist); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1270,7 +1267,7 @@ public final class BluetoothAdapter { }) public String getAddress() { try { - return mManagerService.getAddress(); + return mManagerService.getAddress(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1288,7 +1285,7 @@ public final class BluetoothAdapter { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName() { try { - return mManagerService.getName(); + return mManagerService.getName(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1300,7 +1297,7 @@ public final class BluetoothAdapter { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public int getNameLengthForAdvertise() { try { - return mService.getNameLengthForAdvertise(); + return mService.getNameLengthForAdvertise(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1319,7 +1316,8 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null && mService.factoryReset() - && mManagerService != null && mManagerService.onFactoryReset()) { + && mManagerService != null + && mManagerService.onFactoryReset(mAttributionSource)) { return true; } Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later"); @@ -1349,7 +1347,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getUuids(resolveAttributionSource()); + return mService.getUuids(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1383,7 +1381,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setName(name, resolveAttributionSource()); + return mService.setName(name, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1411,7 +1409,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getBluetoothClass(resolveAttributionSource()); + return mService.getBluetoothClass(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1467,7 +1465,7 @@ public final class BluetoothAdapter { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getIoCapability(resolveAttributionSource()); + if (mService != null) return mService.getIoCapability(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1520,7 +1518,7 @@ public final class BluetoothAdapter { if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; try { mServiceLock.readLock().lock(); - if (mService != null) return mService.getLeIoCapability(resolveAttributionSource()); + if (mService != null) return mService.getLeIoCapability(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.getMessage(), e); } finally { @@ -1582,7 +1580,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getScanMode(resolveAttributionSource()); + return mService.getScanMode(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1631,7 +1629,7 @@ public final class BluetoothAdapter { mServiceLock.readLock().lock(); if (mService != null) { int durationSeconds = Math.toIntExact(durationMillis / 1000); - return mService.setScanMode(mode, durationSeconds, resolveAttributionSource()); + return mService.setScanMode(mode, durationSeconds, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1681,7 +1679,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.setScanMode(mode, getDiscoverableTimeout(), resolveAttributionSource()); + return mService.setScanMode(mode, getDiscoverableTimeout(), mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1702,7 +1700,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getDiscoverableTimeout(resolveAttributionSource()); + return mService.getDiscoverableTimeout(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1723,7 +1721,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - mService.setDiscoverableTimeout(timeout, resolveAttributionSource()); + mService.setDiscoverableTimeout(timeout, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1796,7 +1794,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.startDiscovery(resolveAttributionSource()); + return mService.startDiscovery(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1832,7 +1830,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.cancelDiscovery(resolveAttributionSource()); + return mService.cancelDiscovery(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1870,7 +1868,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.isDiscovering(resolveAttributionSource()); + return mService.isDiscovering(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1913,7 +1911,7 @@ public final class BluetoothAdapter { mServiceLock.readLock().lock(); if (mService != null) { if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles); - return mService.removeActiveDevice(profiles); + return mService.removeActiveDevice(profiles, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1965,7 +1963,7 @@ public final class BluetoothAdapter { if (DBG) { Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles); } - return mService.setActiveDevice(device, profiles); + return mService.setActiveDevice(device, profiles, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1998,7 +1996,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.connectAllEnabledProfiles(device); + return mService.connectAllEnabledProfiles(device, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2030,7 +2028,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.disconnectAllEnabledProfiles(device); + return mService.disconnectAllEnabledProfiles(device, mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2307,7 +2305,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getMaxConnectedAudioDevices(resolveAttributionSource()); + return mService.getMaxConnectedAudioDevices(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e); @@ -2335,7 +2333,7 @@ public final class BluetoothAdapter { // BLE is not supported return false; } - return (iGatt.numHwTrackFiltersAvailable() != 0); + return (iGatt.numHwTrackFiltersAvailable(mAttributionSource) != 0); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2419,7 +2417,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.getMostRecentlyConnectedDevices(resolveAttributionSource()); + return mService.getMostRecentlyConnectedDevices(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); @@ -2449,7 +2447,7 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return toDeviceSet(mService.getBondedDevices(resolveAttributionSource())); + return toDeviceSet(mService.getBondedDevices(mAttributionSource)); } return toDeviceSet(new BluetoothDevice[0]); } catch (RemoteException e) { @@ -2974,50 +2972,51 @@ public final class BluetoothAdapter { } if (profile == BluetoothProfile.HEADSET) { - BluetoothHeadset headset = new BluetoothHeadset(context, listener); + BluetoothHeadset headset = new BluetoothHeadset(context, listener, this); return true; } else if (profile == BluetoothProfile.A2DP) { - BluetoothA2dp a2dp = new BluetoothA2dp(context, listener); + BluetoothA2dp a2dp = new BluetoothA2dp(context, listener, this); return true; } else if (profile == BluetoothProfile.A2DP_SINK) { - BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener); + BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener, this); return true; } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) { - BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener); + BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener, this); return true; } else if (profile == BluetoothProfile.HID_HOST) { - BluetoothHidHost iDev = new BluetoothHidHost(context, listener); + BluetoothHidHost iDev = new BluetoothHidHost(context, listener, this); return true; } else if (profile == BluetoothProfile.PAN) { - BluetoothPan pan = new BluetoothPan(context, listener); + BluetoothPan pan = new BluetoothPan(context, listener, this); return true; } else if (profile == BluetoothProfile.PBAP) { - BluetoothPbap pbap = new BluetoothPbap(context, listener); + BluetoothPbap pbap = new BluetoothPbap(context, listener, this); return true; } else if (profile == BluetoothProfile.HEALTH) { Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated"); return false; } else if (profile == BluetoothProfile.MAP) { - BluetoothMap map = new BluetoothMap(context, listener); + BluetoothMap map = new BluetoothMap(context, listener, this); return true; } else if (profile == BluetoothProfile.HEADSET_CLIENT) { - BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener); + BluetoothHeadsetClient headsetClient = + new BluetoothHeadsetClient(context, listener, this); return true; } else if (profile == BluetoothProfile.SAP) { - BluetoothSap sap = new BluetoothSap(context, listener); + BluetoothSap sap = new BluetoothSap(context, listener, this); return true; } else if (profile == BluetoothProfile.PBAP_CLIENT) { - BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener); + BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener, this); return true; } else if (profile == BluetoothProfile.MAP_CLIENT) { - BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); + BluetoothMapClient mapClient = new BluetoothMapClient(context, listener, this); return true; } else if (profile == BluetoothProfile.HID_DEVICE) { - BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener); + BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener, this); return true; } else if (profile == BluetoothProfile.HEARING_AID) { if (isHearingAidProfileSupported()) { - BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener); + BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this); return true; } return false; @@ -3171,11 +3170,11 @@ public final class BluetoothAdapter { if (mLeScanClients != null) { mLeScanClients.clear(); } - if (sBluetoothLeAdvertiser != null) { - sBluetoothLeAdvertiser.cleanup(); + if (mBluetoothLeAdvertiser != null) { + mBluetoothLeAdvertiser.cleanup(); } - if (sBluetoothLeScanner != null) { - sBluetoothLeScanner.cleanup(); + if (mBluetoothLeScanner != null) { + mBluetoothLeScanner.cleanup(); } } finally { mServiceLock.writeLock().unlock(); @@ -3221,7 +3220,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName()); + return mManagerService.enableNoAutoConnect(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -3514,11 +3513,17 @@ public final class BluetoothAdapter { && (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11; } + /** {@hide} */ @UnsupportedAppUsage - /*package*/ IBluetoothManager getBluetoothManager() { + public IBluetoothManager getBluetoothManager() { return mManagerService; } + /** {@hide} */ + public AttributionSource getAttributionSource() { + return mAttributionSource; + } + private final ArrayList<IBluetoothManagerCallback> mProxyServiceStateCallbacks = new ArrayList<IBluetoothManagerCallback>(); diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java index cac676d7dc9a..0b43e71aba2a 100644 --- a/core/java/android/bluetooth/BluetoothAvrcpController.java +++ b/core/java/android/bluetooth/BluetoothAvrcpController.java @@ -22,6 +22,7 @@ import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -86,7 +87,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public static final String EXTRA_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothAvrcpController> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER, "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) { @@ -101,8 +103,10 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * Create a BluetoothAvrcpController proxy object for interacting with the local * Bluetooth AVRCP service. */ - /*package*/ BluetoothAvrcpController(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothAvrcpController(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -131,7 +135,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -153,7 +158,9 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -175,7 +182,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -199,7 +206,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - settings = service.getPlayerSettings(device); + settings = service.getPlayerSettings(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getMetadata() " + e); return null; @@ -220,7 +227,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.setPlayerApplicationSetting(plAppSetting); + return service.setPlayerApplicationSetting(plAppSetting, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e); return false; @@ -243,7 +250,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - service.sendGroupNavigationCmd(device, keyCode, keyState); + service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource); return; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e); diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index cc2ba9b8abbf..98823b096a55 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -24,7 +24,6 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.app.ActivityThread; import android.app.PropertyInvalidatedCache; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresBluetoothLocationPermission; @@ -48,6 +47,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; import java.util.UUID; /** @@ -1167,15 +1167,27 @@ public final class BluetoothDevice implements Parcelable { mAddress = address; mAddressType = ADDRESS_TYPE_PUBLIC; + mAttributionSource = BluetoothManager.resolveAttributionSource(null); } void setAttributionSource(AttributionSource attributionSource) { mAttributionSource = attributionSource; } - private AttributionSource resolveAttributionSource() { - return (mAttributionSource != null) ? mAttributionSource - : ActivityThread.currentAttributionSource(); + static BluetoothDevice setAttributionSource(BluetoothDevice device, + AttributionSource attributionSource) { + device.setAttributionSource(attributionSource); + return device; + } + + static List<BluetoothDevice> setAttributionSource(List<BluetoothDevice> devices, + AttributionSource attributionSource) { + if (devices != null) { + for (BluetoothDevice device : devices) { + device.setAttributionSource(attributionSource); + } + } + return devices; } @Override @@ -1268,7 +1280,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - String name = service.getRemoteName(this, resolveAttributionSource()); + String name = service.getRemoteName(this, mAttributionSource); if (name != null) { // remove whitespace characters from the name return name @@ -1299,7 +1311,7 @@ public final class BluetoothDevice implements Parcelable { return DEVICE_TYPE_UNKNOWN; } try { - return service.getRemoteType(this, resolveAttributionSource()); + return service.getRemoteType(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1323,7 +1335,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - String alias = service.getRemoteAliasWithAttribution(this, resolveAttributionSource()); + String alias = service.getRemoteAliasWithAttribution(this, mAttributionSource); if (alias == null) { return getName(); } @@ -1364,9 +1376,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.setRemoteAlias(this, alias, - resolveAttributionSource().getPackageName(), - resolveAttributionSource()); + return service.setRemoteAlias(this, alias, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1392,7 +1402,7 @@ public final class BluetoothDevice implements Parcelable { return BATTERY_LEVEL_BLUETOOTH_OFF; } try { - return service.getBatteryLevel(this, resolveAttributionSource()); + return service.getBatteryLevel(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1483,7 +1493,7 @@ public final class BluetoothDevice implements Parcelable { } try { return service.createBond( - this, transport, remoteP192Data, remoteP256Data, resolveAttributionSource()); + this, transport, remoteP192Data, remoteP256Data, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1508,7 +1518,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.isBondingInitiatedLocally(this, resolveAttributionSource()); + return service.isBondingInitiatedLocally(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1533,7 +1543,7 @@ public final class BluetoothDevice implements Parcelable { Log.i(TAG, "cancelBondProcess() for device " + getAddress() + " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); - return service.cancelBondProcess(this, resolveAttributionSource()); + return service.cancelBondProcess(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1561,7 +1571,7 @@ public final class BluetoothDevice implements Parcelable { Log.i(TAG, "removeBond() for device " + getAddress() + " called by pid: " + Process.myPid() + " tid: " + Process.myTid()); - return service.removeBond(this, resolveAttributionSource()); + return service.removeBond(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1577,7 +1587,7 @@ public final class BluetoothDevice implements Parcelable { @SuppressLint("AndroidFrameworkRequiresPermission") protected Integer recompute(BluetoothDevice query) { try { - return sService.getBondState(query, resolveAttributionSource()); + return sService.getBondState(query, mAttributionSource); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -1667,7 +1677,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.getConnectionStateWithAttribution(this, resolveAttributionSource()) + return service.getConnectionStateWithAttribution(this, mAttributionSource) != CONNECTION_STATE_DISCONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1693,7 +1703,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.getConnectionStateWithAttribution(this, resolveAttributionSource()) + return service.getConnectionStateWithAttribution(this, mAttributionSource) > CONNECTION_STATE_CONNECTED; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1716,7 +1726,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - int classInt = service.getRemoteClass(this, resolveAttributionSource()); + int classInt = service.getRemoteClass(this, mAttributionSource); if (classInt == BluetoothClass.ERROR) return null; return new BluetoothClass(classInt); } catch (RemoteException e) { @@ -1745,7 +1755,7 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return service.getRemoteUuids(this, resolveAttributionSource()); + return service.getRemoteUuids(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1775,7 +1785,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.fetchRemoteUuidsWithAttribution(this, resolveAttributionSource()); + return service.fetchRemoteUuidsWithAttribution(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1812,7 +1822,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.sdpSearch(this, uuid, resolveAttributionSource()); + return service.sdpSearch(this, uuid, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1834,7 +1844,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.setPin(this, true, pin.length, pin, resolveAttributionSource()); + return service.setPin(this, true, pin.length, pin, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1897,7 +1907,7 @@ public final class BluetoothDevice implements Parcelable { return false; } try { - return service.cancelBondProcess(this, resolveAttributionSource()); + return service.cancelBondProcess(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1930,7 +1940,7 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } try { - return service.getPhonebookAccessPermission(this, resolveAttributionSource()); + return service.getPhonebookAccessPermission(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2036,7 +2046,7 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } try { - return service.getMessageAccessPermission(this, resolveAttributionSource()); + return service.getMessageAccessPermission(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2087,7 +2097,7 @@ public final class BluetoothDevice implements Parcelable { return ACCESS_UNKNOWN; } try { - return service.getSimAccessPermission(this, resolveAttributionSource()); + return service.getSimAccessPermission(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2516,7 +2526,8 @@ public final class BluetoothDevice implements Parcelable { // BLE is not supported return null; } - BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, opportunistic, phy); + BluetoothGatt gatt = new BluetoothGatt( + iGatt, this, transport, opportunistic, phy, mAttributionSource); gatt.connect(autoConnect, callback, handler); return gatt; } catch (RemoteException e) { diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index 9d3eed8f6fa1..aea82102ca36 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -22,6 +22,7 @@ import android.annotation.SuppressLint; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.os.Build; import android.os.Handler; import android.os.ParcelUuid; @@ -69,6 +70,7 @@ public final class BluetoothGatt implements BluetoothProfile { private int mTransport; private int mPhy; private boolean mOpportunistic; + private final AttributionSource mAttributionSource; private static final int AUTH_RETRY_STATE_IDLE = 0; private static final int AUTH_RETRY_STATE_NO_MITM = 1; @@ -198,7 +200,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.clientConnect(mClientIf, mDevice.getAddress(), !mAutoConnect, mTransport, mOpportunistic, - mPhy); // autoConnect is inverse of "isDirect" + mPhy, mAttributionSource); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -376,7 +378,8 @@ public final class BluetoothGatt implements BluetoothProfile { try { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.readCharacteristic(mClientIf, address, handle, authReq); + mService.readCharacteristic( + mClientIf, address, handle, authReq, mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -439,7 +442,7 @@ public final class BluetoothGatt implements BluetoothProfile { ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeCharacteristic(mClientIf, address, handle, characteristic.getWriteType(), authReq, - characteristic.getValue()); + characteristic.getValue(), mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -521,7 +524,8 @@ public final class BluetoothGatt implements BluetoothProfile { try { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.readDescriptor(mClientIf, address, handle, authReq); + mService.readDescriptor( + mClientIf, address, handle, authReq, mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -573,7 +577,7 @@ public final class BluetoothGatt implements BluetoothProfile { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeDescriptor(mClientIf, address, handle, - authReq, descriptor.getValue()); + authReq, descriptor.getValue(), mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -726,13 +730,14 @@ public final class BluetoothGatt implements BluetoothProfile { } }; - /*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, - int transport, boolean opportunistic, int phy) { + /* package */ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, int transport, + boolean opportunistic, int phy, AttributionSource attributionSource) { mService = iGatt; mDevice = device; mTransport = transport; mPhy = phy; mOpportunistic = opportunistic; + mAttributionSource = attributionSource; mServices = new ArrayList<BluetoothGattService>(); mConnState = CONN_STATE_IDLE; @@ -867,7 +872,8 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); try { - mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support); + mService.registerClient( + new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -888,7 +894,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mCallback = null; - mService.unregisterClient(mClientIf); + mService.unregisterClient(mClientIf, mAttributionSource); mClientIf = 0; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -958,7 +964,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return; try { - mService.clientDisconnect(mClientIf, mDevice.getAddress()); + mService.clientDisconnect(mClientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -977,8 +983,9 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect() { try { + // autoConnect is inverse of "isDirect" mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport, - mOpportunistic, mPhy); // autoConnect is inverse of "isDirect" + mOpportunistic, mPhy, mAttributionSource); return true; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1009,7 +1016,7 @@ public final class BluetoothGatt implements BluetoothProfile { public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) { try { mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy, - phyOptions); + phyOptions, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1023,7 +1030,7 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void readPhy() { try { - mService.clientReadPhy(mClientIf, mDevice.getAddress()); + mService.clientReadPhy(mClientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1060,7 +1067,7 @@ public final class BluetoothGatt implements BluetoothProfile { mServices.clear(); try { - mService.discoverServices(mClientIf, mDevice.getAddress()); + mService.discoverServices(mClientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1087,7 +1094,8 @@ public final class BluetoothGatt implements BluetoothProfile { mServices.clear(); try { - mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid)); + mService.discoverServiceByUuid( + mClientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1179,7 +1187,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.readCharacteristic(mClientIf, device.getAddress(), - characteristic.getInstanceId(), AUTHENTICATION_NONE); + characteristic.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mDeviceBusy = false; @@ -1214,7 +1222,8 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(), - new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE); + new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mDeviceBusy = false; @@ -1262,7 +1271,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.writeCharacteristic(mClientIf, device.getAddress(), characteristic.getInstanceId(), characteristic.getWriteType(), - AUTHENTICATION_NONE, characteristic.getValue()); + AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mDeviceBusy = false; @@ -1305,7 +1314,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.readDescriptor(mClientIf, device.getAddress(), - descriptor.getInstanceId(), AUTHENTICATION_NONE); + descriptor.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mDeviceBusy = false; @@ -1347,7 +1356,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(), - AUTHENTICATION_NONE, descriptor.getValue()); + AUTHENTICATION_NONE, descriptor.getValue(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mDeviceBusy = false; @@ -1383,7 +1392,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return false; try { - mService.beginReliableWrite(mClientIf, mDevice.getAddress()); + mService.beginReliableWrite(mClientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1416,7 +1425,7 @@ public final class BluetoothGatt implements BluetoothProfile { } try { - mService.endReliableWrite(mClientIf, mDevice.getAddress(), true); + mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mDeviceBusy = false; @@ -1440,7 +1449,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return; try { - mService.endReliableWrite(mClientIf, mDevice.getAddress(), false); + mService.endReliableWrite(mClientIf, mDevice.getAddress(), false, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1487,7 +1496,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.registerForNotification(mClientIf, device.getAddress(), - characteristic.getInstanceId(), enable); + characteristic.getInstanceId(), enable, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1510,7 +1519,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return false; try { - mService.refreshDevice(mClientIf, mDevice.getAddress()); + mService.refreshDevice(mClientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1535,7 +1544,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return false; try { - mService.readRemoteRssi(mClientIf, mDevice.getAddress()); + mService.readRemoteRssi(mClientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1567,7 +1576,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return false; try { - mService.configureMTU(mClientIf, mDevice.getAddress(), mtu); + mService.configureMTU(mClientIf, mDevice.getAddress(), mtu, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1599,7 +1608,8 @@ public final class BluetoothGatt implements BluetoothProfile { if (mService == null || mClientIf == 0) return false; try { - mService.connectionParameterUpdate(mClientIf, mDevice.getAddress(), connectionPriority); + mService.connectionParameterUpdate( + mClientIf, mDevice.getAddress(), connectionPriority, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1633,9 +1643,10 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.leConnectionUpdate(mClientIf, mDevice.getAddress(), - minConnectionInterval, maxConnectionInterval, - slaveLatency, supervisionTimeout, - minConnectionEventLen, maxConnectionEventLen); + minConnectionInterval, maxConnectionInterval, + slaveLatency, supervisionTimeout, + minConnectionEventLen, maxConnectionEventLen, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java index 865f476e7869..3e799defa5e9 100644 --- a/core/java/android/bluetooth/BluetoothGattServer.java +++ b/core/java/android/bluetooth/BluetoothGattServer.java @@ -21,6 +21,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.content.AttributionSource; import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; @@ -45,8 +46,10 @@ public final class BluetoothGattServer implements BluetoothProfile { private static final boolean DBG = true; private static final boolean VDBG = false; - private BluetoothAdapter mAdapter; - private IBluetoothGatt mService; + private final IBluetoothGatt mService; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; + private BluetoothGattServerCallback mCallback; private Object mServerIfLock = new Object(); @@ -382,9 +385,11 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Create a BluetoothGattServer proxy object. */ - /*package*/ BluetoothGattServer(IBluetoothGatt iGatt, int transport) { + /* package */ BluetoothGattServer(IBluetoothGatt iGatt, int transport, + BluetoothAdapter adapter) { mService = iGatt; - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mCallback = null; mServerIf = 0; mTransport = transport; @@ -488,7 +493,8 @@ public final class BluetoothGattServer implements BluetoothProfile { mCallback = callback; try { - mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback, eatt_support); + mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback, + eatt_support, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); mCallback = null; @@ -522,7 +528,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mCallback = null; - mService.unregisterServer(mServerIf); + mService.unregisterServer(mServerIf, mAttributionSource); mServerIf = 0; } catch (RemoteException e) { Log.e(TAG, "", e); @@ -576,7 +582,8 @@ public final class BluetoothGattServer implements BluetoothProfile { try { // autoConnect is inverse of "isDirect" - mService.serverConnect(mServerIf, device.getAddress(), !autoConnect, mTransport); + mService.serverConnect( + mServerIf, device.getAddress(), !autoConnect, mTransport, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -599,7 +606,7 @@ public final class BluetoothGattServer implements BluetoothProfile { if (mService == null || mServerIf == 0) return; try { - mService.serverDisconnect(mServerIf, device.getAddress()); + mService.serverDisconnect(mServerIf, device.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -628,7 +635,7 @@ public final class BluetoothGattServer implements BluetoothProfile { public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) { try { mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy, - phyOptions); + phyOptions, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -644,7 +651,7 @@ public final class BluetoothGattServer implements BluetoothProfile { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void readPhy(BluetoothDevice device) { try { - mService.serverReadPhy(mServerIf, device.getAddress()); + mService.serverReadPhy(mServerIf, device.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -679,7 +686,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.sendResponse(mServerIf, device.getAddress(), requestId, - status, offset, value); + status, offset, value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -722,7 +729,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.sendNotification(mServerIf, device.getAddress(), characteristic.getInstanceId(), confirm, - characteristic.getValue()); + characteristic.getValue(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -757,7 +764,7 @@ public final class BluetoothGattServer implements BluetoothProfile { mPendingService = service; try { - mService.addService(mServerIf, service); + mService.addService(mServerIf, service, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -784,7 +791,7 @@ public final class BluetoothGattServer implements BluetoothProfile { if (intService == null) return false; try { - mService.removeService(mServerIf, service.getInstanceId()); + mService.removeService(mServerIf, service.getInstanceId(), mAttributionSource); mServices.remove(intService); } catch (RemoteException e) { Log.e(TAG, "", e); @@ -805,7 +812,7 @@ public final class BluetoothGattServer implements BluetoothProfile { if (mService == null || mServerIf == 0) return; try { - mService.clearServices(mServerIf); + mService.clearServices(mServerIf, mAttributionSource); mServices.clear(); } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 51ef3c284f47..9dc2d8e9539e 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -28,6 +28,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; import android.os.Binder; @@ -339,7 +340,8 @@ public final class BluetoothHeadset implements BluetoothProfile { private Context mContext; private ServiceListener mServiceListener; private volatile IBluetoothHeadset mService; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; @SuppressLint("AndroidFrameworkBluetoothPermission") private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -357,10 +359,11 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Create a BluetoothHeadset proxy object. */ - /*package*/ BluetoothHeadset(Context context, ServiceListener l) { + /* package */ BluetoothHeadset(Context context, ServiceListener l, BluetoothAdapter adapter) { mContext = context; mServiceListener = l; - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { @@ -519,7 +522,8 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -540,7 +544,9 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -603,7 +609,8 @@ public final class BluetoothHeadset implements BluetoothProfile { } try { return service.setPriority( - device, BluetoothAdapter.priorityToConnectionPolicy(priority)); + device, BluetoothAdapter.priorityToConnectionPolicy(priority), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -642,7 +649,7 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } try { - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -672,7 +679,8 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device)); + return BluetoothAdapter.connectionPolicyToPriority( + service.getPriority(device, mAttributionSource)); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.PRIORITY_OFF; @@ -694,13 +702,17 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -724,7 +736,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.isNoiseReductionSupported(device); + return service.isNoiseReductionSupported(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -747,7 +759,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.isVoiceRecognitionSupported(device); + return service.isVoiceRecognitionSupported(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -786,7 +798,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.startVoiceRecognition(device); + return service.startVoiceRecognition(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -815,7 +827,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.stopVoiceRecognition(device); + return service.stopVoiceRecognition(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -838,7 +850,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.isAudioConnected(device); + return service.isAudioConnected(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -872,7 +884,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && !isDisabled()) { try { - return service.getAudioState(device); + return service.getAudioState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -900,7 +912,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - service.setAudioRouteAllowed(allowed); + service.setAudioRouteAllowed(allowed, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -923,7 +935,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.getAudioRouteAllowed(); + return service.getAudioRouteAllowed(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -948,7 +960,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - service.setForceScoAudio(forced); + service.setForceScoAudio(forced, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -973,7 +985,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.isAudioOn(); + return service.isAudioOn(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1008,7 +1020,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.connectAudio(); + return service.connectAudio(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1037,7 +1049,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.disconnectAudio(); + return service.disconnectAudio(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1081,7 +1093,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.startScoUsingVirtualVoiceCall(); + return service.startScoUsingVirtualVoiceCall(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1116,7 +1128,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.stopScoUsingVirtualVoiceCall(); + return service.stopScoUsingVirtualVoiceCall(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1146,7 +1158,8 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - service.phoneStateChanged(numActive, numHeld, callState, number, type, name); + service.phoneStateChanged(numActive, numHeld, callState, number, type, name, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1171,7 +1184,8 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - service.clccResponse(index, direction, status, mode, mpty, number, type); + service.clccResponse(index, direction, status, mode, mpty, number, type, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1211,7 +1225,8 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.sendVendorSpecificResultCode(device, command, arg); + return service.sendVendorSpecificResultCode(device, command, arg, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1255,7 +1270,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled() && (device == null || isValidDevice(device))) { try { - return service.setActiveDevice(device); + return service.setActiveDevice(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1285,7 +1300,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.getActiveDevice(); + return service.getActiveDevice(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1313,7 +1328,7 @@ public final class BluetoothHeadset implements BluetoothProfile { final IBluetoothHeadset service = mService; if (service != null && isEnabled()) { try { - return service.isInbandRingingEnabled(); + return service.isInbandRingingEnabled(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java index 840b4d3121b6..0059cdb9fbb7 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -23,6 +23,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -424,7 +425,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public static final int CALL_ACCEPT_HOLD = 1; public static final int CALL_ACCEPT_TERMINATE = 2; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothHeadsetClient> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT, "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) { @@ -437,8 +439,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { /** * Create a BluetoothHeadsetClient proxy object. */ - /*package*/ BluetoothHeadsetClient(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothHeadsetClient(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -479,7 +483,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.connect(device); + return service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -507,7 +511,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -531,7 +535,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -557,7 +562,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -582,7 +589,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -635,7 +642,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return false; } try { - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -683,7 +690,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -712,7 +719,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.startVoiceRecognition(device); + return service.startVoiceRecognition(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -739,7 +746,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.sendVendorAtCommand(device, vendorId, atCommand); + return service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -767,7 +774,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.stopVoiceRecognition(device); + return service.stopVoiceRecognition(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -790,7 +797,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getCurrentCalls(device); + return service.getCurrentCalls(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -813,7 +820,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getCurrentAgEvents(device); + return service.getCurrentAgEvents(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -840,7 +847,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.acceptCall(device, flag); + return service.acceptCall(device, flag, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -864,7 +871,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.holdCall(device); + return service.holdCall(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -893,7 +900,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.rejectCall(device); + return service.rejectCall(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -926,7 +933,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.terminateCall(device, call); + return service.terminateCall(device, call, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -957,7 +964,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.enterPrivateMode(device, index); + return service.enterPrivateMode(device, index, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -987,7 +994,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.explicitCallTransfer(device); + return service.explicitCallTransfer(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1013,7 +1020,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.dial(device, number); + return service.dial(device, number, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1040,7 +1047,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.sendDTMF(device, code); + return service.sendDTMF(device, code, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1069,7 +1076,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getLastVoiceTagNumber(device); + return service.getLastVoiceTagNumber(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -1092,7 +1099,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.getAudioState(device); + return service.getAudioState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1118,7 +1125,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - service.setAudioRouteAllowed(device, allowed); + service.setAudioRouteAllowed(device, allowed, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1143,7 +1150,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.getAudioRouteAllowed(device); + return service.getAudioRouteAllowed(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1170,7 +1177,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.connectAudio(device); + return service.connectAudio(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1197,7 +1204,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.disconnectAudio(device); + return service.disconnectAudio(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -1221,7 +1228,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { getService(); if (service != null && isEnabled()) { try { - return service.getCurrentAgFeatures(device); + return service.getCurrentAgFeatures(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java index fa52eda8ea61..3ff2ebd86b6e 100644 --- a/core/java/android/bluetooth/BluetoothHearingAid.java +++ b/core/java/android/bluetooth/BluetoothHearingAid.java @@ -28,6 +28,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -130,7 +131,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothHearingAid> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID, "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) { @@ -144,8 +146,10 @@ public final class BluetoothHearingAid implements BluetoothProfile { * Create a BluetoothHearingAid proxy object for interacting with the local * Bluetooth Hearing Aid service. */ - /*package*/ BluetoothHearingAid(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothHearingAid(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -181,7 +185,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.connect(device); + return service.connect(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -213,13 +217,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -240,7 +248,8 @@ public final class BluetoothHearingAid implements BluetoothProfile { final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled()) { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); @@ -262,7 +271,9 @@ public final class BluetoothHearingAid implements BluetoothProfile { final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled()) { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); @@ -285,7 +296,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; @@ -324,7 +335,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { try { if (service != null && isEnabled() && ((device == null) || isValidDevice(device))) { - service.setActiveDevice(device); + service.setActiveDevice(device, mAttributionSource); return true; } if (service == null) Log.w(TAG, "Proxy not attached to service"); @@ -352,7 +363,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { final IBluetoothHearingAid service = getService(); try { if (service != null && isEnabled()) { - return service.getActiveDevices(); + return service.getActiveDevices(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<>(); @@ -413,7 +424,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -433,7 +444,11 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -451,7 +466,11 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); verifyDeviceNotNull(device, "getConnectionPolicy"); @@ -459,7 +478,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -511,7 +530,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (!isEnabled()) return; - service.setVolume(volume); + service.setVolume(volume, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -528,7 +547,11 @@ public final class BluetoothHearingAid implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public long getHiSyncId(@NonNull BluetoothDevice device) { if (VDBG) { log("getHiSyncId(" + device + ")"); @@ -543,7 +566,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID; - return service.getHiSyncId(device); + return service.getHiSyncId(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return HI_SYNC_ID_INVALID; @@ -568,7 +591,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.getDeviceSide(device); + return service.getDeviceSide(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return SIDE_LEFT; @@ -596,7 +619,7 @@ public final class BluetoothHearingAid implements BluetoothProfile { try { if (service != null && isEnabled() && isValidDevice(device)) { - return service.getDeviceMode(device); + return service.getDeviceMode(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return MODE_MONAURAL; diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java index 6565ec0566bf..11e5711eff1a 100644 --- a/core/java/android/bluetooth/BluetoothHidDevice.java +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -25,6 +25,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SystemApi; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -415,7 +416,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { } } - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothHidDevice> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.HID_DEVICE, "BluetoothHidDevice", IBluetoothHidDevice.class.getName()) { @@ -425,8 +427,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { } }; - BluetoothHidDevice(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothHidDevice(Context context, ServiceListener listener, BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -446,7 +449,8 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -465,7 +469,9 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -484,7 +490,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -544,7 +550,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { if (service != null) { try { CallbackWrapper cbw = new CallbackWrapper(executor, callback); - result = service.registerApp(sdp, inQos, outQos, cbw); + result = service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -573,7 +579,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - result = service.unregisterApp(); + result = service.unregisterApp(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -600,7 +606,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - result = service.sendReport(device, id, data); + result = service.sendReport(device, id, data, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -628,7 +634,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - result = service.replyReport(device, type, id, data); + result = service.replyReport(device, type, id, data, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -654,7 +660,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - result = service.reportError(device, error); + result = service.reportError(device, error, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -678,7 +684,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { if (service != null) { try { - return service.getUserAppName(); + return service.getUserAppName(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -705,7 +711,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - result = service.connect(device); + result = service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -731,7 +737,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { final IBluetoothHidDevice service = getService(); if (service != null) { try { - result = service.disconnect(device); + result = service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -776,7 +782,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java index 68a9d371d3f9..0abe18c4d220 100644 --- a/core/java/android/bluetooth/BluetoothHidHost.java +++ b/core/java/android/bluetooth/BluetoothHidHost.java @@ -26,6 +26,7 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -236,7 +237,8 @@ public final class BluetoothHidHost implements BluetoothProfile { public static final String EXTRA_IDLE_TIME = "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME"; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothHidHost> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST, "BluetoothHidHost", IBluetoothHidHost.class.getName()) { @@ -250,8 +252,10 @@ public final class BluetoothHidHost implements BluetoothProfile { * Create a BluetoothHidHost proxy object for interacting with the local * Bluetooth Service which handles the InputDevice profile */ - /*package*/ BluetoothHidHost(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothHidHost(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -280,13 +284,17 @@ public final class BluetoothHidHost implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.connect(device); + return service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -318,13 +326,17 @@ public final class BluetoothHidHost implements BluetoothProfile { * @return false on immediate error, true otherwise * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -348,7 +360,8 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -371,7 +384,9 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -398,7 +413,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -419,7 +434,11 @@ public final class BluetoothHidHost implements BluetoothProfile { * @return true if priority is set, false on error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -438,7 +457,11 @@ public final class BluetoothHidHost implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); @@ -452,7 +475,7 @@ public final class BluetoothHidHost implements BluetoothProfile { return false; } try { - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -472,7 +495,11 @@ public final class BluetoothHidHost implements BluetoothProfile { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -490,7 +517,11 @@ public final class BluetoothHidHost implements BluetoothProfile { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); if (device == null) { @@ -499,7 +530,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -532,7 +563,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.virtualUnplug(device); + return service.virtualUnplug(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -559,7 +590,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getProtocolMode(device); + return service.getProtocolMode(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -584,7 +615,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.setProtocolMode(device, protocolMode); + return service.setProtocolMode(device, protocolMode, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -616,7 +647,8 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getReport(device, reportType, reportId, bufferSize); + return service.getReport(device, reportType, reportId, bufferSize, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -643,7 +675,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.setReport(device, reportType, report); + return service.setReport(device, reportType, report, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -669,7 +701,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.sendData(device, report); + return service.sendData(device, report, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -694,7 +726,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getIdleTime(device); + return service.getIdleTime(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -720,7 +752,7 @@ public final class BluetoothHidHost implements BluetoothProfile { final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.setIdleTime(device, idleTime); + return service.setIdleTime(device, idleTime, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java index 462c7b7ede66..51bfd048fdc4 100644 --- a/core/java/android/bluetooth/BluetoothLeAudio.java +++ b/core/java/android/bluetooth/BluetoothLeAudio.java @@ -26,6 +26,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -101,7 +102,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { */ public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothLeAudio> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio", IBluetoothLeAudio.class.getName()) { @@ -115,8 +117,10 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * Create a BluetoothLeAudio proxy object for interacting with the local * Bluetooth LeAudio service. */ - /*package*/ BluetoothLeAudio(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothLeAudio(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); mCloseGuard = new CloseGuard(); mCloseGuard.open("close"); @@ -162,7 +166,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { - return service.connect(device); + return service.connect(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -202,7 +206,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -223,7 +227,8 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); @@ -245,7 +250,9 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); @@ -268,7 +275,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; @@ -306,7 +313,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled() && ((device == null) || isValidDevice(device))) { - service.setActiveDevice(device); + service.setActiveDevice(device, mAttributionSource); return true; } if (service == null) Log.w(TAG, "Proxy not attached to service"); @@ -332,7 +339,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { - return service.getActiveDevices(); + return service.getActiveDevices(mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<>(); @@ -357,7 +364,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { try { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled()) { - return service.getGroupId(device); + return service.getGroupId(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return GROUP_ID_INVALID; @@ -395,7 +402,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -424,7 +431,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { final IBluetoothLeAudio service = getService(); if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index 69f9a79c7389..b13ccaf7e570 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -20,8 +20,11 @@ import android.annotation.RequiresFeature; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SystemService; +import android.app.ActivityThread; +import android.app.AppGlobals; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; +import android.content.AttributionSource; import android.content.Context; import android.content.pm.PackageManager; import android.os.RemoteException; @@ -56,16 +59,41 @@ public final class BluetoothManager { private static final String TAG = "BluetoothManager"; private static final boolean DBG = false; + private final AttributionSource mAttributionSource; private final BluetoothAdapter mAdapter; /** * @hide */ public BluetoothManager(Context context) { - mAdapter = BluetoothAdapter.createAdapter(); + mAttributionSource = resolveAttributionSource(context); + mAdapter = BluetoothAdapter.createAdapter(mAttributionSource); + } + + /** {@hide} */ + public static AttributionSource resolveAttributionSource(Context context) { + AttributionSource res = null; if (context != null) { - mAdapter.setAttributionSource(context.getAttributionSource()); + res = context.getAttributionSource(); + } + if (res == null) { + res = ActivityThread.currentAttributionSource(); + } + if (res == null) { + int uid = android.os.Process.myUid(); + if (uid == android.os.Process.ROOT_UID) { + uid = android.os.Process.SYSTEM_UID; + } + try { + res = new AttributionSource(uid, + AppGlobals.getPackageManager().getPackagesForUid(uid)[0], null); + } catch (RemoteException ignored) { + } + } + if (res == null) { + throw new IllegalStateException("Failed to resolve AttributionSource"); } + return res; } /** @@ -126,24 +154,9 @@ public final class BluetoothManager { @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List<BluetoothDevice> getConnectedDevices(int profile) { if (DBG) Log.d(TAG, "getConnectedDevices"); - if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) { - throw new IllegalArgumentException("Profile not supported: " + profile); - } - - List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>(); - - try { - IBluetoothManager managerService = mAdapter.getBluetoothManager(); - IBluetoothGatt iGatt = managerService.getBluetoothGatt(); - if (iGatt == null) return connectedDevices; - - connectedDevices = iGatt.getDevicesMatchingConnectionStates( - new int[]{BluetoothProfile.STATE_CONNECTED}); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - - return connectedDevices; + return getDevicesMatchingConnectionStates(profile, new int[] { + BluetoothProfile.STATE_CONNECTED + }); } /** @@ -180,7 +193,9 @@ public final class BluetoothManager { IBluetoothManager managerService = mAdapter.getBluetoothManager(); IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) return devices; - devices = iGatt.getDevicesMatchingConnectionStates(states); + devices = BluetoothDevice.setAttributionSource( + iGatt.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -283,7 +298,8 @@ public final class BluetoothManager { Log.e(TAG, "Fail to get GATT Server connection"); return null; } - BluetoothGattServer mGattServer = new BluetoothGattServer(iGatt, transport); + BluetoothGattServer mGattServer = + new BluetoothGattServer(iGatt, transport, mAdapter); Boolean regStatus = mGattServer.registerCallback(callback, eatt_support); return regStatus ? mGattServer : null; } catch (RemoteException e) { diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java index a025d9b4d0cd..88505b51f831 100644 --- a/core/java/android/bluetooth/BluetoothMap.java +++ b/core/java/android/bluetooth/BluetoothMap.java @@ -26,6 +26,7 @@ import android.annotation.SystemApi; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -79,7 +80,8 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { */ public static final int RESULT_CANCELED = 2; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothMap> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.MAP, "BluetoothMap", IBluetoothMap.class.getName()) { @@ -92,9 +94,11 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { /** * Create a BluetoothMap proxy object. */ - /*package*/ BluetoothMap(Context context, ServiceListener listener) { + /* package */ BluetoothMap(Context context, ServiceListener listener, + BluetoothAdapter adapter) { if (DBG) Log.d(TAG, "Create BluetoothMap proxy object"); - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); mCloseGuard = new CloseGuard(); mCloseGuard.open("close"); @@ -140,7 +144,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null) { try { - return service.getState(); + return service.getState(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -166,7 +170,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null) { try { - return service.getClient(); + return service.getClient(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -191,7 +195,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null) { try { - return service.isConnected(device); + return service.isConnected(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -230,7 +234,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -281,7 +285,8 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -305,7 +310,9 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -329,7 +336,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -388,7 +395,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { return false; } try { - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -440,7 +447,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -455,13 +462,10 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { } private boolean isEnabled() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; - log("Bluetooth is Not enabled"); - return false; + return mAdapter.isEnabled(); } + private static boolean isValidDevice(BluetoothDevice device) { return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); } - } diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java index d72081c0bac7..14804dbefeb6 100644 --- a/core/java/android/bluetooth/BluetoothMapClient.java +++ b/core/java/android/bluetooth/BluetoothMapClient.java @@ -21,12 +21,12 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.app.PendingIntent; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.net.Uri; import android.os.Binder; @@ -173,7 +173,8 @@ public final class BluetoothMapClient implements BluetoothProfile { /** @hide */ public static final int DELETED = 3; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT, "BluetoothMapClient", IBluetoothMapClient.class.getName()) { @@ -186,9 +187,11 @@ public final class BluetoothMapClient implements BluetoothProfile { /** * Create a BluetoothMapClient proxy object. */ - /*package*/ BluetoothMapClient(Context context, ServiceListener listener) { + /* package */ BluetoothMapClient(Context context, ServiceListener listener, + BluetoothAdapter adapter) { if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object"); - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -220,7 +223,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null) { try { - return service.isConnected(device); + return service.isConnected(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -247,7 +250,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null) { try { - return service.connect(device); + return service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -276,7 +279,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); } @@ -299,7 +302,8 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<>(); @@ -323,7 +327,9 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<>(); @@ -347,7 +353,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -405,7 +411,7 @@ public final class BluetoothMapClient implements BluetoothProfile { return false; } try { - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -456,7 +462,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -493,7 +499,7 @@ public final class BluetoothMapClient implements BluetoothProfile { if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendMessage(device, contacts.toArray(new Uri[contacts.size()]), - message, sentIntent, deliveredIntent); + message, sentIntent, deliveredIntent, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -527,7 +533,8 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent); + return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -553,7 +560,7 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getUnreadMessages(device); + return service.getUnreadMessages(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -576,7 +583,8 @@ public final class BluetoothMapClient implements BluetoothProfile { final IBluetoothMapClient service = getService(); try { return (service != null && isEnabled() && isValidDevice(device)) - && ((service.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0); + && ((service.getSupportedFeatures(device, mAttributionSource) + & UPLOADING_FEATURE_BITMASK) > 0); } catch (RemoteException e) { Log.e(TAG, e.getMessage()); } @@ -610,7 +618,7 @@ public final class BluetoothMapClient implements BluetoothProfile { if (service != null && isEnabled() && isValidDevice(device) && handle != null && (status == READ || status == UNREAD || status == UNDELETED || status == DELETED)) { try { - return service.setMessageStatus(device, handle, status); + return service.setMessageStatus(device, handle, status, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -620,14 +628,10 @@ public final class BluetoothMapClient implements BluetoothProfile { } private boolean isEnabled() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; - if (DBG) Log.d(TAG, "Bluetooth is Not enabled"); - return false; + return mAdapter.isEnabled(); } private static boolean isValidDevice(BluetoothDevice device) { return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); } - } diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java index c41c9dee2547..90c94de2f4ec 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -22,11 +22,12 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -183,7 +184,8 @@ public final class BluetoothPan implements BluetoothProfile { private final Context mContext; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.PAN, "BluetoothPan", IBluetoothPan.class.getName()) { @@ -201,8 +203,10 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @UnsupportedAppUsage - /*package*/ BluetoothPan(Context context, ServiceListener listener) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); + /* package */ BluetoothPan(Context context, ServiceListener listener, + BluetoothAdapter adapter) { + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mContext = context; mProfileConnector.connect(context, listener); } @@ -250,7 +254,7 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.connect(device); + return service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -290,7 +294,7 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; @@ -329,7 +333,7 @@ public final class BluetoothPan implements BluetoothProfile { && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -355,7 +359,8 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -381,7 +386,9 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -397,13 +404,17 @@ public final class BluetoothPan implements BluetoothProfile { */ @SystemApi @Override - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public int getConnectionState(@NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -432,7 +443,7 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - service.setBluetoothTethering(value, pkgName, mContext.getAttributionTag()); + service.setBluetoothTethering(value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -453,7 +464,7 @@ public final class BluetoothPan implements BluetoothProfile { final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { - return service.isTetheringOn(); + return service.isTetheringOn(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java index ef6fddf54d61..2600029362a3 100644 --- a/core/java/android/bluetooth/BluetoothPbap.java +++ b/core/java/android/bluetooth/BluetoothPbap.java @@ -25,6 +25,7 @@ import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -98,7 +99,8 @@ public class BluetoothPbap implements BluetoothProfile { private volatile IBluetoothPbap mService; private final Context mContext; private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; /** @hide */ public static final int RESULT_FAILURE = 0; @@ -129,10 +131,11 @@ public class BluetoothPbap implements BluetoothProfile { * * @hide */ - public BluetoothPbap(Context context, ServiceListener l) { + public BluetoothPbap(Context context, ServiceListener l, BluetoothAdapter adapter) { mContext = context; mServiceListener = l; - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { @@ -229,7 +232,8 @@ public class BluetoothPbap implements BluetoothProfile { return new ArrayList<BluetoothDevice>(); } try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -243,13 +247,17 @@ public class BluetoothPbap implements BluetoothProfile { */ @SystemApi @Override - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { log("getConnectionState: device=" + device); try { final IBluetoothPbap service = mService; if (service != null && isEnabled() && isValidDevice(device)) { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } if (service == null) { Log.w(TAG, "Proxy not attached to service"); @@ -277,7 +285,9 @@ public class BluetoothPbap implements BluetoothProfile { return new ArrayList<BluetoothDevice>(); } try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -317,7 +327,7 @@ public class BluetoothPbap implements BluetoothProfile { && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { return false; } - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; @@ -345,7 +355,7 @@ public class BluetoothPbap implements BluetoothProfile { return false; } try { - service.disconnect(device); + service.disconnect(device, mAttributionSource); return true; } catch (RemoteException e) { Log.e(TAG, e.toString()); diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java index 7f4863891efb..3ebd8fe19205 100644 --- a/core/java/android/bluetooth/BluetoothPbapClient.java +++ b/core/java/android/bluetooth/BluetoothPbapClient.java @@ -23,6 +23,7 @@ import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -57,7 +58,8 @@ public final class BluetoothPbapClient implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothPbapClient> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.PBAP_CLIENT, "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) { @@ -70,11 +72,12 @@ public final class BluetoothPbapClient implements BluetoothProfile { /** * Create a BluetoothPbapClient proxy object. */ - BluetoothPbapClient(Context context, ServiceListener listener) { + BluetoothPbapClient(Context context, ServiceListener listener, BluetoothAdapter adapter) { if (DBG) { Log.d(TAG, "Create BluetoothPbapClient proxy object"); } - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -111,7 +114,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean connect(BluetoothDevice device) { if (DBG) { log("connect(" + device + ") for PBAP Client."); @@ -119,7 +126,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.connect(device); + return service.connect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -139,7 +146,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean disconnect(BluetoothDevice device) { if (DBG) { log("disconnect(" + device + ")" + new Exception()); @@ -147,7 +158,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - service.disconnect(device); + service.disconnect(device, mAttributionSource); return true; } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); @@ -176,7 +187,8 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -203,7 +215,9 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -230,7 +244,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -247,12 +261,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { } private boolean isEnabled() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) { - return true; - } - log("Bluetooth is Not enabled"); - return false; + return mAdapter.isEnabled(); } private static boolean isValidDevice(BluetoothDevice device) { @@ -270,7 +279,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return true if priority is set, false on error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); @@ -288,7 +301,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setConnectionPolicy(@NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { if (DBG) { @@ -301,7 +318,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { return false; } try { - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -323,7 +340,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return priority of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); @@ -340,7 +361,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { if (VDBG) { log("getConnectionPolicy(" + device + ")"); @@ -348,7 +373,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java index b86857f42f41..0631abdadac9 100644 --- a/core/java/android/bluetooth/BluetoothSap.java +++ b/core/java/android/bluetooth/BluetoothSap.java @@ -20,11 +20,11 @@ import android.Manifest; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; -import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.Build; @@ -97,7 +97,8 @@ public final class BluetoothSap implements BluetoothProfile { */ public static final int RESULT_CANCELED = 2; - private BluetoothAdapter mAdapter; + private final BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector<IBluetoothSap> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.SAP, "BluetoothSap", IBluetoothSap.class.getName()) { @@ -110,9 +111,11 @@ public final class BluetoothSap implements BluetoothProfile { /** * Create a BluetoothSap proxy object. */ - /*package*/ BluetoothSap(Context context, ServiceListener listener) { + /* package */ BluetoothSap(Context context, ServiceListener listener, + BluetoothAdapter adapter) { if (DBG) Log.d(TAG, "Create BluetoothSap proxy object"); - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter = adapter; + mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); } @@ -154,7 +157,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null) { try { - return service.getState(); + return service.getState(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -179,7 +182,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null) { try { - return service.getClient(); + return service.getClient(mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -204,7 +207,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null) { try { - return service.isConnected(device); + return service.isConnected(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -242,7 +245,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.disconnect(device); + return service.disconnect(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -265,7 +268,8 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null && isEnabled()) { try { - return service.getConnectedDevices(); + return BluetoothDevice.setAttributionSource( + service.getConnectedDevices(mAttributionSource), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -288,7 +292,9 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null && isEnabled()) { try { - return service.getDevicesMatchingConnectionStates(states); + return BluetoothDevice.setAttributionSource( + service.getDevicesMatchingConnectionStates(states, mAttributionSource), + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); @@ -311,7 +317,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -369,7 +375,7 @@ public final class BluetoothSap implements BluetoothProfile { return false; } try { - return service.setConnectionPolicy(device, connectionPolicy); + return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return false; @@ -420,7 +426,7 @@ public final class BluetoothSap implements BluetoothProfile { final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionPolicy(device); + return service.getConnectionPolicy(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -435,17 +441,10 @@ public final class BluetoothSap implements BluetoothProfile { } private boolean isEnabled() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - - if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) { - return true; - } - log("Bluetooth is Not enabled"); - return false; + return mAdapter.isEnabled(); } private static boolean isValidDevice(BluetoothDevice device) { return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); } - } diff --git a/core/java/android/bluetooth/le/AdvertisingSet.java b/core/java/android/bluetooth/le/AdvertisingSet.java index d7e48ca543ca..caa91fb23918 100644 --- a/core/java/android/bluetooth/le/AdvertisingSet.java +++ b/core/java/android/bluetooth/le/AdvertisingSet.java @@ -18,12 +18,12 @@ package android.bluetooth.le; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; import android.bluetooth.BluetoothAdapter; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.content.AttributionSource; import android.os.RemoteException; import android.util.Log; @@ -40,11 +40,12 @@ public final class AdvertisingSet { private final IBluetoothGatt mGatt; private int mAdvertiserId; + private AttributionSource mAttributionSource; - /* package */ AdvertisingSet(int advertiserId, - IBluetoothManager bluetoothManager) { + /* package */ AdvertisingSet(int advertiserId, IBluetoothManager bluetoothManager, + AttributionSource attributionSource) { mAdvertiserId = advertiserId; - + mAttributionSource = attributionSource; try { mGatt = bluetoothManager.getBluetoothGatt(); } catch (RemoteException e) { @@ -75,7 +76,7 @@ public final class AdvertisingSet { int maxExtendedAdvertisingEvents) { try { mGatt.enableAdvertisingSet(mAdvertiserId, enable, duration, - maxExtendedAdvertisingEvents); + maxExtendedAdvertisingEvents, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -98,7 +99,7 @@ public final class AdvertisingSet { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setAdvertisingData(AdvertiseData advertiseData) { try { - mGatt.setAdvertisingData(mAdvertiserId, advertiseData); + mGatt.setAdvertisingData(mAdvertiserId, advertiseData, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -118,7 +119,7 @@ public final class AdvertisingSet { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setScanResponseData(AdvertiseData scanResponse) { try { - mGatt.setScanResponseData(mAdvertiserId, scanResponse); + mGatt.setScanResponseData(mAdvertiserId, scanResponse, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -136,7 +137,7 @@ public final class AdvertisingSet { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setAdvertisingParameters(AdvertisingSetParameters parameters) { try { - mGatt.setAdvertisingParameters(mAdvertiserId, parameters); + mGatt.setAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -152,7 +153,7 @@ public final class AdvertisingSet { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) { try { - mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters); + mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -173,7 +174,7 @@ public final class AdvertisingSet { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingData(AdvertiseData periodicData) { try { - mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData); + mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } @@ -191,7 +192,7 @@ public final class AdvertisingSet { @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingEnabled(boolean enable) { try { - mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable); + mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "remote exception - ", e); } diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java index ff279d859873..58029745ab9c 100644 --- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -26,6 +26,7 @@ import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManager; import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.content.AttributionSource; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; @@ -35,6 +36,7 @@ import android.util.Log; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * This class provides a way to perform Bluetooth LE advertise operations, such as starting and @@ -58,9 +60,11 @@ public final class BluetoothLeAdvertiser { private static final int FLAGS_FIELD_BYTES = 3; private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2; + private final BluetoothAdapter mBluetoothAdapter; private final IBluetoothManager mBluetoothManager; + private final AttributionSource mAttributionSource; + private final Handler mHandler; - private BluetoothAdapter mBluetoothAdapter; private final Map<AdvertiseCallback, AdvertisingSetCallback> mLegacyAdvertisers = new HashMap<>(); private final Map<AdvertisingSetCallback, IAdvertisingSetCallback> @@ -74,9 +78,10 @@ public final class BluetoothLeAdvertiser { * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management * @hide */ - public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) { - mBluetoothManager = bluetoothManager; - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + public BluetoothLeAdvertiser(BluetoothAdapter bluetoothAdapter) { + mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter); + mBluetoothManager = mBluetoothAdapter.getBluetoothManager(); + mAttributionSource = mBluetoothAdapter.getAttributionSource(); mHandler = new Handler(Looper.getMainLooper()); } @@ -453,7 +458,8 @@ public final class BluetoothLeAdvertiser { try { gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, duration, maxExtendedAdvertisingEvents, wrapped); + periodicData, duration, maxExtendedAdvertisingEvents, wrapped, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to start advertising set - ", e); postStartSetFailure(handler, callback, @@ -482,7 +488,7 @@ public final class BluetoothLeAdvertiser { IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); - gatt.stopAdvertisingSet(wrapped); + gatt.stopAdvertisingSet(wrapped, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to stop advertising - ", e); } @@ -600,8 +606,8 @@ public final class BluetoothLeAdvertiser { return; } - AdvertisingSet advertisingSet = - new AdvertisingSet(advertiserId, mBluetoothManager); + AdvertisingSet advertisingSet = new AdvertisingSet( + advertiserId, mBluetoothManager, mAttributionSource); mAdvertisingSets.put(advertiserId, advertisingSet); callback.onAdvertisingSetStarted(advertisingSet, txPower, status); } diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java index f27f22b9af5c..60d4e2d6d2d3 100644 --- a/core/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java @@ -41,6 +41,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; /** * This class provides methods to perform scan related operations for Bluetooth LE devices. An @@ -80,11 +81,12 @@ public final class BluetoothLeScanner { */ public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE"; + private final BluetoothAdapter mBluetoothAdapter; private final IBluetoothManager mBluetoothManager; + private final AttributionSource mAttributionSource; + private final Handler mHandler; - private BluetoothAdapter mBluetoothAdapter; private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients; - private final AttributionSource mAttributionSource; /** * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. @@ -94,13 +96,12 @@ public final class BluetoothLeScanner { * @param featureId The featureId of the context this object was created from * @hide */ - public BluetoothLeScanner(IBluetoothManager bluetoothManager, - @NonNull AttributionSource attributionSource) { - mBluetoothManager = bluetoothManager; - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + public BluetoothLeScanner(BluetoothAdapter bluetoothAdapter) { + mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter); + mBluetoothManager = mBluetoothAdapter.getBluetoothManager(); + mAttributionSource = mBluetoothAdapter.getAttributionSource(); mHandler = new Handler(Looper.getMainLooper()); mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>(); - mAttributionSource = attributionSource; } /** @@ -276,7 +277,8 @@ public final class BluetoothLeScanner { wrapper.startRegistration(); } else { try { - gatt.startScanForIntent(callbackIntent, settings, filters, mAttributionSource); + gatt.startScanForIntent(callbackIntent, settings, filters, + mAttributionSource); } catch (RemoteException e) { return ScanCallback.SCAN_FAILED_INTERNAL_ERROR; } @@ -321,7 +323,7 @@ public final class BluetoothLeScanner { IBluetoothGatt gatt; try { gatt = mBluetoothManager.getBluetoothGatt(); - gatt.stopScanForIntent(callbackIntent); + gatt.stopScanForIntent(callbackIntent, mAttributionSource); } catch (RemoteException e) { } } @@ -420,7 +422,7 @@ public final class BluetoothLeScanner { // Scan stopped. if (mScannerId == -1 || mScannerId == -2) return; try { - mBluetoothGatt.registerScanner(this, mWorkSource); + mBluetoothGatt.registerScanner(this, mWorkSource, mAttributionSource); wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); } catch (InterruptedException | RemoteException e) { Log.e(TAG, "application registeration exception", e); @@ -450,8 +452,8 @@ public final class BluetoothLeScanner { return; } try { - mBluetoothGatt.stopScan(mScannerId); - mBluetoothGatt.unregisterScanner(mScannerId); + mBluetoothGatt.stopScan(mScannerId, mAttributionSource); + mBluetoothGatt.unregisterScanner(mScannerId, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to stop scan and unregister", e); } @@ -467,7 +469,7 @@ public final class BluetoothLeScanner { return; } try { - mBluetoothGatt.flushPendingBatchResults(mScannerId); + mBluetoothGatt.flushPendingBatchResults(mScannerId, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to get pending scan results", e); } @@ -486,7 +488,7 @@ public final class BluetoothLeScanner { try { if (mScannerId == -1) { // Registration succeeds after timeout, unregister scanner. - mBluetoothGatt.unregisterScanner(scannerId); + mBluetoothGatt.unregisterScanner(scannerId, mAttributionSource); } else { mScannerId = scannerId; mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java index 26978e398072..47f47bb8e363 100644 --- a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -25,6 +25,7 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.annotations.RequiresBluetoothLocationPermission; import android.bluetooth.annotations.RequiresBluetoothScanPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; +import android.content.AttributionSource; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -32,6 +33,7 @@ import android.util.Log; import java.util.IdentityHashMap; import java.util.Map; +import java.util.Objects; /** * This class provides methods to perform periodic advertising related @@ -54,8 +56,9 @@ public final class PeriodicAdvertisingManager { private static final int SYNC_STARTING = -1; + private final BluetoothAdapter mBluetoothAdapter; private final IBluetoothManager mBluetoothManager; - private BluetoothAdapter mBluetoothAdapter; + private final AttributionSource mAttributionSource; /* maps callback, to callback wrapper and sync handle */ Map<PeriodicAdvertisingCallback, @@ -67,9 +70,10 @@ public final class PeriodicAdvertisingManager { * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. * @hide */ - public PeriodicAdvertisingManager(IBluetoothManager bluetoothManager) { - mBluetoothManager = bluetoothManager; - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + public PeriodicAdvertisingManager(BluetoothAdapter bluetoothAdapter) { + mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter); + mBluetoothManager = mBluetoothAdapter.getBluetoothManager(); + mAttributionSource = mBluetoothAdapter.getAttributionSource(); mCallbackWrappers = new IdentityHashMap<>(); } @@ -166,7 +170,8 @@ public final class PeriodicAdvertisingManager { mCallbackWrappers.put(callback, wrapped); try { - gatt.registerSync(scanResult, skip, timeout, wrapped); + gatt.registerSync( + scanResult, skip, timeout, wrapped, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to register sync - ", e); return; @@ -202,7 +207,7 @@ public final class PeriodicAdvertisingManager { } try { - gatt.unregisterSync(wrapper); + gatt.unregisterSync(wrapper, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Failed to cancel sync creation - ", e); return; diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index 2c155d5884ac..7ab731f15ad2 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -31,10 +31,9 @@ import android.permission.PermissionManager; import android.util.ArraySet; import com.android.internal.annotations.Immutable; -import com.android.internal.util.CollectionUtils; -import com.android.internal.util.DataClass; -import com.android.internal.util.Parcelling; +import java.util.Arrays; +import java.util.Collections; import java.util.Objects; import java.util.Set; @@ -70,10 +69,10 @@ import java.util.Set; * This is supported to handle cases where you don't have access to the caller's attribution * source and you can directly use the {@link AttributionSource.Builder} APIs. However, * if the data flows through more than two apps (more than you access the data for the - * caller - which you cannot know ahead of time) you need to have a handle to the {@link - * AttributionSource} for the calling app's context in order to create an attribution context. - * This means you either need to have an API for the other app to send you its attribution - * source or use a platform API that pipes the callers attribution source. + * caller) you need to have a handle to the {@link AttributionSource} for the calling app's + * context in order to create an attribution context. This means you either need to have an + * API for the other app to send you its attribution source or use a platform API that pipes + * the callers attribution source. * <p> * You cannot forge an attribution chain without the participation of every app in the * attribution chain (aside of the special case mentioned above). To create an attribution @@ -85,80 +84,11 @@ import java.util.Set; * permission protected APIs since some app in the chain may not have the permission. */ @Immutable -// TODO: Codegen doesn't properly verify the class if the parcelling is inner class -// TODO: Codegen doesn't allow overriding the constructor to change its visibility -// TODO: Codegen applies method level annotations to argument vs the generated member (@SystemApi) -// TODO: Codegen doesn't properly read/write IBinder members -// TODO: Codegen doesn't properly handle Set arguments -// TODO: Codegen requires @SystemApi annotations on fields which breaks -// android.signature.cts.api.AnnotationTest (need to update the test) -// @DataClass(genEqualsHashCode = true, genConstructor = false, genBuilder = true) public final class AttributionSource implements Parcelable { - /** - * @hide - */ - static class RenouncedPermissionsParcelling implements Parcelling<Set<String>> { - - @Override - public void parcel(Set<String> item, Parcel dest, int parcelFlags) { - if (item == null) { - dest.writeInt(-1); - } else { - dest.writeInt(item.size()); - for (String permission : item) { - dest.writeString8(permission); - } - } - } - - @Override - public Set<String> unparcel(Parcel source) { - final int size = source.readInt(); - if (size < 0) { - return null; - } - final ArraySet<String> result = new ArraySet<>(size); - for (int i = 0; i < size; i++) { - result.add(source.readString8()); - } - return result; - } - } - - /** - * The UID that is accessing the permission protected data. - */ - private final int mUid; - - /** - * The package that is accessing the permission protected data. - */ - private @Nullable String mPackageName = null; + private final @NonNull AttributionSourceState mAttributionSourceState; - /** - * The attribution tag of the app accessing the permission protected data. - */ - private @Nullable String mAttributionTag = null; - - /** - * Unique token for that source. - * - * @hide - */ - private @Nullable IBinder mToken = null; - - /** - * Permissions that should be considered revoked regardless if granted. - * - * @hide - */ - @DataClass.ParcelWith(RenouncedPermissionsParcelling.class) - private @Nullable Set<String> mRenouncedPermissions = null; - - /** - * The next app to receive the permission protected data. - */ - private @Nullable AttributionSource mNext = null; + private @Nullable AttributionSource mNextCached; + private @Nullable Set<String> mRenouncedPermissionsCached; /** @hide */ @TestApi @@ -171,8 +101,7 @@ public final class AttributionSource implements Parcelable { @TestApi public AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable AttributionSource next) { - this(uid, packageName, attributionTag, /*token*/ null, - /*renouncedPermissions*/ null, next); + this(uid, packageName, attributionTag, /*renouncedPermissions*/ null, next); } /** @hide */ @@ -180,8 +109,8 @@ public final class AttributionSource implements Parcelable { public AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable Set<String> renouncedPermissions, @Nullable AttributionSource next) { - this(uid, packageName, attributionTag, /*token*/ null, - renouncedPermissions, next); + this(uid, packageName, attributionTag, /*token*/ null, (renouncedPermissions != null) + ? renouncedPermissions.toArray(new String[0]) : null, next); } /** @hide */ @@ -191,16 +120,49 @@ public final class AttributionSource implements Parcelable { /*token*/ null, /*renouncedPermissions*/ null, next); } + AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag, + @Nullable IBinder token, @Nullable String[] renouncedPermissions, + @Nullable AttributionSource next) { + mAttributionSourceState = new AttributionSourceState(); + mAttributionSourceState.uid = uid; + mAttributionSourceState.packageName = packageName; + mAttributionSourceState.attributionTag = attributionTag; + mAttributionSourceState.token = token; + mAttributionSourceState.renouncedPermissions = renouncedPermissions; + mAttributionSourceState.next = (next != null) ? new AttributionSourceState[] + {next.mAttributionSourceState} : null; + } + + AttributionSource(@NonNull Parcel in) { + this(AttributionSourceState.CREATOR.createFromParcel(in)); + } + + /** @hide */ + public AttributionSource(@NonNull AttributionSourceState attributionSourceState) { + mAttributionSourceState = attributionSourceState; + } + /** @hide */ public AttributionSource withNextAttributionSource(@Nullable AttributionSource next) { - return new AttributionSource(mUid, mPackageName, mAttributionTag, mToken, - mRenouncedPermissions, next); + return new AttributionSource(getUid(), getPackageName(), getAttributionTag(), + getToken(), mAttributionSourceState.renouncedPermissions, next); } /** @hide */ public AttributionSource withToken(@Nullable IBinder token) { - return new AttributionSource(mUid, mPackageName, mAttributionTag, token, - mRenouncedPermissions, mNext); + return new AttributionSource(getUid(), getPackageName(), getAttributionTag(), + token, mAttributionSourceState.renouncedPermissions, getNext()); + } + + /** @hide */ + public AttributionSource withPackageName(@Nullable String packageName) { + return new AttributionSource(getUid(), packageName, getAttributionTag(), getToken(), + mAttributionSourceState.renouncedPermissions, getNext()); + } + + /** @hide */ + public @NonNull AttributionSourceState asState() { + return mAttributionSourceState; } /** @@ -213,10 +175,9 @@ public final class AttributionSource implements Parcelable { * from the caller. */ public void enforceCallingUid() { - final int callingUid = Binder.getCallingUid(); - if (callingUid != Process.SYSTEM_UID && callingUid != mUid) { - throw new SecurityException("Calling uid: " + callingUid - + " doesn't match source uid: " + mUid); + if (!checkCallingUid()) { + throw new SecurityException("Calling uid: " + Binder.getCallingUid() + + " doesn't match source uid: " + mAttributionSourceState.uid); } // No need to check package as app ops manager does it already. } @@ -231,7 +192,8 @@ public final class AttributionSource implements Parcelable { */ public boolean checkCallingUid() { final int callingUid = Binder.getCallingUid(); - if (callingUid != Process.SYSTEM_UID && callingUid != mUid) { + if (callingUid != Process.SYSTEM_UID + && callingUid != mAttributionSourceState.uid) { return false; } // No need to check package as app ops manager does it already. @@ -242,11 +204,12 @@ public final class AttributionSource implements Parcelable { public String toString() { if (Build.IS_DEBUGGABLE) { return "AttributionSource { " + - "uid = " + mUid + ", " + - "packageName = " + mPackageName + ", " + - "attributionTag = " + mAttributionTag + ", " + - "token = " + mToken + ", " + - "next = " + mNext + + "uid = " + mAttributionSourceState.uid + ", " + + "packageName = " + mAttributionSourceState.packageName + ", " + + "attributionTag = " + mAttributionSourceState.attributionTag + ", " + + "token = " + mAttributionSourceState.token + ", " + + "next = " + (mAttributionSourceState.next != null + ? mAttributionSourceState.next[0]: null) + " }"; } return super.toString(); @@ -258,8 +221,8 @@ public final class AttributionSource implements Parcelable { * @hide */ public int getNextUid() { - if (mNext != null) { - return mNext.getUid(); + if (mAttributionSourceState.next != null) { + return mAttributionSourceState.next[0].uid; } return Process.INVALID_UID; } @@ -270,8 +233,8 @@ public final class AttributionSource implements Parcelable { * @hide */ public @Nullable String getNextPackageName() { - if (mNext != null) { - return mNext.getPackageName(); + if (mAttributionSourceState.next != null) { + return mAttributionSourceState.next[0].packageName; } return null; } @@ -283,8 +246,8 @@ public final class AttributionSource implements Parcelable { * @hide */ public @Nullable String getNextAttributionTag() { - if (mNext != null) { - return mNext.getAttributionTag(); + if (mAttributionSourceState.next != null) { + return mAttributionSourceState.next[0].attributionTag; } return null; } @@ -297,8 +260,9 @@ public final class AttributionSource implements Parcelable { * @return Whether this is a trusted source. */ public boolean isTrusted(@NonNull Context context) { - return mToken != null && context.getSystemService(PermissionManager.class) - .isRegisteredAttributionSource(this); + return mAttributionSourceState.token != null + && context.getSystemService(PermissionManager.class) + .isRegisteredAttributionSource(this); } /** @@ -310,71 +274,36 @@ public final class AttributionSource implements Parcelable { @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) @NonNull public Set<String> getRenouncedPermissions() { - return CollectionUtils.emptyIfNull(mRenouncedPermissions); - } - - @DataClass.Suppress({"setUid", "setToken"}) - static class BaseBuilder {} - - - - - - - // Code below generated by codegen v1.0.22. - // - // DO NOT MODIFY! - // CHECKSTYLE:OFF Generated code - // - // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/AttributionSource.java - // - // To exclude the generated code from IntelliJ auto-formatting enable (one-time): - // Settings > Editor > Code Style > Formatter Control - //@formatter:off - - - /* package-private */ AttributionSource( - int uid, - @Nullable String packageName, - @Nullable String attributionTag, - @Nullable IBinder token, - @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) @Nullable Set<String> renouncedPermissions, - @Nullable AttributionSource next) { - this.mUid = uid; - this.mPackageName = packageName; - this.mAttributionTag = attributionTag; - this.mToken = token; - this.mRenouncedPermissions = renouncedPermissions; - com.android.internal.util.AnnotationValidations.validate( - SystemApi.class, null, mRenouncedPermissions); - com.android.internal.util.AnnotationValidations.validate( - RequiresPermission.class, null, mRenouncedPermissions, - "value", android.Manifest.permission.RENOUNCE_PERMISSIONS); - this.mNext = next; - - // onConstructed(); // You can define this method to get a callback + if (mRenouncedPermissionsCached == null) { + if (mAttributionSourceState.renouncedPermissions != null) { + mRenouncedPermissionsCached = new ArraySet<>( + mAttributionSourceState.renouncedPermissions); + } else { + mRenouncedPermissionsCached = Collections.emptySet(); + } + } + return mRenouncedPermissionsCached; } /** * The UID that is accessing the permission protected data. */ public int getUid() { - return mUid; + return mAttributionSourceState.uid; } /** * The package that is accessing the permission protected data. */ public @Nullable String getPackageName() { - return mPackageName; + return mAttributionSourceState.packageName; } /** * The attribution tag of the app accessing the permission protected data. */ public @Nullable String getAttributionTag() { - return mAttributionTag; + return mAttributionSourceState.attributionTag; } /** @@ -383,113 +312,56 @@ public final class AttributionSource implements Parcelable { * @hide */ public @Nullable IBinder getToken() { - return mToken; + return mAttributionSourceState.token; } /** * The next app to receive the permission protected data. */ public @Nullable AttributionSource getNext() { - return mNext; + if (mNextCached == null && mAttributionSourceState.next != null) { + mNextCached = new AttributionSource(mAttributionSourceState.next[0]); + } + return mNextCached; } @Override public boolean equals(@Nullable Object o) { - // You can override field equality logic by defining either of the methods like: - // boolean fieldNameEquals(AttributionSource other) { ... } - // boolean fieldNameEquals(FieldType otherValue) { ... } - if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - @SuppressWarnings("unchecked") AttributionSource that = (AttributionSource) o; - //noinspection PointlessBooleanExpression - return true - && mUid == that.mUid - && Objects.equals(mPackageName, that.mPackageName) - && Objects.equals(mAttributionTag, that.mAttributionTag) - && Objects.equals(mToken, that.mToken) - && Objects.equals(mRenouncedPermissions, that.mRenouncedPermissions) - && Objects.equals(mNext, that.mNext); + return mAttributionSourceState.uid == that.mAttributionSourceState.uid + && Objects.equals(mAttributionSourceState.packageName, + that.mAttributionSourceState.packageName) + && Objects.equals(mAttributionSourceState.attributionTag, + that.mAttributionSourceState.attributionTag) + && Objects.equals(mAttributionSourceState.token, + that.mAttributionSourceState.token) + && Arrays.equals(mAttributionSourceState.renouncedPermissions, + that.mAttributionSourceState.renouncedPermissions) + && Objects.equals(getNext(), that.getNext()); } @Override public int hashCode() { - // You can override field hashCode logic by defining methods like: - // int fieldNameHashCode() { ... } - int _hash = 1; - _hash = 31 * _hash + mUid; - _hash = 31 * _hash + Objects.hashCode(mPackageName); - _hash = 31 * _hash + Objects.hashCode(mAttributionTag); - _hash = 31 * _hash + Objects.hashCode(mToken); - _hash = 31 * _hash + Objects.hashCode(mRenouncedPermissions); - _hash = 31 * _hash + Objects.hashCode(mNext); + _hash = 31 * _hash + mAttributionSourceState.uid; + _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.packageName); + _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.attributionTag); + _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.token); + _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.renouncedPermissions); + _hash = 31 * _hash + Objects.hashCode(getNext()); return _hash; } - static Parcelling<Set<String>> sParcellingForRenouncedPermissions = - Parcelling.Cache.get( - RenouncedPermissionsParcelling.class); - static { - if (sParcellingForRenouncedPermissions == null) { - sParcellingForRenouncedPermissions = Parcelling.Cache.put( - new RenouncedPermissionsParcelling()); - } - } - @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - // You can override field parcelling by defining methods like: - // void parcelFieldName(Parcel dest, int flags) { ... } - - byte flg = 0; - if (mPackageName != null) flg |= 0x2; - if (mAttributionTag != null) flg |= 0x4; - if (mToken != null) flg |= 0x8; - if (mRenouncedPermissions != null) flg |= 0x10; - if (mNext != null) flg |= 0x20; - dest.writeByte(flg); - dest.writeInt(mUid); - if (mPackageName != null) dest.writeString(mPackageName); - if (mAttributionTag != null) dest.writeString(mAttributionTag); - if (mToken != null) dest.writeStrongBinder(mToken); - sParcellingForRenouncedPermissions.parcel(mRenouncedPermissions, dest, flags); - if (mNext != null) dest.writeTypedObject(mNext, flags); + mAttributionSourceState.writeToParcel(dest, flags); } @Override public int describeContents() { return 0; } - /** @hide */ - @SuppressWarnings({"unchecked", "RedundantCast"}) - /* package-private */ AttributionSource(@NonNull Parcel in) { - // You can override field unparcelling by defining methods like: - // static FieldType unparcelFieldName(Parcel in) { ... } - - byte flg = in.readByte(); - int uid = in.readInt(); - String packageName = (flg & 0x2) == 0 ? null : in.readString(); - String attributionTag = (flg & 0x4) == 0 ? null : in.readString(); - IBinder token = (flg & 0x8) == 0 ? null : in.readStrongBinder(); - Set<String> renouncedPermissions = sParcellingForRenouncedPermissions.unparcel(in); - AttributionSource next = (flg & 0x20) == 0 ? null : (AttributionSource) in.readTypedObject(AttributionSource.CREATOR); - - this.mUid = uid; - this.mPackageName = packageName; - this.mAttributionTag = attributionTag; - this.mToken = token; - this.mRenouncedPermissions = renouncedPermissions; - com.android.internal.util.AnnotationValidations.validate( - SystemApi.class, null, mRenouncedPermissions); - com.android.internal.util.AnnotationValidations.validate( - RequiresPermission.class, null, mRenouncedPermissions, - "value", android.Manifest.permission.RENOUNCE_PERMISSIONS); - this.mNext = next; - - // onConstructed(); // You can define this method to get a callback - } - public static final @NonNull Parcelable.Creator<AttributionSource> CREATOR = new Parcelable.Creator<AttributionSource>() { @Override @@ -506,15 +378,9 @@ public final class AttributionSource implements Parcelable { /** * A builder for {@link AttributionSource} */ - @SuppressWarnings("WeakerAccess") - public static final class Builder extends BaseBuilder { - - private int mUid; - private @Nullable String mPackageName; - private @Nullable String mAttributionTag; - private @Nullable IBinder mToken; - private @Nullable Set<String> mRenouncedPermissions; - private @Nullable AttributionSource mNext; + public static final class Builder { + private @NonNull final AttributionSourceState mAttributionSourceState = + new AttributionSourceState(); private long mBuilderFieldsSet = 0L; @@ -524,9 +390,8 @@ public final class AttributionSource implements Parcelable { * @param uid * The UID that is accessing the permission protected data. */ - public Builder( - int uid) { - mUid = uid; + public Builder(int uid) { + mAttributionSourceState.uid = uid; } /** @@ -535,7 +400,7 @@ public final class AttributionSource implements Parcelable { public @NonNull Builder setPackageName(@Nullable String value) { checkNotUsed(); mBuilderFieldsSet |= 0x2; - mPackageName = value; + mAttributionSourceState.packageName = value; return this; } @@ -545,7 +410,7 @@ public final class AttributionSource implements Parcelable { public @NonNull Builder setAttributionTag(@Nullable String value) { checkNotUsed(); mBuilderFieldsSet |= 0x4; - mAttributionTag = value; + mAttributionSourceState.attributionTag = value; return this; } @@ -578,7 +443,8 @@ public final class AttributionSource implements Parcelable { public @NonNull Builder setRenouncedPermissions(@Nullable Set<String> value) { checkNotUsed(); mBuilderFieldsSet |= 0x10; - mRenouncedPermissions = value; + mAttributionSourceState.renouncedPermissions = (value != null) + ? value.toArray(new String[0]) : null; return this; } @@ -588,7 +454,8 @@ public final class AttributionSource implements Parcelable { public @NonNull Builder setNext(@Nullable AttributionSource value) { checkNotUsed(); mBuilderFieldsSet |= 0x20; - mNext = value; + mAttributionSourceState.next = (value != null) ? new AttributionSourceState[] + {value.mAttributionSourceState} : null; return this; } @@ -598,28 +465,21 @@ public final class AttributionSource implements Parcelable { mBuilderFieldsSet |= 0x40; // Mark builder used if ((mBuilderFieldsSet & 0x2) == 0) { - mPackageName = null; + mAttributionSourceState.packageName = null; } if ((mBuilderFieldsSet & 0x4) == 0) { - mAttributionTag = null; + mAttributionSourceState.attributionTag = null; } if ((mBuilderFieldsSet & 0x8) == 0) { - mToken = null; + mAttributionSourceState.token = null; } if ((mBuilderFieldsSet & 0x10) == 0) { - mRenouncedPermissions = null; + mAttributionSourceState.renouncedPermissions = null; } if ((mBuilderFieldsSet & 0x20) == 0) { - mNext = null; + mAttributionSourceState.next = null; } - AttributionSource o = new AttributionSource( - mUid, - mPackageName, - mAttributionTag, - mToken, - mRenouncedPermissions, - mNext); - return o; + return new AttributionSource(mAttributionSourceState); } private void checkNotUsed() { @@ -629,9 +489,4 @@ public final class AttributionSource implements Parcelable { } } } - - - //@formatter:on - // End of generated code - } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 13cc748478ff..6a224915ccbc 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2219,6 +2219,26 @@ public abstract class Context { } /** + * Version of {@link #sendBroadcastMultiplePermissions(Intent, String[])} that allows you to + * specify the {@link android.app.BroadcastOptions}. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermissions Array of names of permissions that a receiver must hold + * in order to receive your broadcast. + * If empty, no permissions are required. + * @param options Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * @see #sendBroadcastMultiplePermissions(Intent, String[]) + * @see android.app.BroadcastOptions + * @hide + */ + public void sendBroadcastMultiplePermissions(@NonNull Intent intent, + @NonNull String[] receiverPermissions, @Nullable Bundle options) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** * Broadcast the given intent to all interested BroadcastReceivers, allowing * an array of required permissions to be enforced. This call is asynchronous; it returns * immediately, and you will continue executing while the receivers are run. No results are diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index dddcbea63872..6324d0ecb0e0 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -500,6 +500,13 @@ public class ContextWrapper extends Context { /** @hide */ @Override + public void sendBroadcastMultiplePermissions(@NonNull Intent intent, + @NonNull String[] receiverPermissions, @Nullable Bundle options) { + mBase.sendBroadcastMultiplePermissions(intent, receiverPermissions, options); + } + + /** @hide */ + @Override public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user, String[] receiverPermissions) { mBase.sendBroadcastAsUserMultiplePermissions(intent, user, receiverPermissions); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 39933a9ee7b4..b498325b602b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2189,6 +2189,8 @@ public class Intent implements Parcelable, Cloneable { * Type: String * </p> * + * E.g. {@link android.Manifest.permission_group.CONTACTS} + * * @hide */ @SystemApi @@ -5339,6 +5341,8 @@ public class Intent implements Parcelable, Cloneable { * A String[] holding attribution tags when used with * {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD} * + * E.g. an attribution tag could be location_provider, com.google.android.gms.*, etc. + * * @hide */ @SystemApi diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS index 1735aa2a338d..8ad134971e0e 100644 --- a/core/java/android/content/OWNERS +++ b/core/java/android/content/OWNERS @@ -1,6 +1,7 @@ # Remain no owner because multiple modules may touch this file. per-file Context.java = * per-file ContextWrapper.java = * +per-file Content* = varunshah@google.com, omakoto@google.com, jsharkey@google.com per-file IntentFilter.java = toddke@google.com per-file IntentFilter.java = patb@google.com per-file Intent.java = toddke@google.com diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java index 5089f30585b4..66e088359459 100644 --- a/core/java/android/content/PermissionChecker.java +++ b/core/java/android/content/PermissionChecker.java @@ -16,21 +16,19 @@ package android.content; -import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; -import android.content.pm.PackageManager; -import android.content.pm.PermissionInfo; import android.os.Binder; +import android.os.IBinder; import android.os.Process; -import android.util.Slog; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.permission.IPermissionChecker; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; /** * This class provides permission check APIs that verify both the @@ -72,34 +70,44 @@ import java.util.concurrent.ConcurrentHashMap; * @hide */ public final class PermissionChecker { - private static final String LOG_TAG = PermissionChecker.class.getName(); - - private static final String PLATFORM_PACKAGE_NAME = "android"; - - /** The permission is granted. */ - public static final int PERMISSION_GRANTED = AppOpsManager.MODE_ALLOWED; + /** + * The permission is granted. + * + * @hide + */ + public static final int PERMISSION_GRANTED = IPermissionChecker.PERMISSION_GRANTED; - /** Only for runtime permissions, its returned when the runtime permission - * is granted, but the corresponding app op is denied. */ - public static final int PERMISSION_SOFT_DENIED = AppOpsManager.MODE_IGNORED; + /** + * The permission is denied. Applicable only to runtime and app op permissions. + * + * <p>Returned when: + * <ul> + * <li>the runtime permission is granted, but the corresponding app op is denied + * for runtime permissions.</li> + * <li>the app ops is ignored for app op permissions.</li> + * </ul> + * + * @hide + */ + public static final int PERMISSION_SOFT_DENIED = IPermissionChecker.PERMISSION_SOFT_DENIED; - /** Returned when: + /** + * The permission is denied. + * + * <p>Returned when: * <ul> - * <li>For non app op permissions, returned when the permission is denied.</li> - * <li>For app op permissions, returned when the app op is denied or app op is - * {@link AppOpsManager#MODE_DEFAULT} and permission is denied.</li> + * <li>the permission is denied for non app op permissions.</li> + * <li>the app op is denied or app op is {@link AppOpsManager#MODE_DEFAULT} + * and permission is denied.</li> * </ul> * + * @hide */ - public static final int PERMISSION_HARD_DENIED = AppOpsManager.MODE_ERRORED; + public static final int PERMISSION_HARD_DENIED = IPermissionChecker.PERMISSION_HARD_DENIED; /** Constant when the PID for which we check permissions is unknown. */ public static final int PID_UNKNOWN = -1; - // Cache for platform defined runtime permissions to avoid multi lookup (name -> info) - private static final ConcurrentHashMap<String, PermissionInfo> sPlatformPermissions - = new ConcurrentHashMap<>(); - /** @hide */ @IntDef({PERMISSION_GRANTED, PERMISSION_SOFT_DENIED, @@ -107,6 +115,8 @@ public final class PermissionChecker { @Retention(RetentionPolicy.SOURCE) public @interface PermissionResult {} + private static volatile IPermissionChecker sService; + private PermissionChecker() { /* do nothing */ } @@ -232,7 +242,7 @@ public final class PermissionChecker { public static int checkPermissionForDataDeliveryFromDataSource(@NonNull Context context, @NonNull String permission, int pid, @NonNull AttributionSource attributionSource, @Nullable String message) { - return checkPermissionForDataDeliveryCommon(context, permission, pid, attributionSource, + return checkPermissionForDataDeliveryCommon(context, permission, attributionSource, message, false /*startDataDelivery*/, /*fromDatasource*/ true); } @@ -307,21 +317,23 @@ public final class PermissionChecker { public static int checkPermissionForDataDelivery(@NonNull Context context, @NonNull String permission, int pid, @NonNull AttributionSource attributionSource, @Nullable String message, boolean startDataDelivery) { - return checkPermissionForDataDeliveryCommon(context, permission, pid, attributionSource, + return checkPermissionForDataDeliveryCommon(context, permission, attributionSource, message, startDataDelivery, /*fromDatasource*/ false); } private static int checkPermissionForDataDeliveryCommon(@NonNull Context context, - @NonNull String permission, int pid, @NonNull AttributionSource attributionSource, + @NonNull String permission, @NonNull AttributionSource attributionSource, @Nullable String message, boolean startDataDelivery, boolean fromDatasource) { // If the check failed in the middle of the chain, finish any started op. - final int result = checkPermissionCommon(context, permission, attributionSource, - message, true /*forDataDelivery*/, startDataDelivery, fromDatasource); - if (startDataDelivery && result != PERMISSION_GRANTED) { - finishDataDelivery(context, AppOpsManager.permissionToOp(permission), - attributionSource); + try { + final int result = getPermissionCheckerService().checkPermission(permission, + attributionSource.asState(), message, true /*forDataDelivery*/, + startDataDelivery, fromDatasource); + return result; + } catch (RemoteException e) { + e.rethrowFromSystemServer(); } - return result; + return PERMISSION_HARD_DENIED; } /** @@ -356,9 +368,14 @@ public final class PermissionChecker { public static int checkPermissionAndStartDataDelivery(@NonNull Context context, @NonNull String permission, @NonNull AttributionSource attributionSource, @Nullable String message) { - return checkPermissionCommon(context, permission, attributionSource, - message, true /*forDataDelivery*/, /*startDataDelivery*/ true, - /*fromDatasource*/ false); + try { + return getPermissionCheckerService().checkPermission(permission, + attributionSource.asState(), message, true /*forDataDelivery*/, + /*startDataDelivery*/ true, /*fromDatasource*/ false); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return PERMISSION_HARD_DENIED; } /** @@ -390,13 +407,14 @@ public final class PermissionChecker { public static int startOpForDataDelivery(@NonNull Context context, @NonNull String opName, @NonNull AttributionSource attributionSource, @Nullable String message) { - final int result = checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource, - message, true /*forDataDelivery*/, true /*startDataDelivery*/); - // It is important to finish any started op if some step in the attribution chain failed. - if (result != PERMISSION_GRANTED) { - finishDataDelivery(context, opName, attributionSource); + try { + return getPermissionCheckerService().checkOp( + AppOpsManager.strOpToOp(opName), attributionSource.asState(), message, + true /*forDataDelivery*/, true /*startDataDelivery*/); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); } - return result; + return PERMISSION_HARD_DENIED; } /** @@ -412,15 +430,10 @@ public final class PermissionChecker { */ public static void finishDataDelivery(@NonNull Context context, @NonNull String op, @NonNull AttributionSource attributionSource) { - if (op == null || attributionSource.getPackageName() == null) { - return; - } - - final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); - appOpsManager.finishProxyOp(op, attributionSource); - - if (attributionSource.getNext() != null) { - finishDataDelivery(context, op, attributionSource.getNext()); + try { + getPermissionCheckerService().finishDataDelivery(op, attributionSource.asState()); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); } } @@ -456,8 +469,14 @@ public final class PermissionChecker { public static int checkOpForPreflight(@NonNull Context context, @NonNull String opName, @NonNull AttributionSource attributionSource, @Nullable String message) { - return checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource, - message, false /*forDataDelivery*/, false /*startDataDelivery*/); + try { + return getPermissionCheckerService().checkOp(AppOpsManager.strOpToOp(opName), + attributionSource.asState(), message, false /*forDataDelivery*/, + false /*startDataDelivery*/); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return PERMISSION_HARD_DENIED; } /** @@ -489,8 +508,14 @@ public final class PermissionChecker { public static int checkOpForDataDelivery(@NonNull Context context, @NonNull String opName, @NonNull AttributionSource attributionSource, @Nullable String message) { - return checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource, - message, true /*forDataDelivery*/, false /*startDataDelivery*/); + try { + return getPermissionCheckerService().checkOp(AppOpsManager.strOpToOp(opName), + attributionSource.asState(), message, true /*forDataDelivery*/, + false /*startDataDelivery*/); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return PERMISSION_HARD_DENIED; } /** @@ -561,9 +586,14 @@ public final class PermissionChecker { @PermissionResult public static int checkPermissionForPreflight(@NonNull Context context, @NonNull String permission, @NonNull AttributionSource attributionSource) { - return checkPermissionCommon(context, permission, attributionSource, - null /*message*/, false /*forDataDelivery*/, /*startDataDelivery*/ false, - /*fromDatasource*/ false); + try { + return getPermissionCheckerService().checkPermission(permission, + attributionSource.asState(), null /*message*/, false /*forDataDelivery*/, + /*startDataDelivery*/ false, /*fromDatasource*/ false); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return PERMISSION_HARD_DENIED; } /** @@ -798,356 +828,12 @@ public final class PermissionChecker { Binder.getCallingUid(), packageName); } - @PermissionResult - private static int checkPermissionCommon(@NonNull Context context, @NonNull String permission, - @NonNull AttributionSource attributionSource, - @Nullable String message, boolean forDataDelivery, boolean startDataDelivery, - boolean fromDatasource) { - PermissionInfo permissionInfo = sPlatformPermissions.get(permission); - - if (permissionInfo == null) { - try { - permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0); - if (PLATFORM_PACKAGE_NAME.equals(permissionInfo.packageName)) { - // Double addition due to concurrency is fine - the backing store is concurrent. - sPlatformPermissions.put(permission, permissionInfo); - } - } catch (PackageManager.NameNotFoundException ignored) { - return PERMISSION_HARD_DENIED; - } - } - - if (permissionInfo.isAppOp()) { - return checkAppOpPermission(context, permission, attributionSource, message, - forDataDelivery, fromDatasource); - } - if (permissionInfo.isRuntime()) { - return checkRuntimePermission(context, permission, attributionSource, message, - forDataDelivery, startDataDelivery, fromDatasource); - } - - if (!fromDatasource && !checkPermission(context, permission, attributionSource.getUid(), - attributionSource.getRenouncedPermissions())) { - return PERMISSION_HARD_DENIED; - } - - if (attributionSource.getNext() != null) { - return checkPermissionCommon(context, permission, - attributionSource.getNext(), message, forDataDelivery, - startDataDelivery, /*fromDatasource*/ false); - } - - return PERMISSION_GRANTED; - } - - @PermissionResult - private static int checkAppOpPermission(@NonNull Context context, @NonNull String permission, - @NonNull AttributionSource attributionSource, @Nullable String message, - boolean forDataDelivery, boolean fromDatasource) { - final int op = AppOpsManager.permissionToOpCode(permission); - if (op < 0) { - Slog.wtf(LOG_TAG, "Appop permission " + permission + " with no app op defined!"); - return PERMISSION_HARD_DENIED; - } - - AttributionSource current = attributionSource; - AttributionSource next = null; - - while (true) { - final boolean skipCurrentChecks = (fromDatasource || next != null); - - next = current.getNext(); - - // If the call is from a datasource we need to vet only the chain before it. This - // way we can avoid the datasource creating an attribution context for every call. - if (!(fromDatasource && current == attributionSource) - && next != null && !current.isTrusted(context)) { - return PERMISSION_HARD_DENIED; - } - - // The access is for oneself if this is the single receiver of data - // after the data source or if this is the single attribution source - // in the chain if not from a datasource. - final boolean singleReceiverFromDatasource = (fromDatasource - && current == attributionSource && next != null && next.getNext() == null); - final boolean selfAccess = singleReceiverFromDatasource || next == null; - - final int opMode = performOpTransaction(context, op, current, message, - forDataDelivery, /*startDataDelivery*/ false, skipCurrentChecks, - selfAccess, singleReceiverFromDatasource); - - switch (opMode) { - case AppOpsManager.MODE_IGNORED: - case AppOpsManager.MODE_ERRORED: { - return PERMISSION_HARD_DENIED; - } - case AppOpsManager.MODE_DEFAULT: { - if (!skipCurrentChecks && !checkPermission(context, permission, - attributionSource.getUid(), attributionSource - .getRenouncedPermissions())) { - return PERMISSION_HARD_DENIED; - } - if (next != null && !checkPermission(context, permission, - next.getUid(), next.getRenouncedPermissions())) { - return PERMISSION_HARD_DENIED; - } - } - } - - if (next == null || next.getNext() == null) { - return PERMISSION_GRANTED; - } - - current = next; - } - } - - private static int checkRuntimePermission(@NonNull Context context, @NonNull String permission, - @NonNull AttributionSource attributionSource, @Nullable String message, - boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) { - // Now let's check the identity chain... - final int op = AppOpsManager.permissionToOpCode(permission); - - AttributionSource current = attributionSource; - AttributionSource next = null; - - while (true) { - final boolean skipCurrentChecks = (fromDatasource || next != null); - next = current.getNext(); - - // If the call is from a datasource we need to vet only the chain before it. This - // way we can avoid the datasource creating an attribution context for every call. - if (!(fromDatasource && current == attributionSource) - && next != null && !current.isTrusted(context)) { - return PERMISSION_HARD_DENIED; - } - - // If we already checked the permission for this one, skip the work - if (!skipCurrentChecks && !checkPermission(context, permission, - current.getUid(), current.getRenouncedPermissions())) { - return PERMISSION_HARD_DENIED; - } - - if (next != null && !checkPermission(context, permission, - next.getUid(), next.getRenouncedPermissions())) { - return PERMISSION_HARD_DENIED; - } - - if (op < 0) { - // Bg location is one-off runtime modifier permission and has no app op - if (sPlatformPermissions.contains(permission) - && !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission)) { - Slog.wtf(LOG_TAG, "Platform runtime permission " + permission - + " with no app op defined!"); - } - if (next == null) { - return PERMISSION_GRANTED; - } - current = next; - continue; - } - - // The access is for oneself if this is the single receiver of data - // after the data source or if this is the single attribution source - // in the chain if not from a datasource. - final boolean singleReceiverFromDatasource = (fromDatasource - && current == attributionSource && next != null && next.getNext() == null); - final boolean selfAccess = singleReceiverFromDatasource || next == null; - - final int opMode = performOpTransaction(context, op, current, message, - forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess, - singleReceiverFromDatasource); - - switch (opMode) { - case AppOpsManager.MODE_ERRORED: { - return PERMISSION_HARD_DENIED; - } - case AppOpsManager.MODE_IGNORED: { - return PERMISSION_SOFT_DENIED; - } - } - - if (next == null || next.getNext() == null) { - return PERMISSION_GRANTED; - } - - current = next; - } - } - - private static boolean checkPermission(@NonNull Context context, @NonNull String permission, - int uid, @NonNull Set<String> renouncedPermissions) { - final boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1, - uid) == PackageManager.PERMISSION_GRANTED; - if (permissionGranted && renouncedPermissions.contains(permission) - && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS, - /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) { - return false; - } - return permissionGranted; - } - - private static int checkOp(@NonNull Context context, @NonNull int op, - @NonNull AttributionSource attributionSource, @Nullable String message, - boolean forDataDelivery, boolean startDataDelivery) { - if (op < 0 || attributionSource.getPackageName() == null) { - return PERMISSION_HARD_DENIED; - } - - AttributionSource current = attributionSource; - AttributionSource next = null; - - while (true) { - final boolean skipCurrentChecks = (next != null); - next = current.getNext(); - - // If the call is from a datasource we need to vet only the chain before it. This - // way we can avoid the datasource creating an attribution context for every call. - if (next != null && !current.isTrusted(context)) { - return PERMISSION_HARD_DENIED; - } - - // The access is for oneself if this is the single attribution source in the chain. - final boolean selfAccess = (next == null); - - final int opMode = performOpTransaction(context, op, current, message, - forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess, - /*fromDatasource*/ false); - - switch (opMode) { - case AppOpsManager.MODE_ERRORED: { - return PERMISSION_HARD_DENIED; - } - case AppOpsManager.MODE_IGNORED: { - return PERMISSION_SOFT_DENIED; - } - } - - if (next == null || next.getNext() == null) { - return PERMISSION_GRANTED; - } - - current = next; - } - } - - private static int performOpTransaction(@NonNull Context context, int op, - @NonNull AttributionSource attributionSource, @Nullable String message, - boolean forDataDelivery, boolean startDataDelivery, boolean skipProxyOperation, - boolean selfAccess, boolean singleReceiverFromDatasource) { - // We cannot perform app ops transactions without a package name. In all relevant - // places we pass the package name but just in case there is a bug somewhere we - // do a best effort to resolve the package from the UID (pick first without a loss - // of generality - they are in the same security sandbox). - final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); - final AttributionSource accessorSource = (!singleReceiverFromDatasource) - ? attributionSource : attributionSource.getNext(); - if (!forDataDelivery) { - final String resolvedAccessorPackageName = resolvePackageName(context, accessorSource); - if (resolvedAccessorPackageName == null) { - return AppOpsManager.MODE_ERRORED; - } - final int opMode = appOpsManager.unsafeCheckOpRawNoThrow(op, - accessorSource.getUid(), resolvedAccessorPackageName); - final AttributionSource next = accessorSource.getNext(); - if (!selfAccess && opMode == AppOpsManager.MODE_ALLOWED && next != null) { - final String resolvedNextPackageName = resolvePackageName(context, next); - if (resolvedNextPackageName == null) { - return AppOpsManager.MODE_ERRORED; - } - return appOpsManager.unsafeCheckOpRawNoThrow(op, next.getUid(), - resolvedNextPackageName); - } - return opMode; - } else if (startDataDelivery) { - final AttributionSource resolvedAttributionSource = resolveAttributionSource( - context, accessorSource); - if (resolvedAttributionSource.getPackageName() == null) { - return AppOpsManager.MODE_ERRORED; - } - if (selfAccess) { - // If the datasource is not in a trusted platform component then in would not - // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that - // an app is exposing runtime permission protected data but cannot blame others - // in a trusted way which would not properly show in permission usage UIs. - // As a fallback we note a proxy op that blames the app and the datasource. - try { - return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(), - resolvedAttributionSource.getPackageName(), - /*startIfModeDefault*/ false, - resolvedAttributionSource.getAttributionTag(), - message); - } catch (SecurityException e) { - Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with" - + " platform defined runtime permission " - + AppOpsManager.opToPermission(op) + " while not having " - + Manifest.permission.UPDATE_APP_OPS_STATS); - return appOpsManager.startProxyOpNoThrow(op, attributionSource, message, - skipProxyOperation); - } - } else { - return appOpsManager.startProxyOpNoThrow(op, resolvedAttributionSource, message, - skipProxyOperation); - } - } else { - final AttributionSource resolvedAttributionSource = resolveAttributionSource( - context, accessorSource); - if (resolvedAttributionSource.getPackageName() == null) { - return AppOpsManager.MODE_ERRORED; - } - if (selfAccess) { - // If the datasource is not in a trusted platform component then in would not - // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that - // an app is exposing runtime permission protected data but cannot blame others - // in a trusted way which would not properly show in permission usage UIs. - // As a fallback we note a proxy op that blames the app and the datasource. - try { - return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(), - resolvedAttributionSource.getPackageName(), - resolvedAttributionSource.getAttributionTag(), - message); - } catch (SecurityException e) { - Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with" - + " platform defined runtime permission " - + AppOpsManager.opToPermission(op) + " while not having " - + Manifest.permission.UPDATE_APP_OPS_STATS); - return appOpsManager.noteProxyOpNoThrow(op, attributionSource, message, - skipProxyOperation); - } - } else { - return appOpsManager.noteProxyOpNoThrow(op, resolvedAttributionSource, message, - skipProxyOperation); - } - } - } - - private static @Nullable String resolvePackageName(@NonNull Context context, - @NonNull AttributionSource attributionSource) { - if (attributionSource.getPackageName() != null) { - return attributionSource.getPackageName(); - } - final String[] packageNames = context.getPackageManager().getPackagesForUid( - attributionSource.getUid()); - if (packageNames != null) { - // This is best effort if the caller doesn't pass a package. The security - // sandbox is UID, therefore we pick an arbitrary package. - return packageNames[0]; - } - // Last resort to handle special UIDs like root, etc. - return AppOpsManager.resolvePackageName(attributionSource.getUid(), - attributionSource.getPackageName()); - } - - private static @NonNull AttributionSource resolveAttributionSource( - @NonNull Context context, @NonNull AttributionSource attributionSource) { - if (attributionSource.getPackageName() != null) { - return attributionSource; + private static @NonNull IPermissionChecker getPermissionCheckerService() { + // Race is fine, we may end up looking up the same instance twice, no big deal. + if (sService == null) { + final IBinder service = ServiceManager.getService("permission_checker"); + sService = IPermissionChecker.Stub.asInterface(service); } - return new AttributionSource(attributionSource.getUid(), - resolvePackageName(context, attributionSource), - attributionSource.getAttributionTag(), - attributionSource.getToken(), - attributionSource.getRenouncedPermissions(), - attributionSource.getNext()); + return sService; } } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 7fe2a41cc4b9..5e72325eed43 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -796,6 +796,10 @@ interface IPackageManager { void setMimeGroup(String packageName, String group, in List<String> mimeTypes); + String getSplashScreenTheme(String packageName, int userId); + + void setSplashScreenTheme(String packageName, String themeName, int userId); + List<String> getMimeGroup(String packageName, String group); boolean isAutoRevokeWhitelisted(String packageName); diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 55a6ab708da5..84317b3739c5 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -81,6 +81,7 @@ public class PackageUserState { public int installReason; public @PackageManager.UninstallReason int uninstallReason; public String harmfulAppWarning; + public String splashScreenTheme; public ArraySet<String> disabledComponents; public ArraySet<String> enabledComponents; @@ -130,6 +131,7 @@ public class PackageUserState { if (o.componentLabelIconOverrideMap != null) { this.componentLabelIconOverrideMap = new ArrayMap<>(o.componentLabelIconOverrideMap); } + splashScreenTheme = o.splashScreenTheme; } @Nullable @@ -242,6 +244,7 @@ public class PackageUserState { return componentLabelIconOverrideMap.get(componentName); } + /** * Test if this package is installed. */ @@ -479,7 +482,11 @@ public class PackageUserState { } if (harmfulAppWarning == null && oldState.harmfulAppWarning != null || (harmfulAppWarning != null - && !harmfulAppWarning.equals(oldState.harmfulAppWarning))) { + && !harmfulAppWarning.equals(oldState.harmfulAppWarning))) { + return false; + } + + if (!Objects.equals(splashScreenTheme, oldState.splashScreenTheme)) { return false; } return true; @@ -505,6 +512,7 @@ public class PackageUserState { hashCode = 31 * hashCode + Objects.hashCode(disabledComponents); hashCode = 31 * hashCode + Objects.hashCode(enabledComponents); hashCode = 31 * hashCode + Objects.hashCode(harmfulAppWarning); + hashCode = 31 * hashCode + Objects.hashCode(splashScreenTheme); return hashCode; } diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java index 4385b1dac7a0..83e273a91f91 100644 --- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java @@ -16,10 +16,14 @@ package android.hardware.biometrics; +import android.annotation.IntDef; import android.app.KeyguardManager; import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.face.FaceManager; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Interface containing all of the face-specific constants. * @@ -48,6 +52,31 @@ public interface BiometricFaceConstants { // Error messages from face authentication hardware during initialization, enrollment, // authentication or removal. Must agree with the list in HAL h file // + + /** + * @hide + */ + @IntDef({FACE_ERROR_HW_UNAVAILABLE, + FACE_ERROR_UNABLE_TO_PROCESS, + FACE_ERROR_TIMEOUT, + FACE_ERROR_NO_SPACE, + FACE_ERROR_CANCELED, + FACE_ERROR_UNABLE_TO_REMOVE, + FACE_ERROR_LOCKOUT, + FACE_ERROR_VENDOR, + FACE_ERROR_LOCKOUT_PERMANENT, + FACE_ERROR_USER_CANCELED, + FACE_ERROR_NOT_ENROLLED, + FACE_ERROR_HW_NOT_PRESENT, + FACE_ERROR_NEGATIVE_BUTTON, + BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL, + BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, + BIOMETRIC_ERROR_RE_ENROLL, + FACE_ERROR_UNKNOWN + }) + @Retention(RetentionPolicy.SOURCE) + @interface FaceError {} + /** * The hardware is unavailable. Try again later. */ @@ -159,6 +188,12 @@ public interface BiometricFaceConstants { int BIOMETRIC_ERROR_RE_ENROLL = 16; /** + * Unknown error received from the HAL. + * @hide + */ + int FACE_ERROR_UNKNOWN = 17; + + /** * @hide */ int FACE_ERROR_VENDOR_BASE = 1000; @@ -169,6 +204,36 @@ public interface BiometricFaceConstants { // /** + * @hide + */ + @IntDef({FACE_ACQUIRED_GOOD, + FACE_ACQUIRED_INSUFFICIENT, + FACE_ACQUIRED_TOO_BRIGHT, + FACE_ACQUIRED_TOO_DARK, + FACE_ACQUIRED_TOO_CLOSE, + FACE_ACQUIRED_TOO_FAR, + FACE_ACQUIRED_TOO_HIGH, + FACE_ACQUIRED_TOO_LOW, + FACE_ACQUIRED_TOO_RIGHT, + FACE_ACQUIRED_TOO_LEFT, + FACE_ACQUIRED_POOR_GAZE, + FACE_ACQUIRED_NOT_DETECTED, + FACE_ACQUIRED_TOO_MUCH_MOTION, + FACE_ACQUIRED_RECALIBRATE, + FACE_ACQUIRED_TOO_DIFFERENT, + FACE_ACQUIRED_TOO_SIMILAR, + FACE_ACQUIRED_PAN_TOO_EXTREME, + FACE_ACQUIRED_TILT_TOO_EXTREME, + FACE_ACQUIRED_ROLL_TOO_EXTREME, + FACE_ACQUIRED_FACE_OBSCURED, + FACE_ACQUIRED_START, + FACE_ACQUIRED_SENSOR_DIRTY, + FACE_ACQUIRED_VENDOR, + FACE_ACQUIRED_UNKNOWN}) + @Retention(RetentionPolicy.SOURCE) + @interface FaceAcquired {} + + /** * The image acquired was good. */ int FACE_ACQUIRED_GOOD = 0; @@ -343,6 +408,12 @@ public interface BiometricFaceConstants { int FACE_ACQUIRED_VENDOR = 22; /** + * Unknown acquired code received from the HAL. + * @hide + */ + int FACE_ACQUIRED_UNKNOWN = 23; + + /** * @hide */ int FACE_ACQUIRED_VENDOR_BASE = 1000; diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java index 30e24d2ec8db..79f716cd5ce9 100644 --- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -57,7 +57,10 @@ public interface BiometricFingerprintConstants { FINGERPRINT_ERROR_HW_NOT_PRESENT, FINGERPRINT_ERROR_NEGATIVE_BUTTON, BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL, - BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED}) + BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, + BIOMETRIC_ERROR_RE_ENROLL, + BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, + FINGERPRINT_ERROR_UNKNOWN}) @Retention(RetentionPolicy.SOURCE) @interface FingerprintError {} @@ -163,7 +166,7 @@ public interface BiometricFingerprintConstants { * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}. * @hide */ - public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; + int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; /** * Authentication cannot proceed because re-enrollment is required. @@ -172,6 +175,12 @@ public interface BiometricFingerprintConstants { int BIOMETRIC_ERROR_RE_ENROLL = 16; /** + * Unknown error received from the HAL. + * @hide + */ + int FINGERPRINT_ERROR_UNKNOWN = 17; + + /** * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -191,7 +200,8 @@ public interface BiometricFingerprintConstants { FINGERPRINT_ACQUIRED_TOO_SLOW, FINGERPRINT_ACQUIRED_TOO_FAST, FINGERPRINT_ACQUIRED_VENDOR, - FINGERPRINT_ACQUIRED_START}) + FINGERPRINT_ACQUIRED_START, + FINGERPRINT_ACQUIRED_UNKNOWN}) @Retention(RetentionPolicy.SOURCE) @interface FingerprintAcquired {} @@ -255,6 +265,12 @@ public interface BiometricFingerprintConstants { int FINGERPRINT_ACQUIRED_START = 7; /** + * Unknown acquired code received from the HAL. + * @hide + */ + int FINGERPRINT_ACQUIRED_UNKNOWN = 8; + + /** * @hide */ int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index d054ee207110..948da3f6e663 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -33,6 +33,7 @@ import android.annotation.TestApi; import android.content.Context; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.security.keystore.KeyProperties; import android.util.Slog; @@ -556,9 +557,22 @@ public class BiometricManager { * @hide */ public long[] getAuthenticatorIds() { + return getAuthenticatorIds(UserHandle.getCallingUserId()); + } + + /** + * Get a list of AuthenticatorIDs for biometric authenticators which have 1) enrolled templates, + * and 2) meet the requirements for integrating with Keystore. The AuthenticatorIDs are known + * in Keystore land as SIDs, and are used during key generation. + * + * @param userId Android user ID for user to look up. + * + * @hide + */ + public long[] getAuthenticatorIds(int userId) { if (mService != null) { try { - return mService.getAuthenticatorIds(); + return mService.getAuthenticatorIds(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl index 86df0994a222..4c2a9ae10cbd 100644 --- a/core/java/android/hardware/biometrics/IAuthService.aidl +++ b/core/java/android/hardware/biometrics/IAuthService.aidl @@ -67,7 +67,9 @@ interface IAuthService { // Get a list of AuthenticatorIDs for authenticators which have enrolled templates and meet // the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore // land as SIDs, and are used during key generation. - long[] getAuthenticatorIds(); + // If userId is not equal to the calling user ID, the caller must have the + // USE_BIOMETRIC_INTERNAL permission. + long[] getAuthenticatorIds(in int userId); // See documentation in BiometricManager. void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, int userId, diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 6654c2c71049..0a124701fc0a 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -2513,7 +2513,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * android.scaler.availableInputOutputFormatsMap.</p> * <p>The following table describes the minimum required output stream * configurations based on the hardware level - * ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel}):</p> + * ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel}), prior to Android 12:</p> * <table> * <thead> * <tr> @@ -2574,6 +2574,76 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * </tr> * </tbody> * </table> + * <p>Starting from Android 12, the camera device may not support JPEG sizes smaller than the + * minimum of 1080p and the camera sensor active array size. The requirements for + * IMPLEMENTATION_DEFINED and YUV_420_888 stay the same. This new minimum required output + * stream configurations are illustrated by the table below:</p> + * <table> + * <thead> + * <tr> + * <th align="center">Format</th> + * <th align="center">Size</th> + * <th align="center">Hardware Level</th> + * <th align="center">Notes</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td align="center">JPEG</td> + * <td align="center">{@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</td> + * <td align="center">Any</td> + * <td align="center"></td> + * </tr> + * <tr> + * <td align="center">JPEG</td> + * <td align="center">1920x1080 (1080p)</td> + * <td align="center">Any</td> + * <td align="center">if 1080p <= activeArraySize</td> + * </tr> + * <tr> + * <td align="center">YUV_420_888</td> + * <td align="center">{@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</td> + * <td align="center">FULL</td> + * <td align="center"></td> + * </tr> + * <tr> + * <td align="center">YUV_420_888</td> + * <td align="center">1920x1080 (1080p)</td> + * <td align="center">FULL</td> + * <td align="center">if 1080p <= activeArraySize</td> + * </tr> + * <tr> + * <td align="center">YUV_420_888</td> + * <td align="center">1280x720 (720)</td> + * <td align="center">FULL</td> + * <td align="center">if 720p <= activeArraySize</td> + * </tr> + * <tr> + * <td align="center">YUV_420_888</td> + * <td align="center">640x480 (480p)</td> + * <td align="center">FULL</td> + * <td align="center">if 480p <= activeArraySize</td> + * </tr> + * <tr> + * <td align="center">YUV_420_888</td> + * <td align="center">320x240 (240p)</td> + * <td align="center">FULL</td> + * <td align="center">if 240p <= activeArraySize</td> + * </tr> + * <tr> + * <td align="center">YUV_420_888</td> + * <td align="center">all output sizes available for FULL hardware level, up to the maximum video size</td> + * <td align="center">LIMITED</td> + * <td align="center"></td> + * </tr> + * <tr> + * <td align="center">IMPLEMENTATION_DEFINED</td> + * <td align="center">same as YUV_420_888</td> + * <td align="center">Any</td> + * <td align="center"></td> + * </tr> + * </tbody> + * </table> * <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} for additional * mandatory stream configurations on a per-capability basis.</p> * <p>Exception on 176x144 (QCIF) resolution: camera devices usually have a fixed capability for @@ -3166,6 +3236,33 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryMaximumResolutionStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class); /** + * <p>Whether the camera device supports multi-resolution input or output streams</p> + * <p>A logical multi-camera or an ultra high resolution camera may support multi-resolution + * input or output streams. With multi-resolution output streams, the camera device is able + * to output different resolution images depending on the current active physical camera or + * pixel mode. With multi-resolution input streams, the camera device can reprocess images + * of different resolutions from different physical cameras or sensor pixel modes.</p> + * <p>When set to TRUE: + * * For a logical multi-camera, the camera framework derives + * {@link CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP android.scaler.multiResolutionStreamConfigurationMap} by combining the + * android.scaler.physicalCameraMultiResolutionStreamConfigurations from its physical + * cameras. + * * For an ultra-high resolution sensor camera, the camera framework directly copies + * the value of android.scaler.physicalCameraMultiResolutionStreamConfigurations to + * {@link CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP android.scaler.multiResolutionStreamConfigurationMap}.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * <p><b>Limited capability</b> - + * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the + * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @see CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP + * @hide + */ + public static final Key<Boolean> SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED = + new Key<Boolean>("android.scaler.multiResolutionStreamSupported", boolean.class); + + /** * <p>The area of the image sensor which corresponds to active pixels after any geometric * distortion correction has been applied.</p> * <p>This is the rectangle representing the size of the active region of the sensor (i.e. diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index fd10c57e85dc..80b5078d9fa0 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -73,6 +73,9 @@ import java.util.Objects; * {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, Class)} for supported * repeating request output sizes.</p> * + * <p>The extension characteristics for a given device are expected to remain static under + * normal operating conditions.</p> + * * @see CameraManager#getCameraExtensionCharacteristics(String) */ public final class CameraExtensionCharacteristics { @@ -585,7 +588,7 @@ public final class CameraExtensionCharacteristics { * @throws IllegalArgumentException in case of format different from {@link ImageFormat#JPEG} / * {@link ImageFormat#YUV_420_888}; or unsupported extension. */ - public @Nullable Range<Long> getEstimatedCaptureLatencyRange(@Extension int extension, + public @Nullable Range<Long> getEstimatedCaptureLatencyRangeMillis(@Extension int extension, @NonNull Size captureOutputSize, @ImageFormat.Format int format) { switch (format) { case ImageFormat.YUV_420_888: diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index c1009ffb3814..651f02591b4d 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -379,17 +379,36 @@ public final class CameraManager { * <p>For a logical multi-camera, query the map between physical camera id and * the physical camera's multi-resolution stream configuration. This map is in turn * combined to form the logical camera's multi-resolution stream configuration map.</p> + * + * <p>For an ultra high resolution camera, directly use + * android.scaler.physicalCameraMultiResolutionStreamConfigurations as the camera device's + * multi-resolution stream configuration map.</p> */ private Map<String, StreamConfiguration[]> getPhysicalCameraMultiResolutionConfigs( - CameraMetadataNative info, ICameraService cameraService) + String cameraId, CameraMetadataNative info, ICameraService cameraService) throws CameraAccessException { HashMap<String, StreamConfiguration[]> multiResolutionStreamConfigurations = new HashMap<String, StreamConfiguration[]>(); + Boolean multiResolutionStreamSupported = info.get( + CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED); + if (multiResolutionStreamSupported == null || !multiResolutionStreamSupported) { + return multiResolutionStreamConfigurations; + } + // Query the characteristics of all physical sub-cameras, and combine the multi-resolution - // stream configurations. Note that framework derived formats such as HEIC and DEPTH_JPEG - // aren't supported as multi-resolution input or output formats. + // stream configurations. Alternatively, for ultra-high resolution camera, direclty use + // its multi-resolution stream configurations. Note that framework derived formats such as + // HEIC and DEPTH_JPEG aren't supported as multi-resolution input or output formats. Set<String> physicalCameraIds = info.getPhysicalCameraIds(); + if (physicalCameraIds.size() == 0 && info.isUltraHighResolutionSensor()) { + StreamConfiguration[] configs = info.get(CameraCharacteristics. + SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS); + if (configs != null) { + multiResolutionStreamConfigurations.put(cameraId, configs); + } + return multiResolutionStreamConfigurations; + } try { for (String physicalCameraId : physicalCameraIds) { CameraMetadataNative physicalCameraInfo = @@ -401,9 +420,6 @@ public final class CameraManager { multiResolutionStreamConfigurations.put(physicalCameraId, configs); } } - - // TODO: If this is an ultra high resolution sensor camera, combine the multi-resolution - // stream combination from "info" as well. } catch (RemoteException e) { ServiceSpecificException sse = new ServiceSpecificException( ICameraService.ERROR_DISCONNECTED, @@ -468,7 +484,7 @@ public final class CameraManager { info.setDisplaySize(displaySize); Map<String, StreamConfiguration[]> multiResolutionSizeMap = - getPhysicalCameraMultiResolutionConfigs(info, cameraService); + getPhysicalCameraMultiResolutionConfigs(cameraId, info, cameraService); if (multiResolutionSizeMap.size() > 0) { info.setMultiResolutionStreamConfigurationMap(multiResolutionSizeMap); } diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java index bb3d91dbc65c..3af1b5b03d35 100644 --- a/core/java/android/hardware/camera2/MultiResolutionImageReader.java +++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java @@ -265,8 +265,18 @@ public class MultiResolutionImageReader implements AutoCloseable { * @return a {@link Surface} to use as the target for a capture request. */ public @NonNull Surface getSurface() { - //TODO: Pick the surface from the reader for default mode stream. - return mReaders[0].getSurface(); + // Pick the surface of smallest size. This is necessary for an ultra high resolution + // camera not to default to maximum resolution pixel mode. + int minReaderSize = mReaders[0].getWidth() * mReaders[0].getHeight(); + Surface candidateSurface = mReaders[0].getSurface(); + for (int i = 1; i < mReaders.length; i++) { + int readerSize = mReaders[i].getWidth() * mReaders[i].getHeight(); + if (readerSize < minReaderSize) { + minReaderSize = readerSize; + candidateSurface = mReaders[i].getSurface(); + } + } + return candidateSurface; } /** diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index aa84b024f69d..2e841f50e84d 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -1322,7 +1322,10 @@ public class CameraMetadataNative implements Parcelable { return ret; } - private boolean isUltraHighResolutionSensor() { + /** + * @hide + */ + public boolean isUltraHighResolutionSensor() { return isCapabilitySupported( CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR); diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java index d63683feed9b..8dfc0a7b41ca 100644 --- a/core/java/android/hardware/camera2/params/InputConfiguration.java +++ b/core/java/android/hardware/camera2/params/InputConfiguration.java @@ -90,7 +90,6 @@ public final class InputConfiguration { public InputConfiguration(@NonNull Collection<MultiResolutionStreamInfo> multiResolutionInputs, @Format int format) { checkCollectionNotEmpty(multiResolutionInputs, "Input multi-resolution stream info"); - //TODO: Pick the default mode stream info for ultra-high resolution sensor camera MultiResolutionStreamInfo info = multiResolutionInputs.iterator().next(); mWidth = info.getWidth(); mHeight = info.getHeight(); diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 84736dc73ad6..0662f160a009 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -353,7 +353,10 @@ public final class OutputConfiguration implements Parcelable { config.setPhysicalCameraId(streamInfo.getPhysicalCameraId()); config.setMultiResolutionOutput(); configs.add(config); - // TODO: Set sensor pixel mode for ultra high resolution sensor camera. + + // No need to call addSensorPixelModeUsed for ultra high resolution sensor camera, + // because regular and max resolution output configurations are used for DEFAULT mode + // and MAX_RESOLUTION mode respectively by default. } return configs; diff --git a/core/java/android/hardware/display/BrightnessInfo.aidl b/core/java/android/hardware/display/BrightnessInfo.aidl new file mode 100644 index 000000000000..5da55c34f3b4 --- /dev/null +++ b/core/java/android/hardware/display/BrightnessInfo.aidl @@ -0,0 +1,19 @@ +/* + * 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.display; + +parcelable BrightnessInfo; diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java new file mode 100644 index 000000000000..4289860d4e8a --- /dev/null +++ b/core/java/android/hardware/display/BrightnessInfo.java @@ -0,0 +1,109 @@ +/* + * 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.display; + +import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Data about the current brightness state. + * {@see android.view.Display.getBrightnessInfo()} + * + * @hide + */ +public final class BrightnessInfo implements Parcelable { + + @IntDef(prefix = {"HIGH_BRIGHTNESS_MODE_"}, value = { + HIGH_BRIGHTNESS_MODE_OFF, + HIGH_BRIGHTNESS_MODE_SUNLIGHT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface HighBrightnessMode {} + + /** + * High brightness mode is OFF. The high brightness range is not currently accessible to the + * user. + */ + public static final int HIGH_BRIGHTNESS_MODE_OFF = 0; + + /** + * High brightness mode is ON due to high ambient light (sunlight). The high brightness range is + * currently accessible to the user. + */ + public static final int HIGH_BRIGHTNESS_MODE_SUNLIGHT = 1; + + /** Brightness */ + public final float brightness; + + /** Current minimum supported brightness. */ + public final float brightnessMinimum; + + /** Current maximum supported brightness. */ + public final float brightnessMaximum; + + /** + * Current state of high brightness mode. + * Can be any of HIGH_BRIGHTNESS_MODE_* values. + */ + public final int highBrightnessMode; + + public BrightnessInfo(float brightness, float brightnessMinimum, float brightnessMaximum, + @HighBrightnessMode int highBrightnessMode) { + this.brightness = brightness; + this.brightnessMinimum = brightnessMinimum; + this.brightnessMaximum = brightnessMaximum; + this.highBrightnessMode = highBrightnessMode; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(brightness); + dest.writeFloat(brightnessMinimum); + dest.writeFloat(brightnessMaximum); + dest.writeInt(highBrightnessMode); + } + + public static final @android.annotation.NonNull Creator<BrightnessInfo> CREATOR = + new Creator<BrightnessInfo>() { + @Override + public BrightnessInfo createFromParcel(Parcel source) { + return new BrightnessInfo(source); + } + + @Override + public BrightnessInfo[] newArray(int size) { + return new BrightnessInfo[size]; + } + }; + + private BrightnessInfo(Parcel source) { + brightness = source.readFloat(); + brightnessMinimum = source.readFloat(); + brightnessMaximum = source.readFloat(); + highBrightnessMode = source.readInt(); + } + +} diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 6c2d1402b303..de32adb1f545 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -418,6 +418,7 @@ public final class DisplayManager { EVENT_FLAG_DISPLAY_ADDED, EVENT_FLAG_DISPLAY_CHANGED, EVENT_FLAG_DISPLAY_REMOVED, + EVENT_FLAG_DISPLAY_BRIGHTNESS }) @Retention(RetentionPolicy.SOURCE) public @interface EventsMask {} @@ -449,6 +450,17 @@ public final class DisplayManager { */ public static final long EVENT_FLAG_DISPLAY_CHANGED = 1L << 2; + /** + * Event flag to register for a display's brightness changes. This notification is sent + * through the {@link DisplayListener#onDisplayChanged} callback method. New brightness + * values can be retrieved via {@link android.view.Display#getBrightnessInfo()}. + * + * @see #registerDisplayListener(DisplayListener, Handler, long) + * + * @hide + */ + public static final long EVENT_FLAG_DISPLAY_BRIGHTNESS = 1L << 3; + /** @hide */ public DisplayManager(Context context) { mContext = context; @@ -583,6 +595,7 @@ public final class DisplayManager { * @see #EVENT_FLAG_DISPLAY_ADDED * @see #EVENT_FLAG_DISPLAY_CHANGED * @see #EVENT_FLAG_DISPLAY_REMOVED + * @see #EVENT_FLAG_DISPLAY_BRIGHTNESS * @see #registerDisplayListener(DisplayListener, Handler) * @see #unregisterDisplayListener * diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 983a43af0802..df51734bc17d 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -79,6 +79,7 @@ public final class DisplayManagerGlobal { EVENT_DISPLAY_ADDED, EVENT_DISPLAY_CHANGED, EVENT_DISPLAY_REMOVED, + EVENT_DISPLAY_BRIGHTNESS_CHANGED }) @Retention(RetentionPolicy.SOURCE) public @interface DisplayEvent {} @@ -86,6 +87,7 @@ public final class DisplayManagerGlobal { public static final int EVENT_DISPLAY_ADDED = 1; public static final int EVENT_DISPLAY_CHANGED = 2; public static final int EVENT_DISPLAY_REMOVED = 3; + public static final int EVENT_DISPLAY_BRIGHTNESS_CHANGED = 4; @UnsupportedAppUsage private static DisplayManagerGlobal sInstance; @@ -665,6 +667,17 @@ public final class DisplayManagerGlobal { } /** + * Retrieves Brightness Info for the specified display. + */ + public BrightnessInfo getBrightnessInfo(int displayId) { + try { + return mDm.getBrightnessInfo(displayId); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** * Gets the preferred wide gamut color space for all displays. * The wide gamut color space is returned from composition pipeline * based on hardware capability. @@ -934,6 +947,11 @@ public final class DisplayManagerGlobal { } } break; + case EVENT_DISPLAY_BRIGHTNESS_CHANGED: + if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0) { + mListener.onDisplayChanged(msg.arg1); + } + break; case EVENT_DISPLAY_REMOVED: if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) { mListener.onDisplayRemoved(msg.arg1); diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 5ca4e0cc657c..2303353ad101 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -19,6 +19,7 @@ package android.hardware.display; import android.content.pm.ParceledListSlice; import android.graphics.Point; import android.hardware.display.BrightnessConfiguration; +import android.hardware.display.BrightnessInfo; import android.hardware.display.Curve; import android.hardware.display.IDisplayManagerCallback; import android.hardware.display.IVirtualDisplayCallback; @@ -143,6 +144,9 @@ interface IDisplayManager { // Get the minimum brightness curve. Curve getMinimumBrightnessCurve(); + // Get Brightness Information for the specified display. + BrightnessInfo getBrightnessInfo(int displayId); + // Gets the id of the preferred wide gamut color space for all displays. // The wide gamut color space is returned from composition pipeline // based on hardware capability. diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 13e2700b3f54..5f8789993a5e 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -133,11 +133,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } @Override - public void onFeatureGet(boolean success, int feature, boolean value) { + public void onFeatureGet(boolean success, int[] features, boolean[] featureState) { SomeArgs args = SomeArgs.obtain(); args.arg1 = success; - args.argi1 = feature; - args.arg2 = value; + args.arg2 = features; + args.arg3 = featureState; mHandler.obtainMessage(MSG_GET_FEATURE_COMPLETED, args).sendToTarget(); } @@ -1088,7 +1088,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * @hide */ public abstract static class GetFeatureCallback { - public abstract void onCompleted(boolean success, int feature, boolean value); + public abstract void onCompleted(boolean success, int[] features, boolean[] featureState); } /** @@ -1179,8 +1179,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan case MSG_GET_FEATURE_COMPLETED: SomeArgs args = (SomeArgs) msg.obj; sendGetFeatureCompleted((boolean) args.arg1 /* success */, - args.argi1 /* feature */, - (boolean) args.arg2 /* value */); + (int[]) args.arg2 /* features */, + (boolean[]) args.arg3 /* featureState */); args.recycle(); break; case MSG_CHALLENGE_GENERATED: @@ -1216,11 +1216,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mSetFeatureCallback.onCompleted(success, feature); } - private void sendGetFeatureCompleted(boolean success, int feature, boolean value) { + private void sendGetFeatureCompleted(boolean success, int[] features, boolean[] featureState) { if (mGetFeatureCallback == null) { return; } - mGetFeatureCallback.onCompleted(success, feature, value); + mGetFeatureCallback.onCompleted(success, features, featureState); } private void sendChallengeGenerated(int sensorId, long challenge) { diff --git a/core/java/android/hardware/face/FaceServiceReceiver.java b/core/java/android/hardware/face/FaceServiceReceiver.java index f0f975dcea57..9e62ca5e466b 100644 --- a/core/java/android/hardware/face/FaceServiceReceiver.java +++ b/core/java/android/hardware/face/FaceServiceReceiver.java @@ -66,7 +66,8 @@ public class FaceServiceReceiver extends IFaceServiceReceiver.Stub { } @Override - public void onFeatureGet(boolean success, int feature, boolean value) throws RemoteException { + public void onFeatureGet(boolean success, int[] features, boolean[] featureState) + throws RemoteException { } diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl index 2ef1430a2f99..0ccb39583554 100644 --- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl +++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl @@ -32,7 +32,7 @@ oneway interface IFaceServiceReceiver { void onError(int error, int vendorCode); void onRemoved(in Face face, int remaining); void onFeatureSet(boolean success, int feature); - void onFeatureGet(boolean success, int feature, boolean value); + void onFeatureGet(boolean success, in int[] features, in boolean[] featureState); void onChallengeGenerated(int sensorId, long challenge); void onChallengeInterrupted(int sensorId); void onChallengeInterruptFinished(int sensorId); diff --git a/core/java/android/hardware/face/OWNERS b/core/java/android/hardware/face/OWNERS index be10df1099ed..0b4d9d9d6f54 100644 --- a/core/java/android/hardware/face/OWNERS +++ b/core/java/android/hardware/face/OWNERS @@ -1,7 +1,3 @@ # Bug component: 879035 -curtislb@google.com -ilyamaty@google.com -jaggies@google.com -joshmccloskey@google.com -kchyn@google.com +include /services/core/java/com/android/server/biometrics/OWNERS diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 6a0772db34d2..88d5ba8be8ab 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -23,6 +23,7 @@ import static android.Manifest.permission.TEST_BIOMETRIC; 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.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON; import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_AUTHENTICATE; import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_HAS_ENROLLED_FINGERPRINTS; @@ -878,6 +879,19 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } /** + * Forwards FingerprintStateListener to FingerprintService + * @param listener new FingerprintStateListener being added + * @hide + */ + public void registerFingerprintStateListener(@NonNull FingerprintStateListener listener) { + try { + mService.registerFingerprintStateListener(listener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) @@ -989,6 +1003,16 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } /** + * Returns whether the device has a power button fingerprint sensor. + * @return boolean indicating whether power button is fingerprint sensor + * @hide + */ + public boolean isPowerbuttonFps() { + final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor(); + return sensorProps.sensorType == TYPE_POWER_BUTTON; + } + + /** * @hide */ public void addLockoutResetCallback(final LockoutResetCallback callback) { diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 4bb3ab6e1bc6..3bceacb5e479 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -156,6 +156,6 @@ interface IFingerprintService { // Sets the controller for managing the UDFPS overlay. void setUdfpsOverlayController(in IUdfpsOverlayController controller); - // Registers FingerprintStateListener in list stored by FingerprintService + // Registers FingerprintStateListener in list stored by FingerprintService. void registerFingerprintStateListener(IFingerprintStateListener listener); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl b/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl index a4d34afcfcf4..56dba7ea1d9a 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 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. @@ -19,7 +19,7 @@ import android.hardware.fingerprint.Fingerprint; /** * Communication channel for FingerprintManager to register the FingerprintStateListener - * in FingerprintService + * in FingerprintService. * @hide */ oneway interface IFingerprintStateListener { diff --git a/core/java/android/hardware/fingerprint/OWNERS b/core/java/android/hardware/fingerprint/OWNERS index e55b8c564ddb..5c9367240b8d 100644 --- a/core/java/android/hardware/fingerprint/OWNERS +++ b/core/java/android/hardware/fingerprint/OWNERS @@ -1,8 +1,3 @@ # Bug component: 114777 -curtislb@google.com -ilyamaty@google.com -jaggies@google.com -joshmccloskey@google.com -kchyn@google.com - +include /services/core/java/com/android/server/biometrics/OWNERS diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 336fbf2954a3..19fb845d9384 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -117,6 +117,11 @@ interface IInputManager { // static association for the cleared input port will be restored. void removePortAssociation(in String inputPort); + // Add a runtime association between the input device and display. + void addUniqueIdAssociation(in String inputDeviceName, in String displayUniqueId); + // Remove the runtime association between the input device and display. + void removeUniqueIdAssociation(in String inputDeviceName); + InputSensorInfo[] getSensorList(int deviceId); boolean registerSensorListener(IInputSensorEventListener listener); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index b6d2eaf11be6..648fda7c22bb 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -1293,7 +1293,7 @@ public final class InputManager { * @param inputPort The port of the input device. * @param displayPort The physical port of the associated display. * <p> - * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT}. + * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. * </p> * @hide */ @@ -1310,7 +1310,7 @@ public final class InputManager { * static association for the cleared input port will be restored. * @param inputPort The port of the input device to be cleared. * <p> - * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT}. + * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. * </p> * @hide */ @@ -1322,6 +1322,41 @@ public final class InputManager { } } + /** + * Add a runtime association between the input device name and display, by unique id. Input + * device names are expected to be unique. + * @param inputDeviceName The name of the input device. + * @param displayUniqueId The unique id of the associated display. + * <p> + * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. + * </p> + * @hide + */ + public void addUniqueIdAssociation(@NonNull String inputDeviceName, + @NonNull String displayUniqueId) { + try { + mIm.addUniqueIdAssociation(inputDeviceName, displayUniqueId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Removes a runtime association between the input device and display. + * @param inputDeviceName The name of the input device. + * <p> + * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. + * </p> + * @hide + */ + public void removeUniqueIdAssociation(@NonNull String inputDeviceName) { + try { + mIm.removeUniqueIdAssociation(inputDeviceName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private void populateInputDevicesLocked() { if (mInputDevicesChangedListener == null) { final InputDevicesChangedListener listener = new InputDevicesChangedListener(); diff --git a/core/java/android/hardware/iris/OWNERS b/core/java/android/hardware/iris/OWNERS index 33527f824827..0b4d9d9d6f54 100644 --- a/core/java/android/hardware/iris/OWNERS +++ b/core/java/android/hardware/iris/OWNERS @@ -1,3 +1,3 @@ # Bug component: 879035 -jaggies@google.com +include /services/core/java/com/android/server/biometrics/OWNERS diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index ba63ba4521df..32ca92cc97b0 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -85,54 +85,6 @@ public abstract class BatteryConsumer { public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999; /** - * Time usage component, describing the particular part of the system - * that was used for the corresponding amount of time. - * - * @hide - */ - @IntDef(prefix = {"TIME_COMPONENT_"}, value = { - TIME_COMPONENT_SCREEN, - TIME_COMPONENT_CPU, - TIME_COMPONENT_CPU_FOREGROUND, - TIME_COMPONENT_BLUETOOTH, - TIME_COMPONENT_CAMERA, - TIME_COMPONENT_FLASHLIGHT, - TIME_COMPONENT_MOBILE_RADIO, - TIME_COMPONENT_SENSORS, - TIME_COMPONENT_GNSS, - TIME_COMPONENT_WIFI, - TIME_COMPONENT_WAKELOCK, - TIME_COMPONENT_MEMORY, - TIME_COMPONENT_PHONE, - TIME_COMPONENT_IDLE, - }) - @Retention(RetentionPolicy.SOURCE) - public static @interface TimeComponent { - } - - public static final int TIME_COMPONENT_SCREEN = 0; - public static final int TIME_COMPONENT_CPU = 1; - public static final int TIME_COMPONENT_CPU_FOREGROUND = 2; - public static final int TIME_COMPONENT_BLUETOOTH = 3; - public static final int TIME_COMPONENT_CAMERA = 4; - public static final int TIME_COMPONENT_AUDIO = 5; - public static final int TIME_COMPONENT_VIDEO = 6; - public static final int TIME_COMPONENT_FLASHLIGHT = 7; - public static final int TIME_COMPONENT_MOBILE_RADIO = 8; - public static final int TIME_COMPONENT_SENSORS = 9; - public static final int TIME_COMPONENT_GNSS = 10; - public static final int TIME_COMPONENT_WIFI = 11; - public static final int TIME_COMPONENT_WAKELOCK = 12; - public static final int TIME_COMPONENT_MEMORY = 13; - public static final int TIME_COMPONENT_PHONE = 14; - public static final int TIME_COMPONENT_IDLE = 15; - - public static final int TIME_COMPONENT_COUNT = 16; - - public static final int FIRST_CUSTOM_TIME_COMPONENT_ID = 1000; - public static final int LAST_CUSTOM_TIME_COMPONENT_ID = 9999; - - /** * Identifiers of models used for power estimation. * * @hide @@ -166,7 +118,7 @@ public abstract class BatteryConsumer { * Total power consumed by this consumer, in mAh. */ public double getConsumedPower() { - return mPowerComponents.getTotalConsumedPower(); + return mPowerComponents.getConsumedPower(); } /** @@ -221,11 +173,11 @@ public abstract class BatteryConsumer { * Returns the amount of time since BatteryStats reset used by the specified component, e.g. * CPU, WiFi etc. * - * @param componentId The ID of the time component, e.g. - * {@link UidBatteryConsumer#TIME_COMPONENT_CPU}. + * @param componentId The ID of the power component, e.g. + * {@link UidBatteryConsumer#POWER_COMPONENT_CPU}. * @return Amount of time in milliseconds. */ - public long getUsageDurationMillis(@TimeComponent int componentId) { + public long getUsageDurationMillis(@PowerComponent int componentId) { return mPowerComponents.getUsageDurationMillis(componentId); } @@ -248,9 +200,9 @@ public abstract class BatteryConsumer { final PowerComponents.Builder mPowerComponentsBuilder; public BaseBuilder(@NonNull String[] customPowerComponentNames, - int customTimeComponentCount, boolean includePowerModels) { + boolean includePowerModels) { mPowerComponentsBuilder = new PowerComponents.Builder(customPowerComponentNames, - customTimeComponentCount, includePowerModels); + includePowerModels); } /** @@ -296,13 +248,13 @@ public abstract class BatteryConsumer { /** * Sets the amount of time used by the specified component, e.g. CPU, WiFi etc. * - * @param componentId The ID of the time component, e.g. - * {@link UidBatteryConsumer#TIME_COMPONENT_CPU}. + * @param componentId The ID of the power component, e.g. + * {@link UidBatteryConsumer#POWER_COMPONENT_CPU}. * @param componentUsageTimeMillis Amount of time in microseconds. */ @SuppressWarnings("unchecked") @NonNull - public T setUsageDurationMillis(@UidBatteryConsumer.TimeComponent int componentId, + public T setUsageDurationMillis(@UidBatteryConsumer.PowerComponent int componentId, long componentUsageTimeMillis) { mPowerComponentsBuilder.setUsageDurationMillis(componentId, componentUsageTimeMillis); return (T) this; diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 043a22b6e311..9ec6938de271 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -6076,7 +6076,7 @@ public abstract class BatteryStats implements Parcelable { pw.print(":"); for (int it=0; it<types.size(); it++) { pw.print(" "); - pw.print(JobParameters.getLegacyReasonCodeDescription(types.keyAt(it))); + pw.print(JobParameters.getInternalReasonCodeDescription(types.keyAt(it))); pw.print("("); pw.print(types.valueAt(it)); pw.print("x)"); diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 8ea59ce37018..efdef62d98ff 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -292,7 +292,6 @@ public final class BatteryUsageStats implements Parcelable { public static final class Builder { @NonNull private final String[] mCustomPowerComponentNames; - private final int mCustomTimeComponentCount; private final boolean mIncludePowerModels; private long mStatsStartTimestampMs; private int mDischargePercentage; @@ -309,14 +308,12 @@ public final class BatteryUsageStats implements Parcelable { private Parcel mHistoryBuffer; private List<BatteryStats.HistoryTag> mHistoryTagPool; - public Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount) { - this(customPowerComponentNames, customTimeComponentCount, false); + public Builder(@NonNull String[] customPowerComponentNames) { + this(customPowerComponentNames, false); } - public Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount, - boolean includePowerModels) { + public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels) { mCustomPowerComponentNames = customPowerComponentNames; - mCustomTimeComponentCount = customTimeComponentCount; mIncludePowerModels = includePowerModels; } @@ -399,7 +396,7 @@ public final class BatteryUsageStats implements Parcelable { UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid); if (builder == null) { builder = new UidBatteryConsumer.Builder(mCustomPowerComponentNames, - mCustomTimeComponentCount, mIncludePowerModels, batteryStatsUid); + mIncludePowerModels, batteryStatsUid); mUidBatteryConsumerBuilders.put(uid, builder); } return builder; @@ -415,7 +412,7 @@ public final class BatteryUsageStats implements Parcelable { SystemBatteryConsumer.Builder builder = mSystemBatteryConsumerBuilders.get(drainType); if (builder == null) { builder = new SystemBatteryConsumer.Builder(mCustomPowerComponentNames, - mCustomTimeComponentCount, mIncludePowerModels, drainType); + mIncludePowerModels, drainType); mSystemBatteryConsumerBuilders.put(drainType, builder); } return builder; @@ -430,7 +427,7 @@ public final class BatteryUsageStats implements Parcelable { UserBatteryConsumer.Builder builder = mUserBatteryConsumerBuilders.get(userId); if (builder == null) { builder = new UserBatteryConsumer.Builder(mCustomPowerComponentNames, - mCustomTimeComponentCount, mIncludePowerModels, userId); + mIncludePowerModels, userId); mUserBatteryConsumerBuilders.put(userId, builder); } return builder; diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index a0a41f4d6630..47a2edccc640 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -26,12 +26,10 @@ import android.annotation.NonNull; class PowerComponents { private static final int CUSTOM_POWER_COMPONENT_OFFSET = BatteryConsumer.POWER_COMPONENT_COUNT - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID; - private static final int CUSTOM_TIME_COMPONENT_OFFSET = BatteryConsumer.TIME_COMPONENT_COUNT - - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID; - private final double mTotalConsumedPowerMah; + private final double mConsumedPowerMah; private final double[] mPowerComponentsMah; - private final long[] mTimeComponentsMs; + private final long[] mUsageDurationsMs; private final int mCustomPowerComponentCount; private final byte[] mPowerModels; // Not written to Parcel and must be explicitly restored during the parent object's unparceling @@ -41,16 +39,16 @@ class PowerComponents { mCustomPowerComponentNames = builder.mCustomPowerComponentNames; mCustomPowerComponentCount = mCustomPowerComponentNames.length; mPowerComponentsMah = builder.mPowerComponentsMah; - mTimeComponentsMs = builder.mTimeComponentsMs; - mTotalConsumedPowerMah = builder.getTotalPower(); + mUsageDurationsMs = builder.mUsageDurationsMs; + mConsumedPowerMah = builder.getTotalPower(); mPowerModels = builder.mPowerModels; } PowerComponents(@NonNull Parcel source) { - mTotalConsumedPowerMah = source.readDouble(); + mConsumedPowerMah = source.readDouble(); mCustomPowerComponentCount = source.readInt(); mPowerComponentsMah = source.createDoubleArray(); - mTimeComponentsMs = source.createLongArray(); + mUsageDurationsMs = source.createLongArray(); if (source.readBoolean()) { mPowerModels = new byte[BatteryConsumer.POWER_COMPONENT_COUNT]; source.readByteArray(mPowerModels); @@ -61,10 +59,10 @@ class PowerComponents { /** Writes contents to Parcel */ void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeDouble(mTotalConsumedPowerMah); + dest.writeDouble(mConsumedPowerMah); dest.writeInt(mCustomPowerComponentCount); dest.writeDoubleArray(mPowerComponentsMah); - dest.writeLongArray(mTimeComponentsMs); + dest.writeLongArray(mUsageDurationsMs); if (mPowerModels != null) { dest.writeBoolean(true); dest.writeByteArray(mPowerModels); @@ -76,8 +74,8 @@ class PowerComponents { /** * Total power consumed by this consumer, in mAh. */ - public double getTotalConsumedPower() { - return mTotalConsumedPowerMah; + public double getConsumedPower() { + return mConsumedPowerMah; } /** @@ -152,17 +150,17 @@ class PowerComponents { /** * Returns the amount of time used by the specified component, e.g. CPU, WiFi etc. * - * @param componentId The ID of the time component, e.g. - * {@link BatteryConsumer#TIME_COMPONENT_CPU}. + * @param componentId The ID of the power component, e.g. + * {@link BatteryConsumer#POWER_COMPONENT_CPU}. * @return Amount of time in milliseconds. */ - public long getUsageDurationMillis(@BatteryConsumer.TimeComponent int componentId) { - if (componentId >= BatteryConsumer.TIME_COMPONENT_COUNT) { + public long getUsageDurationMillis(@BatteryConsumer.PowerComponent int componentId) { + if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) { throw new IllegalArgumentException( - "Unsupported time component ID: " + componentId); + "Unsupported power component ID: " + componentId); } try { - return mTimeComponentsMs[componentId]; + return mUsageDurationsMs[componentId]; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException("Unsupported power component ID: " + componentId); } @@ -175,15 +173,15 @@ class PowerComponents { * @return Amount of time in milliseconds. */ public long getUsageDurationForCustomComponentMillis(int componentId) { - if (componentId < BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID) { + if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) { throw new IllegalArgumentException( - "Unsupported custom time component ID: " + componentId); + "Unsupported custom power component ID: " + componentId); } try { - return mTimeComponentsMs[CUSTOM_TIME_COMPONENT_OFFSET + componentId]; + return mUsageDurationsMs[CUSTOM_POWER_COMPONENT_OFFSET + componentId]; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException( - "Unsupported custom time component ID: " + componentId); + "Unsupported custom power component ID: " + componentId); } } @@ -192,13 +190,13 @@ class PowerComponents { } /** - * Returns the largest usage duration among all time components. + * Returns the largest usage duration among all power components. */ public long getMaxComponentUsageDurationMillis() { long max = 0; - for (int i = mTimeComponentsMs.length - 1; i >= 0; i--) { - if (mTimeComponentsMs[i] > max) { - max = mTimeComponentsMs[i]; + for (int i = mUsageDurationsMs.length - 1; i >= 0; i--) { + if (mUsageDurationsMs[i] > max) { + max = mUsageDurationsMs[i]; } } return max; @@ -210,17 +208,15 @@ class PowerComponents { static final class Builder { private final double[] mPowerComponentsMah; private final String[] mCustomPowerComponentNames; - private final long[] mTimeComponentsMs; + private final long[] mUsageDurationsMs; private final byte[] mPowerModels; - Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount, - boolean includePowerModels) { + Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels) { mCustomPowerComponentNames = customPowerComponentNames; int powerComponentCount = BatteryConsumer.POWER_COMPONENT_COUNT + mCustomPowerComponentNames.length; mPowerComponentsMah = new double[powerComponentCount]; - mTimeComponentsMs = - new long[BatteryConsumer.TIME_COMPONENT_COUNT + customTimeComponentCount]; + mUsageDurationsMs = new long[powerComponentCount]; if (includePowerModels) { mPowerModels = new byte[BatteryConsumer.POWER_COMPONENT_COUNT]; } else { @@ -281,22 +277,22 @@ class PowerComponents { /** * Sets the amount of time used by the specified component, e.g. CPU, WiFi etc. * - * @param componentId The ID of the time component, e.g. - * {@link BatteryConsumer#TIME_COMPONENT_CPU}. + * @param componentId The ID of the power component, e.g. + * {@link BatteryConsumer#POWER_COMPONENT_CPU}. * @param componentUsageDurationMillis Amount of time in milliseconds. */ @NonNull - public Builder setUsageDurationMillis(@BatteryConsumer.TimeComponent int componentId, + public Builder setUsageDurationMillis(@BatteryConsumer.PowerComponent int componentId, long componentUsageDurationMillis) { - if (componentId >= BatteryConsumer.TIME_COMPONENT_COUNT) { + if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) { throw new IllegalArgumentException( - "Unsupported time component ID: " + componentId); + "Unsupported power component ID: " + componentId); } try { - mTimeComponentsMs[componentId] = componentUsageDurationMillis; + mUsageDurationsMs[componentId] = componentUsageDurationMillis; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException( - "Unsupported time component ID: " + componentId); + "Unsupported power component ID: " + componentId); } return this; } @@ -310,16 +306,16 @@ class PowerComponents { @NonNull public Builder setUsageDurationForCustomComponentMillis(int componentId, long componentUsageDurationMillis) { - if (componentId < BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID) { + if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) { throw new IllegalArgumentException( - "Unsupported custom time component ID: " + componentId); + "Unsupported custom power component ID: " + componentId); } try { - mTimeComponentsMs[CUSTOM_TIME_COMPONENT_OFFSET + componentId] = + mUsageDurationsMs[CUSTOM_POWER_COMPONENT_OFFSET + componentId] = componentUsageDurationMillis; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException( - "Unsupported custom time component ID: " + componentId); + "Unsupported custom power component ID: " + componentId); } return this; } @@ -328,8 +324,8 @@ class PowerComponents { for (int i = mPowerComponentsMah.length - 1; i >= 0; i--) { mPowerComponentsMah[i] += other.mPowerComponentsMah[i]; } - for (int i = mTimeComponentsMs.length - 1; i >= 0; i--) { - mTimeComponentsMs[i] += other.mTimeComponentsMs[i]; + for (int i = mUsageDurationsMs.length - 1; i >= 0; i--) { + mUsageDurationsMs[i] += other.mUsageDurationsMs[i]; } } diff --git a/core/java/android/os/SystemBatteryConsumer.java b/core/java/android/os/SystemBatteryConsumer.java index 1327978ad0ba..7618339260bd 100644 --- a/core/java/android/os/SystemBatteryConsumer.java +++ b/core/java/android/os/SystemBatteryConsumer.java @@ -147,9 +147,9 @@ public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable private double mPowerConsumedByAppsMah; private List<UidBatteryConsumer.Builder> mUidBatteryConsumers; - Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount, + Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels, @DrainType int drainType) { - super(customPowerComponentNames, customTimeComponentCount, includePowerModels); + super(customPowerComponentNames, includePowerModels); mDrainType = drainType; } diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index 92e960344f3e..b1fb570b4395 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -139,9 +139,9 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela public long mTimeInBackgroundMs; private boolean mExcludeFromBatteryUsageStats; - public Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount, + public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels, @NonNull BatteryStats.Uid batteryStatsUid) { - super(customPowerComponentNames, customTimeComponentCount, includePowerModels); + super(customPowerComponentNames, includePowerModels); mBatteryStatsUid = batteryStatsUid; mUid = batteryStatsUid.getUid(); } diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java index de0a707fb656..d0d0d38a75d2 100644 --- a/core/java/android/os/UserBatteryConsumer.java +++ b/core/java/android/os/UserBatteryConsumer.java @@ -77,9 +77,9 @@ public class UserBatteryConsumer extends BatteryConsumer implements Parcelable { private final int mUserId; private List<UidBatteryConsumer.Builder> mUidBatteryConsumers; - Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount, - boolean includePowerModels, int userId) { - super(customPowerComponentNames, customTimeComponentCount, includePowerModels); + Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels, + int userId) { + super(customPowerComponentNames, includePowerModels); mUserId = userId; } diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index b3502f38dd53..95962c82a611 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -554,9 +554,12 @@ public abstract class Vibrator { /** * Query the estimated durations of the given primitives. * - * The returned array will be the same length as the query array and the value at a given index - * will contain the duration in milliseconds of the effect at the same index in the querying - * array. + * <p>The returned array will be the same length as the query array and the value at a given + * index will contain the duration in milliseconds of the effect at the same index in the + * querying array. + * + * <p>The duration will be positive for primitives that are supported and zero for the + * unsupported ones, in correspondence with {@link #arePrimitivesSupported(int...)}. * * @param primitiveIds Which primitives to query for. * @return The duration of each primitive, with zeroes for primitives that are not supported. diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 17c90d64ce6a..64d5d9c9d858 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -20,6 +20,7 @@ import static android.os.Build.VERSION_CODES.S; import android.Manifest; import android.annotation.CheckResult; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -31,6 +32,7 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.AppGlobals; +import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.PropertyInvalidatedCache; import android.compat.annotation.ChangeId; @@ -63,6 +65,8 @@ import com.android.internal.R; import com.android.internal.annotations.Immutable; import com.android.internal.util.CollectionUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f8991ce1e97b..55cdb26214b1 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -83,6 +83,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.MemoryIntArray; import android.view.Display; +import android.view.Window; import android.view.WindowManager.LayoutParams; import com.android.internal.annotations.GuardedBy; @@ -13372,6 +13373,19 @@ public final class Settings { public static final String WINDOW_ANIMATION_SCALE = "window_animation_scale"; /** + * Setting to disable cross-window blurs. This includes window blur behind, (see + * {@link LayoutParams#setBlurBehindRadius}) and window background blur (see + * {@link Window#setBackgroundBlurRadius}). + * + * The value is a boolean (1 or 0). + * @hide + */ + @TestApi + @Readable + @SuppressLint("NoSettingsProvider") + public static final String DISABLE_WINDOW_BLURS = "disable_window_blurs"; + + /** * Scaling factor for activity transition animations. * * The value is a float. Setting to 0.0f will disable window animations. diff --git a/core/java/android/service/translation/ITranslationService.aidl b/core/java/android/service/translation/ITranslationService.aidl index 297f00a4d753..e9dd2c3bd70b 100644 --- a/core/java/android/service/translation/ITranslationService.aidl +++ b/core/java/android/service/translation/ITranslationService.aidl @@ -16,6 +16,7 @@ package android.service.translation; +import android.os.IBinder; import android.os.ResultReceiver; import android.view.translation.TranslationContext; import com.android.internal.os.IResultReceiver; @@ -30,7 +31,7 @@ import com.android.internal.os.IResultReceiver; * @hide */ oneway interface ITranslationService { - void onConnected(); + void onConnected(in IBinder callback); void onDisconnected(); void onCreateTranslationSession(in TranslationContext translationContext, int sessionId, in IResultReceiver receiver); diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java index 17cb3dae6cb9..e7234ccc5dd2 100644 --- a/core/java/android/service/translation/TranslationService.java +++ b/core/java/android/service/translation/TranslationService.java @@ -41,6 +41,7 @@ import android.os.ResultReceiver; import android.util.ArraySet; import android.util.Log; import android.view.translation.ITranslationDirectManager; +import android.view.translation.ITranslationServiceCallback; import android.view.translation.TranslationCapability; import android.view.translation.TranslationContext; import android.view.translation.TranslationManager; @@ -50,6 +51,7 @@ import android.view.translation.TranslationSpec; import com.android.internal.os.IResultReceiver; +import java.util.Objects; import java.util.Set; import java.util.function.Consumer; @@ -84,15 +86,17 @@ public abstract class TranslationService extends Service { public static final String SERVICE_META_DATA = "android.translation_service"; private Handler mHandler; + private ITranslationServiceCallback mCallback; + /** * Binder to receive calls from system server. */ private final ITranslationService mInterface = new ITranslationService.Stub() { @Override - public void onConnected() { - mHandler.sendMessage(obtainMessage(TranslationService::onConnected, - TranslationService.this)); + public void onConnected(IBinder callback) { + mHandler.sendMessage(obtainMessage(TranslationService::handleOnConnected, + TranslationService.this, callback)); } @Override @@ -275,6 +279,32 @@ public abstract class TranslationService extends Service { @TranslationSpec.DataFormat int targetFormat, @NonNull Consumer<Set<TranslationCapability>> callback); + /** + * Called by the service to notify an update in existing {@link TranslationCapability}s. + * + * @param capability the updated {@link TranslationCapability} with its new states and flags. + */ + public final void updateTranslationCapability(@NonNull TranslationCapability capability) { + Objects.requireNonNull(capability, "translation capability should not be null"); + + final ITranslationServiceCallback callback = mCallback; + if (callback == null) { + Log.w(TAG, "updateTranslationCapability(): no server callback"); + return; + } + + try { + callback.updateTranslationCapability(capability); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + private void handleOnConnected(@NonNull IBinder callback) { + mCallback = ITranslationServiceCallback.Stub.asInterface(callback); + onConnected(); + } + // TODO(b/176464808): Need to handle client dying case private void handleOnCreateTranslationSession(@NonNull TranslationContext translationContext, diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 3c53e8f305ae..bacc6ec2227b 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -349,7 +349,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { private final HotwordDetectedResult mHotwordDetectedResult; private final ParcelFileDescriptor mAudioStream; - private EventPayload(boolean triggerAvailable, boolean captureAvailable, + EventPayload(boolean triggerAvailable, boolean captureAvailable, AudioFormat audioFormat, int captureSession, byte[] data) { this(triggerAvailable, captureAvailable, audioFormat, captureSession, data, null, null); diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java index f5d796f92a4d..e50de1c30ba0 100644 --- a/core/java/android/service/voice/HotwordDetectedResult.java +++ b/core/java/android/service/voice/HotwordDetectedResult.java @@ -21,6 +21,7 @@ import static android.service.voice.HotwordDetector.CONFIDENCE_LEVEL_NONE; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.media.MediaSyncEvent; import android.os.Parcelable; import android.os.PersistableBundle; @@ -53,9 +54,17 @@ public final class HotwordDetectedResult implements Parcelable { } /** + * A {@code MediaSyncEvent} that allows the {@link HotwordDetector} to recapture the audio + * that contains the hotword trigger. This must be obtained using + * {@link android.media.AudioRecord#shareAudioHistory(String, long)}. + * <p> + * This can be {@code null} if reprocessing the hotword trigger isn't required. + */ + @Nullable + private MediaSyncEvent mMediaSyncEvent = null; + + /** * Byte offset in the audio stream when the trigger event happened. - * - * <p>If unset, the most recent bytes in the audio stream will be used. */ private final int mByteOffset; private static int defaultByteOffset() { @@ -84,7 +93,7 @@ public final class HotwordDetectedResult implements Parcelable { /** * Returns the maximum values of {@link #getScore} and {@link #getPersonalizedScore}. - * + * <p> * The float value should be calculated as {@code getScore() / getMaxScore()}. */ public static int getMaxScore() { @@ -159,6 +168,7 @@ public final class HotwordDetectedResult implements Parcelable { @DataClass.Generated.Member /* package-private */ HotwordDetectedResult( @HotwordDetector.HotwordConfidenceLevelValue int confidenceLevel, + @Nullable MediaSyncEvent mediaSyncEvent, int byteOffset, int score, int personalizedScore, @@ -167,6 +177,7 @@ public final class HotwordDetectedResult implements Parcelable { this.mConfidenceLevel = confidenceLevel; com.android.internal.util.AnnotationValidations.validate( HotwordDetector.HotwordConfidenceLevelValue.class, null, mConfidenceLevel); + this.mMediaSyncEvent = mediaSyncEvent; this.mByteOffset = byteOffset; this.mScore = score; this.mPersonalizedScore = personalizedScore; @@ -187,9 +198,19 @@ public final class HotwordDetectedResult implements Parcelable { } /** + * A {@code MediaSyncEvent} that allows the {@link HotwordDetector} to recapture the audio + * that contains the hotword trigger. This must be obtained using + * {@link android.media.AudioRecord#shareAudioHistory(String, long)}. + * <p> + * This can be {@code null} if reprocessing the hotword trigger isn't required. + */ + @DataClass.Generated.Member + public @Nullable MediaSyncEvent getMediaSyncEvent() { + return mMediaSyncEvent; + } + + /** * Byte offset in the audio stream when the trigger event happened. - * - * <p>If unset, the most recent bytes in the audio stream will be used. */ @DataClass.Generated.Member public int getByteOffset() { @@ -237,6 +258,9 @@ public final class HotwordDetectedResult implements Parcelable { * * <p>The use of this method is discouraged, and support for it will be removed in future * versions of Android. + * + * <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents + * that can be used to communicate with other processes. */ @DataClass.Generated.Member public @NonNull PersistableBundle getExtras() { @@ -251,6 +275,7 @@ public final class HotwordDetectedResult implements Parcelable { return "HotwordDetectedResult { " + "confidenceLevel = " + mConfidenceLevel + ", " + + "mediaSyncEvent = " + mMediaSyncEvent + ", " + "byteOffset = " + mByteOffset + ", " + "score = " + mScore + ", " + "personalizedScore = " + mPersonalizedScore + ", " + @@ -273,6 +298,7 @@ public final class HotwordDetectedResult implements Parcelable { //noinspection PointlessBooleanExpression return true && mConfidenceLevel == that.mConfidenceLevel + && java.util.Objects.equals(mMediaSyncEvent, that.mMediaSyncEvent) && mByteOffset == that.mByteOffset && mScore == that.mScore && mPersonalizedScore == that.mPersonalizedScore @@ -288,6 +314,7 @@ public final class HotwordDetectedResult implements Parcelable { int _hash = 1; _hash = 31 * _hash + mConfidenceLevel; + _hash = 31 * _hash + java.util.Objects.hashCode(mMediaSyncEvent); _hash = 31 * _hash + mByteOffset; _hash = 31 * _hash + mScore; _hash = 31 * _hash + mPersonalizedScore; @@ -302,7 +329,11 @@ public final class HotwordDetectedResult implements Parcelable { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } + byte flg = 0; + if (mMediaSyncEvent != null) flg |= 0x2; + dest.writeByte(flg); dest.writeInt(mConfidenceLevel); + if (mMediaSyncEvent != null) dest.writeTypedObject(mMediaSyncEvent, flags); dest.writeInt(mByteOffset); dest.writeInt(mScore); dest.writeInt(mPersonalizedScore); @@ -321,7 +352,9 @@ public final class HotwordDetectedResult implements Parcelable { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } + byte flg = in.readByte(); int confidenceLevel = in.readInt(); + MediaSyncEvent mediaSyncEvent = (flg & 0x2) == 0 ? null : (MediaSyncEvent) in.readTypedObject(MediaSyncEvent.CREATOR); int byteOffset = in.readInt(); int score = in.readInt(); int personalizedScore = in.readInt(); @@ -331,6 +364,7 @@ public final class HotwordDetectedResult implements Parcelable { this.mConfidenceLevel = confidenceLevel; com.android.internal.util.AnnotationValidations.validate( HotwordDetector.HotwordConfidenceLevelValue.class, null, mConfidenceLevel); + this.mMediaSyncEvent = mediaSyncEvent; this.mByteOffset = byteOffset; this.mScore = score; this.mPersonalizedScore = personalizedScore; @@ -364,6 +398,7 @@ public final class HotwordDetectedResult implements Parcelable { public static final class Builder { private @HotwordDetector.HotwordConfidenceLevelValue int mConfidenceLevel; + private @Nullable MediaSyncEvent mMediaSyncEvent; private int mByteOffset; private int mScore; private int mPersonalizedScore; @@ -387,14 +422,27 @@ public final class HotwordDetectedResult implements Parcelable { } /** + * A {@code MediaSyncEvent} that allows the {@link HotwordDetector} to recapture the audio + * that contains the hotword trigger. This must be obtained using + * {@link android.media.AudioRecord#shareAudioHistory(String, long)}. + * <p> + * This can be {@code null} if reprocessing the hotword trigger isn't required. + */ + @DataClass.Generated.Member + public @NonNull Builder setMediaSyncEvent(@NonNull MediaSyncEvent value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mMediaSyncEvent = value; + return this; + } + + /** * Byte offset in the audio stream when the trigger event happened. - * - * <p>If unset, the most recent bytes in the audio stream will be used. */ @DataClass.Generated.Member public @NonNull Builder setByteOffset(int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x2; + mBuilderFieldsSet |= 0x4; mByteOffset = value; return this; } @@ -407,7 +455,7 @@ public final class HotwordDetectedResult implements Parcelable { @DataClass.Generated.Member public @NonNull Builder setScore(int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x4; + mBuilderFieldsSet |= 0x8; mScore = value; return this; } @@ -420,7 +468,7 @@ public final class HotwordDetectedResult implements Parcelable { @DataClass.Generated.Member public @NonNull Builder setPersonalizedScore(int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x8; + mBuilderFieldsSet |= 0x10; mPersonalizedScore = value; return this; } @@ -433,7 +481,7 @@ public final class HotwordDetectedResult implements Parcelable { @DataClass.Generated.Member public @NonNull Builder setHotwordPhraseId(int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x10; + mBuilderFieldsSet |= 0x20; mHotwordPhraseId = value; return this; } @@ -449,11 +497,14 @@ public final class HotwordDetectedResult implements Parcelable { * * <p>The use of this method is discouraged, and support for it will be removed in future * versions of Android. + * + * <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents + * that can be used to communicate with other processes. */ @DataClass.Generated.Member public @NonNull Builder setExtras(@NonNull PersistableBundle value) { checkNotUsed(); - mBuilderFieldsSet |= 0x20; + mBuilderFieldsSet |= 0x40; mExtras = value; return this; } @@ -461,28 +512,32 @@ public final class HotwordDetectedResult implements Parcelable { /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull HotwordDetectedResult build() { checkNotUsed(); - mBuilderFieldsSet |= 0x40; // Mark builder used + mBuilderFieldsSet |= 0x80; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mConfidenceLevel = defaultConfidenceLevel(); } if ((mBuilderFieldsSet & 0x2) == 0) { - mByteOffset = defaultByteOffset(); + mMediaSyncEvent = null; } if ((mBuilderFieldsSet & 0x4) == 0) { - mScore = defaultScore(); + mByteOffset = defaultByteOffset(); } if ((mBuilderFieldsSet & 0x8) == 0) { - mPersonalizedScore = defaultPersonalizedScore(); + mScore = defaultScore(); } if ((mBuilderFieldsSet & 0x10) == 0) { - mHotwordPhraseId = defaultHotwordPhraseId(); + mPersonalizedScore = defaultPersonalizedScore(); } if ((mBuilderFieldsSet & 0x20) == 0) { + mHotwordPhraseId = defaultHotwordPhraseId(); + } + if ((mBuilderFieldsSet & 0x40) == 0) { mExtras = defaultExtras(); } HotwordDetectedResult o = new HotwordDetectedResult( mConfidenceLevel, + mMediaSyncEvent, mByteOffset, mScore, mPersonalizedScore, @@ -492,7 +547,7 @@ public final class HotwordDetectedResult implements Parcelable { } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x40) != 0) { + if ((mBuilderFieldsSet & 0x80) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -500,10 +555,10 @@ public final class HotwordDetectedResult implements Parcelable { } @DataClass.Generated( - time = 1616965644404L, + time = 1619059352684L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java", - inputSignatures = "public static final int BYTE_OFFSET_UNSET\nprivate final @android.service.voice.HotwordDetector.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate final int mByteOffset\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int defaultConfidenceLevel()\nprivate static int defaultByteOffset()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)") + inputSignatures = "public static final int BYTE_OFFSET_UNSET\nprivate final @android.service.voice.HotwordDetector.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate final int mByteOffset\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int defaultConfidenceLevel()\nprivate static int defaultByteOffset()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java index ea854e8c9283..7e0117df288e 100644 --- a/core/java/android/service/voice/HotwordDetectionService.java +++ b/core/java/android/service/voice/HotwordDetectionService.java @@ -30,6 +30,7 @@ import android.app.Service; import android.content.ContentCaptureOptions; import android.content.Context; import android.content.Intent; +import android.hardware.soundtrigger.SoundTrigger; import android.media.AudioFormat; import android.os.Bundle; import android.os.Handler; @@ -133,7 +134,7 @@ public abstract class HotwordDetectionService extends Service { private final IHotwordDetectionService mInterface = new IHotwordDetectionService.Stub() { @Override public void detectFromDspSource( - ParcelFileDescriptor audioStream, + SoundTrigger.KeyphraseRecognitionEvent event, AudioFormat audioFormat, long timeoutMillis, IDspHotwordDetectionCallback callback) @@ -143,8 +144,9 @@ public abstract class HotwordDetectionService extends Service { } mHandler.sendMessage(obtainMessage(HotwordDetectionService::onDetect, HotwordDetectionService.this, - audioStream, - audioFormat, + new AlwaysOnHotwordDetector.EventPayload( + event.triggerInData, event.captureAvailable, + event.captureFormat, event.captureSession, event.data), timeoutMillis, new Callback(callback))); } @@ -178,8 +180,6 @@ public abstract class HotwordDetectionService extends Service { mHandler.sendMessage(obtainMessage( HotwordDetectionService::onDetect, HotwordDetectionService.this, - audioStream, - audioFormat, new Callback(callback))); break; case AUDIO_SOURCE_EXTERNAL: @@ -246,9 +246,12 @@ public abstract class HotwordDetectionService extends Service { * the application fails to abide by the timeout, system will close the * microphone and cancel the operation. * @param callback The callback to use for responding to the detection request. + * @deprecated Implement + * {@link #onDetect(AlwaysOnHotwordDetector.EventPayload, long, Callback)} instead. * * @hide */ + @Deprecated @SystemApi public void onDetect( @NonNull ParcelFileDescriptor audioStream, @@ -260,6 +263,32 @@ public abstract class HotwordDetectionService extends Service { } /** + * Called when the device hardware (such as a DSP) detected the hotword, to request second stage + * validation before handing over the audio to the {@link AlwaysOnHotwordDetector}. + * <p> + * After {@code callback} is invoked or {@code timeoutMillis} has passed, and invokes the + * appropriate {@link AlwaysOnHotwordDetector.Callback callback}. + * + * @param eventPayload Payload data for the hardware detection event. This may contain the + * trigger audio, if requested when calling + * {@link AlwaysOnHotwordDetector#startRecognition(int)}. + * @param timeoutMillis Timeout in milliseconds for the operation to invoke the callback. If + * the application fails to abide by the timeout, system will close the + * microphone and cancel the operation. + * @param callback The callback to use for responding to the detection request. + * + * @hide + */ + @SystemApi + public void onDetect( + @NonNull AlwaysOnHotwordDetector.EventPayload eventPayload, + @DurationMillisLong long timeoutMillis, + @NonNull Callback callback) { + // TODO: Add a helpful error message. + throw new UnsupportedOperationException(); + } + + /** * Called when the {@link VoiceInteractionService#createAlwaysOnHotwordDetector(String, Locale, * PersistableBundle, SharedMemory, AlwaysOnHotwordDetector.Callback)} or * {@link AlwaysOnHotwordDetector#updateState(PersistableBundle, SharedMemory)} requests an @@ -305,7 +334,9 @@ public abstract class HotwordDetectionService extends Service { * @param audioFormat Format of the supplied audio * @param callback The callback to use for responding to the detection request. * {@link Callback#onRejected(HotwordRejectedResult) callback.onRejected} cannot be used here. + * @deprecated Implement {@link #onDetect(Callback)} instead. */ + @Deprecated public void onDetect( @NonNull ParcelFileDescriptor audioStream, @NonNull AudioFormat audioFormat, @@ -316,6 +347,22 @@ public abstract class HotwordDetectionService extends Service { /** * Called when the {@link VoiceInteractionService} requests that this service + * {@link HotwordDetector#startRecognition() start} hotword recognition on audio coming directly + * from the device microphone. + * <p> + * On successful detection of a hotword, call + * {@link Callback#onDetected(HotwordDetectedResult)}. + * + * @param callback The callback to use for responding to the detection request. + * {@link Callback#onRejected(HotwordRejectedResult) callback.onRejected} cannot be used here. + */ + public void onDetect(@NonNull Callback callback) { + // TODO: Add a helpful error message. + throw new UnsupportedOperationException(); + } + + /** + * Called when the {@link VoiceInteractionService} requests that this service * {@link HotwordDetector#startRecognition(ParcelFileDescriptor, AudioFormat, * PersistableBundle)} run} hotword recognition on audio coming from an external connected * microphone. diff --git a/core/java/android/service/voice/IHotwordDetectionService.aidl b/core/java/android/service/voice/IHotwordDetectionService.aidl index 2ffe787e895a..7ba00982e6a2 100644 --- a/core/java/android/service/voice/IHotwordDetectionService.aidl +++ b/core/java/android/service/voice/IHotwordDetectionService.aidl @@ -16,6 +16,8 @@ package android.service.voice; +import android.content.ContentCaptureOptions; +import android.hardware.soundtrigger.SoundTrigger; import android.media.AudioFormat; import android.os.IRemoteCallback; import android.os.ParcelFileDescriptor; @@ -23,7 +25,6 @@ import android.os.PersistableBundle; import android.os.SharedMemory; import android.service.voice.IDspHotwordDetectionCallback; import android.view.contentcapture.IContentCaptureManager; -import android.content.ContentCaptureOptions; /** * Provide the interface to communicate with hotword detection service. @@ -32,7 +33,7 @@ import android.content.ContentCaptureOptions; */ oneway interface IHotwordDetectionService { void detectFromDspSource( - in ParcelFileDescriptor audioStream, + in SoundTrigger.KeyphraseRecognitionEvent event, in AudioFormat audioFormat, long timeoutMillis, in IDspHotwordDetectionCallback callback); diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index 2a2522741955..b5c838b1871b 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -420,9 +420,13 @@ public class VoiceInteractionService extends Service { * * @see #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory, * AlwaysOnHotwordDetector.Callback) + * @deprecated Use + * {@link #createHotwordDetector(PersistableBundle, SharedMemory, HotwordDetector.Callback)} + * instead. * * @hide */ + @Deprecated @SystemApi @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION) @NonNull @@ -445,6 +449,58 @@ public class VoiceInteractionService extends Service { } /** + * Creates a {@link HotwordDetector} and initializes the application's + * {@link HotwordDetectionService} using {@code options} and {code sharedMemory}. + * + * <p>To be able to call this, you need to set android:hotwordDetectionService in the + * android.voice_interaction metadata file to a valid hotword detection service, and set + * android:isolatedProcess="true" in the hotword detection service's declaration. Otherwise, + * this throws an {@link IllegalStateException}. + * + * <p>This instance must be retained and used by the client. + * Calling this a second time invalidates the previously created hotword detector + * which can no longer be used to manage recognition. + * + * <p>Using this has a noticeable impact on battery, since the microphone is kept open + * for the lifetime of the recognition {@link HotwordDetector#startRecognition() session}. On + * devices where hardware filtering is available (such as through a DSP), it's highly + * recommended to use {@link #createAlwaysOnHotwordDetector} instead. + * + * @param options Application configuration data to be provided to the + * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or + * other contents that can be used to communicate with other processes. + * @param sharedMemory The unrestricted data blob to be provided to the + * {@link HotwordDetectionService}. Use this to provide hotword models or other such data to the + * sandboxed process. + * @param callback The callback to notify of detection events. + * @return A hotword detector for the given audio format. + * + * @see #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory, + * AlwaysOnHotwordDetector.Callback) + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION) + @NonNull + public final HotwordDetector createHotwordDetector( + @Nullable PersistableBundle options, + @Nullable SharedMemory sharedMemory, + @NonNull HotwordDetector.Callback callback) { + if (mSystemService == null) { + throw new IllegalStateException("Not available until onReady() is called"); + } + synchronized (mLock) { + // Allow only one concurrent recognition via the APIs. + safelyShutdownHotwordDetector(); + mSoftwareHotwordDetector = + new SoftwareHotwordDetector( + mSystemService, null, options, sharedMemory, callback); + } + return mSoftwareHotwordDetector; + } + + /** * Creates an {@link KeyphraseModelManager} to use for enrolling voice models outside of the * pre-bundled system voice models. * @hide diff --git a/core/java/android/speech/IRecognitionService.aidl b/core/java/android/speech/IRecognitionService.aidl index 9a5e534a68cf..cc349c8d030d 100644 --- a/core/java/android/speech/IRecognitionService.aidl +++ b/core/java/android/speech/IRecognitionService.aidl @@ -58,7 +58,6 @@ oneway interface IRecognitionService { * Cancels the speech recognition. * * @param listener to receive callbacks, note that this must be non-null - * @param packageName the package name calling this API */ void cancel(in IRecognitionListener listener, boolean isShutdown); } diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java index ad670c8182b9..bb48757988e9 100644 --- a/core/java/android/speech/RecognitionService.java +++ b/core/java/android/speech/RecognitionService.java @@ -34,7 +34,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; -import android.os.Process; import android.os.RemoteException; import android.util.Log; @@ -66,7 +65,12 @@ public abstract class RecognitionService extends Service { private static final String TAG = "RecognitionService"; /** Debugging flag */ - private static final boolean DBG = false; + private static final boolean DBG = true; + + private static final String RECORD_AUDIO_APP_OP = + AppOpsManager.permissionToOp(Manifest.permission.RECORD_AUDIO); + private static final int RECORD_AUDIO_APP_OP_CODE = + AppOpsManager.permissionToOpCode(Manifest.permission.RECORD_AUDIO); /** Binder of the recognition service */ private RecognitionServiceBinder mBinder = new RecognitionServiceBinder(this); @@ -97,7 +101,7 @@ public abstract class RecognitionService extends Service { dispatchStopListening((IRecognitionListener) msg.obj); break; case MSG_CANCEL: - dispatchCancel((IRecognitionListener) msg.obj, msg.arg1 == 1); + dispatchCancel((IRecognitionListener) msg.obj); break; case MSG_RESET: dispatchClearCallback(); @@ -108,18 +112,20 @@ public abstract class RecognitionService extends Service { private void dispatchStartListening(Intent intent, final IRecognitionListener listener, @NonNull AttributionSource attributionSource) { - if (mCurrentCallback == null) { - if (DBG) Log.d(TAG, "created new mCurrentCallback, listener = " + listener.asBinder()); - mCurrentCallback = new Callback(listener, attributionSource); + try { + if (mCurrentCallback == null) { + if (DBG) { + Log.d(TAG, "created new mCurrentCallback, listener = " + listener.asBinder()); + } + mCurrentCallback = new Callback(listener, attributionSource); - RecognitionService.this.onStartListening(intent, mCurrentCallback); - } else { - try { + RecognitionService.this.onStartListening(intent, mCurrentCallback); + } else { listener.onError(SpeechRecognizer.ERROR_RECOGNIZER_BUSY); - } catch (RemoteException e) { - Log.d(TAG, "onError call from startListening failed"); + Log.i(TAG, "concurrent startListening received - ignoring this call"); } - Log.i(TAG, "concurrent startListening received - ignoring this call"); + } catch (RemoteException e) { + Log.d(TAG, "onError call from startListening failed"); } } @@ -139,16 +145,13 @@ public abstract class RecognitionService extends Service { } } - private void dispatchCancel(IRecognitionListener listener, boolean shutDown) { + private void dispatchCancel(IRecognitionListener listener) { if (mCurrentCallback == null) { if (DBG) Log.d(TAG, "cancel called with no preceding startListening - ignoring"); } else if (mCurrentCallback.mListener.asBinder() != listener.asBinder()) { Log.w(TAG, "cancel called by client who did not call startListening - ignoring"); } else { // the correct state RecognitionService.this.onCancel(mCurrentCallback); - if (shutDown) { - mCurrentCallback.finishRecordAudioOpAttributionToCallerIfNeeded(); - } mCurrentCallback = null; if (DBG) Log.d(TAG, "canceling - setting mCurrentCallback to null"); } @@ -173,47 +176,6 @@ public abstract class RecognitionService extends Service { } /** - * Checks whether the caller has sufficient permissions - * - * @param listener to send the error message to in case of error - * @param forDataDelivery If the permission check is for delivering the sensitive data. - * @param packageName the package name of the caller - * @param featureId The feature in the package - * @return {@code true} if the caller has enough permissions, {@code false} otherwise - */ - private boolean checkPermissions(IRecognitionListener listener, boolean forDataDelivery, - @NonNull String packageName, @Nullable String featureId) { - if (DBG) Log.d(TAG, "checkPermissions"); - - final int callingUid = Binder.getCallingUid(); - if (callingUid == Process.SYSTEM_UID) { - // Assuming system has verified permissions of the caller. - return true; - } - - if (forDataDelivery) { - if (PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(this, - android.Manifest.permission.RECORD_AUDIO, packageName, featureId, - null /*message*/) == PermissionChecker.PERMISSION_GRANTED) { - return true; - } - } else { - if (PermissionChecker.checkCallingOrSelfPermissionForPreflight(this, - android.Manifest.permission.RECORD_AUDIO) - == PermissionChecker.PERMISSION_GRANTED) { - return true; - } - } - try { - Log.e(TAG, "call for recognition service without RECORD_AUDIO permissions"); - listener.onError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS); - } catch (RemoteException re) { - Log.e(TAG, "sending ERROR_INSUFFICIENT_PERMISSIONS message failed", re); - } - return false; - } - - /** * Notifies the service that it should start listening for speech. * * @param recognizerIntent contains parameters for the recognition to be performed. The intent @@ -281,7 +243,6 @@ public abstract class RecognitionService extends Service { * single channel audio stream. The sample rate is implementation dependent. */ public void bufferReceived(byte[] buffer) throws RemoteException { - startRecordAudioOpAttributionToCallerIfNeeded(); mListener.onBufferReceived(buffer); } @@ -314,7 +275,6 @@ public abstract class RecognitionService extends Service { * {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter */ public void partialResults(Bundle partialResults) throws RemoteException { - startRecordAudioOpAttributionToCallerIfNeeded(); mListener.onPartialResults(partialResults); } @@ -336,7 +296,6 @@ public abstract class RecognitionService extends Service { * {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter */ public void results(Bundle results) throws RemoteException { - startRecordAudioOpAttributionToCallerIfNeeded(); Message.obtain(mHandler, MSG_RESET).sendToTarget(); mListener.onResults(results); } @@ -366,9 +325,6 @@ public abstract class RecognitionService extends Service { * and passing this identity to {@link * android.content.ContextParams.Builder#setNextAttributionSource(AttributionSource)}. * - * - * - * * @return The permission identity of the calling app. * * @see android.content.ContextParams.Builder#setNextAttributionSource( @@ -379,40 +335,55 @@ public abstract class RecognitionService extends Service { return mCallingAttributionSource; } - private void startRecordAudioOpAttributionToCallerIfNeeded() throws RemoteException { - if (!isProxyingRecordAudioToCaller()) { - final int result = PermissionChecker.checkPermissionAndStartDataDelivery( - RecognitionService.this, Manifest.permission.RECORD_AUDIO, - getAttributionContextForCaller().getAttributionSource(), - /*message*/ null); - if (result == PermissionChecker.PERMISSION_GRANTED) { - return; - } - error(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS); + boolean maybeStartAttribution() { + if (DBG) { + Log.i(TAG, "Starting attribution"); + } + + if (DBG && isProxyingRecordAudioToCaller()) { + Log.i(TAG, "Proxying already in progress, not starting the attribution"); } - } - private @NonNull Context getAttributionContextForCaller() { - if (mAttributionContext == null) { + if (!isProxyingRecordAudioToCaller()) { mAttributionContext = createContext(new ContextParams.Builder() .setNextAttributionSource(mCallingAttributionSource) .build()); + + final int result = PermissionChecker.checkPermissionAndStartDataDelivery( + RecognitionService.this, + Manifest.permission.RECORD_AUDIO, + mAttributionContext.getAttributionSource(), + /*message*/ null); + + return result == PermissionChecker.PERMISSION_GRANTED; } - return mAttributionContext; + return false; } - void finishRecordAudioOpAttributionToCallerIfNeeded() { + void maybeFinishAttribution() { + if (DBG) { + Log.i(TAG, "Finishing attribution"); + } + + if (DBG && !isProxyingRecordAudioToCaller()) { + Log.i(TAG, "Not proxying currently, not finishing the attribution"); + } + if (isProxyingRecordAudioToCaller()) { - final String op = AppOpsManager.permissionToOp(Manifest.permission.RECORD_AUDIO); - PermissionChecker.finishDataDelivery(RecognitionService.this, - op, getAttributionContextForCaller().getAttributionSource()); + PermissionChecker.finishDataDelivery( + RecognitionService.this, + RECORD_AUDIO_APP_OP, + mAttributionContext.getAttributionSource()); + + mAttributionContext = null; } } private boolean isProxyingRecordAudioToCaller() { - final int op = AppOpsManager.permissionToOpCode(Manifest.permission.RECORD_AUDIO); final AppOpsManager appOpsManager = getSystemService(AppOpsManager.class); - return appOpsManager.isProxying(op, getAttributionTag(), + return appOpsManager.isProxying( + RECORD_AUDIO_APP_OP_CODE, + getAttributionTag(), mCallingAttributionSource.getUid(), mCallingAttributionSource.getPackageName()); } @@ -423,7 +394,7 @@ public abstract class RecognitionService extends Service { private final WeakReference<RecognitionService> mServiceRef; public RecognitionServiceBinder(RecognitionService service) { - mServiceRef = new WeakReference<RecognitionService>(service); + mServiceRef = new WeakReference<>(service); } @Override @@ -445,8 +416,8 @@ public abstract class RecognitionService extends Service { if (DBG) Log.d(TAG, "stopListening called by:" + listener.asBinder()); final RecognitionService service = mServiceRef.get(); if (service != null) { - service.mHandler.sendMessage(Message.obtain(service.mHandler, - MSG_STOP_LISTENING, listener)); + service.mHandler.sendMessage( + Message.obtain(service.mHandler, MSG_STOP_LISTENING, listener)); } } @@ -455,8 +426,8 @@ public abstract class RecognitionService extends Service { if (DBG) Log.d(TAG, "cancel called by:" + listener.asBinder()); final RecognitionService service = mServiceRef.get(); if (service != null) { - service.mHandler.sendMessage(Message.obtain(service.mHandler, - MSG_CANCEL, isShutdown ? 1 : 0, 0, listener)); + service.mHandler.sendMessage( + Message.obtain(service.mHandler, MSG_CANCEL, listener)); } } diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java index 22ccd23a22bc..3183f15fe16c 100644 --- a/core/java/android/speech/RecognizerIntent.java +++ b/core/java/android/speech/RecognizerIntent.java @@ -43,7 +43,7 @@ public class RecognizerIntent { /** * The extra key used in an intent which is providing an already opened audio source for the - * RecognitionService to use. + * RecognitionService to use. Data should be a URI to an audio resource. */ public static final String EXTRA_AUDIO_INJECT_SOURCE = "android.speech.extra.AUDIO_INJECT_SOURCE"; diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java index 8061bf36fa6a..ff04825f788f 100644 --- a/core/java/android/util/PackageUtils.java +++ b/core/java/android/util/PackageUtils.java @@ -19,6 +19,7 @@ package android.util; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.Signature; +import android.text.TextUtils; import libcore.util.HexEncoding; @@ -39,18 +40,27 @@ public final class PackageUtils { } /** + * @see #computeSignaturesSha256Digests(Signature[], String) + */ + public static @NonNull String[] computeSignaturesSha256Digests( + @NonNull Signature[] signatures) { + return computeSignaturesSha256Digests(signatures, null); + } + + /** * Computes the SHA256 digests of a list of signatures. Items in the * resulting array of hashes correspond to the signatures in the * input array. * @param signatures The signatures. + * @param separator Separator between each pair of characters, such as a colon, or null to omit. * @return The digest array. */ public static @NonNull String[] computeSignaturesSha256Digests( - @NonNull Signature[] signatures) { + @NonNull Signature[] signatures, @Nullable String separator) { final int signatureCount = signatures.length; final String[] digests = new String[signatureCount]; for (int i = 0; i < signatureCount; i++) { - digests[i] = computeSha256Digest(signatures[i].toByteArray()); + digests[i] = computeSha256Digest(signatures[i].toByteArray(), separator); } return digests; } @@ -66,11 +76,11 @@ public final class PackageUtils { @NonNull Signature[] signatures) { // Shortcut for optimization - most apps singed by a single cert if (signatures.length == 1) { - return computeSha256Digest(signatures[0].toByteArray()); + return computeSha256Digest(signatures[0].toByteArray(), null); } // Make sure these are sorted to handle reversed certificates - final String[] sha256Digests = computeSignaturesSha256Digests(signatures); + final String[] sha256Digests = computeSignaturesSha256Digests(signatures, null); return computeSignaturesSha256Digest(sha256Digests); } @@ -99,7 +109,7 @@ public final class PackageUtils { /* ignore - can't happen */ } } - return computeSha256Digest(bytes.toByteArray()); + return computeSha256Digest(bytes.toByteArray(), null); } /** @@ -122,15 +132,34 @@ public final class PackageUtils { } /** + * @see #computeSha256Digest(byte[], String) + */ + public static @Nullable String computeSha256Digest(@NonNull byte[] data) { + return computeSha256Digest(data, null); + } + /** * Computes the SHA256 digest of some data. * @param data The data. + * @param separator Separator between each pair of characters, such as a colon, or null to omit. * @return The digest or null if an error occurs. */ - public static @Nullable String computeSha256Digest(@NonNull byte[] data) { + public static @Nullable String computeSha256Digest(@NonNull byte[] data, + @Nullable String separator) { byte[] sha256DigestBytes = computeSha256DigestBytes(data); if (sha256DigestBytes == null) { return null; } - return HexEncoding.encodeToString(sha256DigestBytes, true /* uppercase */); + + if (separator == null) { + return HexEncoding.encodeToString(sha256DigestBytes, true /* uppercase */); + } + + int length = sha256DigestBytes.length; + String[] pieces = new String[length]; + for (int index = 0; index < length; index++) { + pieces[index] = HexEncoding.encodeToString(sha256DigestBytes[index], true); + } + + return TextUtils.join(separator, pieces); } } diff --git a/core/java/android/uwb/AdapterStateListener.java b/core/java/android/uwb/AdapterStateListener.java index b9900951591f..91847f740953 100644 --- a/core/java/android/uwb/AdapterStateListener.java +++ b/core/java/android/uwb/AdapterStateListener.java @@ -68,8 +68,7 @@ public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub { mIsRegistered = true; } catch (RemoteException e) { Log.w(TAG, "Failed to register adapter state callback"); - executor.execute(() -> callback.onStateChanged(mAdapterState, - AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN)); + throw e.rethrowFromSystemServer(); } } else { sendCurrentState(callback); @@ -95,6 +94,7 @@ public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub { mAdapter.unregisterAdapterStateCallbacks(this); } catch (RemoteException e) { Log.w(TAG, "Failed to unregister AdapterStateCallback with service"); + throw e.rethrowFromSystemServer(); } mIsRegistered = false; } @@ -115,24 +115,24 @@ public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub { mAdapter.setEnabled(isEnabled); } catch (RemoteException e) { Log.w(TAG, "Failed to set adapter state"); - sendErrorState(); + throw e.rethrowFromSystemServer(); } } } } - private void sendErrorState() { + /** + * Gets the adapter enabled state + * + * @return integer representing adapter enabled state + */ + public int getAdapterState() { synchronized (this) { - for (AdapterStateCallback callback: mCallbackMap.keySet()) { - Executor executor = mCallbackMap.get(callback); - - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callback.onStateChanged( - mAdapterState, mAdapterStateChangeReason)); - } finally { - Binder.restoreCallingIdentity(identity); - } + try { + return mAdapter.getAdapterState(); + } catch (RemoteException e) { + Log.w(TAG, "Failed to get adapter state"); + throw e.rethrowFromSystemServer(); } } } diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl index 29021c111dd1..d87935003e03 100644 --- a/core/java/android/uwb/IUwbAdapter.aidl +++ b/core/java/android/uwb/IUwbAdapter.aidl @@ -161,6 +161,18 @@ interface IUwbAdapter { */ void setEnabled(boolean enabled); + /** + * Returns the current enabled/disabled UWB state. + * + * Possible values are: + * IUwbAdapterState#STATE_DISABLED + * IUwbAdapterState#STATE_ENABLED_ACTIVE + * IUwbAdapterState#STATE_ENABLED_INACTIVE + * + * @return value representing enabled/disabled UWB state. + */ + int getAdapterState(); + /** * The maximum allowed time to open a ranging session. */ diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java index 3c99f89c68b4..f7406ae277de 100644 --- a/core/java/android/uwb/UwbManager.java +++ b/core/java/android/uwb/UwbManager.java @@ -178,7 +178,7 @@ public final class UwbManager { * <p>The provided callback will be invoked by the given {@link Executor}. * * <p>When first registering a callback, the callbacks's - * {@link AdapterStateCallback#onStateChanged(boolean, int)} is immediately invoked to indicate + * {@link AdapterStateCallback#onStateChanged(int, int)} is immediately invoked to indicate * the current state of the underlying UWB adapter with the most recent * {@link AdapterStateCallback.StateChangedReason} that caused the change. * @@ -279,6 +279,21 @@ public final class UwbManager { } /** + * Returns the current enabled/disabled state for UWB. + * + * Possible values are: + * AdapterStateCallback#STATE_DISABLED + * AdapterStateCallback#STATE_ENABLED_INACTIVE + * AdapterStateCallback#STATE_ENABLED_ACTIVE + * + * @return value representing current enabled/disabled state for UWB. + * @hide + */ + public @AdapterStateCallback.State int getAdapterState() { + return mAdapterStateListener.getAdapterState(); + } + + /** * Disables or enables UWB for a user * * @param enabled value representing intent to disable or enable UWB. If true any subsequent diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index c87db65c9de2..9cb0d1ff2c3f 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -17,6 +17,7 @@ package android.view; import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE; +import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS; import android.annotation.IntDef; import android.annotation.NonNull; @@ -36,6 +37,7 @@ import android.graphics.ColorSpace; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; +import android.hardware.display.BrightnessInfo; import android.hardware.display.DeviceProductInfo; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; @@ -730,6 +732,15 @@ public final class Display { } /** + * @return Brightness information about the display. + * @hide + */ + @RequiresPermission(CONTROL_DISPLAY_BRIGHTNESS) + public @Nullable BrightnessInfo getBrightnessInfo() { + return mGlobal.getBrightnessInfo(mDisplayId); + } + + /** * Gets the size of the display, in pixels. * Value returned by this method does not necessarily represent the actual raw size * (native resolution) of the display. diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 1706799758e1..7125232005c4 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -19,7 +19,6 @@ package android.view; import android.app.ActivityManager; import android.view.IRemoteAnimationFinishedCallback; import android.graphics.GraphicBuffer; -import android.graphics.Rect; import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; @@ -39,14 +38,13 @@ interface IRecentsAnimationController { TaskSnapshot screenshotTask(int taskId); /** - * Sets the final bounds on a Task. This is used by Launcher to notify the system that - * animating Activity to PiP has completed and the associated task surface should be updated - * accordingly. This should be called before `finish` + * Sets the final surface transaction on a Task. This is used by Launcher to notify the system + * that animating Activity to PiP has completed and the associated task surface should be + * updated accordingly. This should be called before `finish` * @param taskId for which the leash should be updated - * @param destinationBounds bounds of the final PiP window * @param finishTransaction leash operations for the final transform. */ - void setFinishTaskBounds(int taskId, in Rect destinationBounds, + void setFinishTaskTransaction(int taskId, in PictureInPictureSurfaceTransaction finishTransaction); /** diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 88406ffcdf14..cd8248934be7 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -857,7 +857,5 @@ interface IWindowManager */ void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener); - void setForceCrossWindowBlurDisabled(boolean disable); - boolean isTaskSnapshotSupported(); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b2df3a942469..11fac0561907 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4003,6 +4003,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @hide + * + * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked + * out of the public fields to keep the undefined bits out of the developer's way. + * + * Flag to disable the ongoing call chip. + */ + public static final int STATUS_BAR_DISABLE_ONGOING_CALL_CHIP = 0x04000000; + + /** + * @hide */ public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00003FF7; @@ -4227,7 +4237,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, name = "STATUS_BAR_DISABLE_RECENT"), @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_SEARCH, equals = STATUS_BAR_DISABLE_SEARCH, - name = "STATUS_BAR_DISABLE_SEARCH") + name = "STATUS_BAR_DISABLE_SEARCH"), + @ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_ONGOING_CALL_CHIP, + equals = STATUS_BAR_DISABLE_ONGOING_CALL_CHIP, + name = "STATUS_BAR_DISABLE_ONGOING_CALL_CHIP") }, formatToHexString = true) @SystemUiVisibility int mSystemUiVisibility; @@ -4256,6 +4269,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, STATUS_BAR_DISABLE_CLOCK, STATUS_BAR_DISABLE_RECENT, STATUS_BAR_DISABLE_SEARCH, + STATUS_BAR_DISABLE_ONGOING_CALL_CHIP, }) @Retention(RetentionPolicy.SOURCE) public @interface SystemUiVisibility {} diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3cfda571ae6a..5a738eb2878a 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2787,8 +2787,8 @@ public final class ViewRootImpl implements ViewParent, if (!mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) { if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: " + mPendingMergedConfiguration.getMergedConfiguration()); - performConfigurationChange(mPendingMergedConfiguration, !mFirst, - INVALID_DISPLAY /* same display */); + performConfigurationChange(new MergedConfiguration(mPendingMergedConfiguration), + !mFirst, INVALID_DISPLAY /* same display */); updatedConfiguration = true; } @@ -2854,7 +2854,6 @@ public final class ViewRootImpl implements ViewParent, return; } } - notifySurfaceCreated(); } else if (surfaceDestroyed) { // If the surface has been removed, then reset the scroll // positions. @@ -2894,10 +2893,6 @@ public final class ViewRootImpl implements ViewParent, } } - if (!surfaceCreated && surfaceReplaced) { - notifySurfaceReplaced(); - } - if (mDragResizing != dragResizing) { if (dragResizing) { mResizeMode = freeformResizing @@ -3110,7 +3105,14 @@ public final class ViewRootImpl implements ViewParent, } } - if (surfaceDestroyed) { + // These callbacks will trigger SurfaceView SurfaceHolder.Callbacks and must be invoked + // after the measure pass. If its invoked before the measure pass and the app modifies + // the view hierarchy in the callbacks, we could leave the views in a broken state. + if (surfaceCreated) { + notifySurfaceCreated(); + } else if (surfaceReplaced) { + notifySurfaceReplaced(); + } else if (surfaceDestroyed) { notifySurfaceDestroyed(); } @@ -5330,8 +5332,8 @@ public final class ViewRootImpl implements ViewParent, mPendingMergedConfiguration.setConfiguration(config, mLastReportedMergedConfiguration.getOverrideConfiguration()); - performConfigurationChange(mPendingMergedConfiguration, false /* force */, - INVALID_DISPLAY /* same display */); + performConfigurationChange(new MergedConfiguration(mPendingMergedConfiguration), + false /* force */, INVALID_DISPLAY /* same display */); } break; case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: { setAccessibilityFocus(null, null); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 60516eb9efd4..c32ab3a2d717 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -908,20 +908,6 @@ public interface WindowManager extends ViewManager { } /** - * Disables cross-window blurs device-wide. This includes window blur behind - * (see {@link LayoutParams#setBlurBehindRadius}) and window background blur - * (see {@link Window#setBackgroundBlurRadius}). - * - * @param disable specifies whether to disable the blur. Note that calling this - * with 'disable=false' will not enable blurs if there is something - * else disabling blurs. - * @hide - */ - @TestApi - default void setForceCrossWindowBlurDisabled(boolean disable) { - } - - /** * @hide */ static String transitTypeToString(@TransitionType int type) { diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 07eeb034b663..f800991944ac 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -328,15 +328,6 @@ public final class WindowManagerImpl implements WindowManager { } @Override - public void setForceCrossWindowBlurDisabled(boolean disable) { - try { - WindowManagerGlobal.getWindowManagerService() - .setForceCrossWindowBlurDisabled(disable); - } catch (RemoteException e) { - } - } - - @Override public boolean isTaskSnapshotSupported() { try { return WindowManagerGlobal.getWindowManagerService().isTaskSnapshotSupported(); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index d6292caba344..d15aee0941e8 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -266,6 +266,14 @@ public final class InputMethodManager { private static final int NOT_A_SUBTYPE_ID = -1; /** + * {@code true} to try to avoid blocking apps' UI thread by sending + * {@link StartInputReason#WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION} and + * {@link StartInputReason#WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION} in a truly asynchronous + * way. {@code false} to go back to the previous synchronous semantics. + */ + private static final boolean USE_REPORT_WINDOW_GAINED_FOCUS_ASYNC = false; + + /** * A constant that represents Voice IME. * * @see InputMethodSubtype#getMode() @@ -689,20 +697,29 @@ public final class InputMethodManager { Log.v(TAG, "Reporting focus gain, without startInput" + ", nextFocusIsServedView=" + nextFocusHasConnection); } - final int startInputReason = - nextFocusHasConnection ? WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION - : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION; - final Completable.InputBindResult value = Completable.createInputBindResult(); - mService.startInputOrWindowGainedFocus( - startInputReason, mClient, - focusedView.getWindowToken(), startInputFlags, softInputMode, - windowFlags, - null, - null, - 0 /* missingMethodFlags */, - mCurRootView.mContext.getApplicationInfo().targetSdkVersion, - ResultCallbacks.of(value)); - Completable.getResult(value); // ignore the result + + if (USE_REPORT_WINDOW_GAINED_FOCUS_ASYNC) { + mService.reportWindowGainedFocusAsync( + nextFocusHasConnection, mClient, focusedView.getWindowToken(), + startInputFlags, softInputMode, windowFlags, + mCurRootView.mContext.getApplicationInfo().targetSdkVersion); + } else { + final int startInputReason = nextFocusHasConnection + ? WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION + : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION; + final Completable.InputBindResult value = + Completable.createInputBindResult(); + mService.startInputOrWindowGainedFocus( + startInputReason, mClient, + focusedView.getWindowToken(), startInputFlags, softInputMode, + windowFlags, + null, + null, + 0 /* missingMethodFlags */, + mCurRootView.mContext.getApplicationInfo().targetSdkVersion, + ResultCallbacks.of(value)); + Completable.getResult(value); // ignore the result + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1087,6 +1104,11 @@ public final class InputMethodManager { public void setImeTraceEnabled(boolean enabled) { ImeTracing.getInstance().setEnabled(enabled); } + + @Override + public void throwExceptionFromSystem(String message) { + throw new RuntimeException(message); + } }; final InputConnection mDummyInputConnection = new BaseInputConnection(this, false); diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl index 560edecc9da6..5fbf228ec72f 100644 --- a/core/java/android/view/translation/ITranslationManager.aidl +++ b/core/java/android/view/translation/ITranslationManager.aidl @@ -22,6 +22,7 @@ import android.os.ResultReceiver; import android.view.autofill.AutofillId; import android.view.translation.TranslationContext; import android.view.translation.TranslationSpec; +import android.view.translation.UiTranslationSpec; import com.android.internal.os.IResultReceiver; import java.util.List; @@ -34,12 +35,14 @@ import java.util.List; oneway interface ITranslationManager { void onTranslationCapabilitiesRequest(int sourceFormat, int destFormat, in ResultReceiver receiver, int userId); + void registerTranslationCapabilityCallback(in IRemoteCallback callback, int userId); + void unregisterTranslationCapabilityCallback(in IRemoteCallback callback, int userId); void onSessionCreated(in TranslationContext translationContext, int sessionId, in IResultReceiver receiver, int userId); void updateUiTranslationState(int state, in TranslationSpec sourceSpec, in TranslationSpec targetSpec, in List<AutofillId> viewIds, IBinder token, int taskId, - int userId); + in UiTranslationSpec uiTranslationSpec, int userId); void registerUiTranslationStateCallback(in IRemoteCallback callback, int userId); void unregisterUiTranslationStateCallback(in IRemoteCallback callback, int userId); diff --git a/core/java/android/view/translation/ITranslationServiceCallback.aidl b/core/java/android/view/translation/ITranslationServiceCallback.aidl new file mode 100644 index 000000000000..eb89b49535de --- /dev/null +++ b/core/java/android/view/translation/ITranslationServiceCallback.aidl @@ -0,0 +1,28 @@ +/* + * 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.translation; + +import android.view.translation.TranslationCapability; + +/** + * Interface from the Translation service to the system. + * + * @hide + */ +oneway interface ITranslationServiceCallback { + void updateTranslationCapability(in TranslationCapability capability); +} diff --git a/core/java/android/view/translation/TranslationCapability.java b/core/java/android/view/translation/TranslationCapability.java index 28b21138eef1..0bea1212e3e2 100644 --- a/core/java/android/view/translation/TranslationCapability.java +++ b/core/java/android/view/translation/TranslationCapability.java @@ -48,6 +48,16 @@ public final class TranslationCapability implements Parcelable { * TODO: fill in javadoc */ public static final @ModelState int STATE_ON_DEVICE = 3; + /** + * The translation service does not support translation between the source and target specs. + * + * <p>Note: This state is not returned from calling + * {@link TranslationManager#getOnDeviceTranslationCapabilities}. This state will only appear as + * part of capability updates from + * {@link TranslationManager#addOnDeviceTranslationCapabilityUpdateListener} if existing support + * was dropped.</p> + */ + public static final @ModelState int STATE_NOT_AVAILABLE = 4; /** * The state of translation readiness between {@code mSourceSpec} and {@code mTargetSpec}. @@ -103,7 +113,7 @@ public final class TranslationCapability implements Parcelable { - // Code below generated by codegen v1.0.22. + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -120,7 +130,8 @@ public final class TranslationCapability implements Parcelable { @IntDef(prefix = "STATE_", value = { STATE_AVAILABLE_TO_DOWNLOAD, STATE_DOWNLOADING, - STATE_ON_DEVICE + STATE_ON_DEVICE, + STATE_NOT_AVAILABLE }) @Retention(RetentionPolicy.SOURCE) @DataClass.Generated.Member @@ -136,6 +147,8 @@ public final class TranslationCapability implements Parcelable { return "STATE_DOWNLOADING"; case STATE_ON_DEVICE: return "STATE_ON_DEVICE"; + case STATE_NOT_AVAILABLE: + return "STATE_NOT_AVAILABLE"; default: return Integer.toHexString(value); } } @@ -238,12 +251,14 @@ public final class TranslationCapability implements Parcelable { if (!(mState == STATE_AVAILABLE_TO_DOWNLOAD) && !(mState == STATE_DOWNLOADING) - && !(mState == STATE_ON_DEVICE)) { + && !(mState == STATE_ON_DEVICE) + && !(mState == STATE_NOT_AVAILABLE)) { throw new java.lang.IllegalArgumentException( "state was " + mState + " but must be one of: " + "STATE_AVAILABLE_TO_DOWNLOAD(" + STATE_AVAILABLE_TO_DOWNLOAD + "), " + "STATE_DOWNLOADING(" + STATE_DOWNLOADING + "), " - + "STATE_ON_DEVICE(" + STATE_ON_DEVICE + ")"); + + "STATE_ON_DEVICE(" + STATE_ON_DEVICE + "), " + + "STATE_NOT_AVAILABLE(" + STATE_NOT_AVAILABLE + ")"); } this.mSourceSpec = sourceSpec; @@ -275,10 +290,10 @@ public final class TranslationCapability implements Parcelable { }; @DataClass.Generated( - time = 1616438309593L, - codegenVersion = "1.0.22", + time = 1619119609557L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/view/translation/TranslationCapability.java", - inputSignatures = "public static final @android.view.translation.TranslationCapability.ModelState int STATE_AVAILABLE_TO_DOWNLOAD\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_DOWNLOADING\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_ON_DEVICE\nprivate final @android.view.translation.TranslationCapability.ModelState int mState\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final boolean mUiTranslationEnabled\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mSupportedTranslationFlags\nclass TranslationCapability extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genConstructor=false)") + inputSignatures = "public static final @android.view.translation.TranslationCapability.ModelState int STATE_AVAILABLE_TO_DOWNLOAD\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_DOWNLOADING\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_ON_DEVICE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_NOT_AVAILABLE\nprivate final @android.view.translation.TranslationCapability.ModelState int mState\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final boolean mUiTranslationEnabled\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mSupportedTranslationFlags\nclass TranslationCapability extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genConstructor=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/translation/TranslationManager.java b/core/java/android/view/translation/TranslationManager.java index 8785d9cec2c6..e75577487d1c 100644 --- a/core/java/android/view/translation/TranslationManager.java +++ b/core/java/android/view/translation/TranslationManager.java @@ -16,13 +16,17 @@ package android.view.translation; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; import android.annotation.WorkerThread; import android.app.PendingIntent; import android.content.Context; +import android.os.Binder; +import android.os.Bundle; import android.os.Handler; +import android.os.IRemoteCallback; import android.os.Looper; import android.os.Parcelable; import android.os.RemoteException; @@ -38,11 +42,14 @@ import com.android.internal.util.SyncResultReceiver; import java.util.ArrayList; import java.util.Collections; +import java.util.Map; import java.util.Objects; import java.util.Random; import java.util.Set; +import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; /** * The {@link TranslationManager} class provides ways for apps to integrate and use the @@ -77,11 +84,14 @@ public final class TranslationManager { */ public static final String EXTRA_CAPABILITIES = "translation_capabilities"; - // TODO: implement update listeners and propagate updates. @GuardedBy("mLock") private final ArrayMap<Pair<Integer, Integer>, ArrayList<PendingIntent>> mTranslationCapabilityUpdateListeners = new ArrayMap<>(); + @GuardedBy("mLock") + private final Map<Consumer<TranslationCapability>, IRemoteCallback> mCapabilityCallbacks = + new ArrayMap<>(); + private static final Random ID_GENERATOR = new Random(); private final Object mLock = new Object(); @@ -228,16 +238,42 @@ public final class TranslationManager { } /** - * Registers a {@link PendingIntent} to listen for updates on states of on-device + * Adds a {@link TranslationCapability} Consumer to listen for updates on states of on-device * {@link TranslationCapability}s. * - * <p>IMPORTANT: the pending intent must be called to start a service, or a broadcast if it is - * an explicit intent.</p> - * - * @param sourceFormat data format for the input data to be translated. - * @param targetFormat data format for the expected translated output data. - * @param pendingIntent the pending intent to invoke when updates are received. + * @param capabilityListener a {@link TranslationCapability} Consumer to receive the updated + * {@link TranslationCapability} from the on-device translation service. + */ + public void addOnDeviceTranslationCapabilityUpdateListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<TranslationCapability> capabilityListener) { + Objects.requireNonNull(executor, "executor should not be null"); + Objects.requireNonNull(capabilityListener, "capability listener should not be null"); + + synchronized (mLock) { + if (mCapabilityCallbacks.containsKey(capabilityListener)) { + Log.w(TAG, "addOnDeviceTranslationCapabilityUpdateListener: the listener for " + + capabilityListener + " already registered; ignoring."); + return; + } + final IRemoteCallback remoteCallback = new TranslationCapabilityRemoteCallback(executor, + capabilityListener); + try { + mService.registerTranslationCapabilityCallback(remoteCallback, + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mCapabilityCallbacks.put(capabilityListener, remoteCallback); + } + } + + + /** + * @deprecated Use {@link TranslationManager#addOnDeviceTranslationCapabilityUpdateListener( + * java.util.concurrent.Executor, java.util.function.Consumer)} */ + @Deprecated public void addOnDeviceTranslationCapabilityUpdateListener( @TranslationSpec.DataFormat int sourceFormat, @TranslationSpec.DataFormat int targetFormat, @@ -252,8 +288,8 @@ public final class TranslationManager { } /** - * @deprecated Use {@link #addOnDeviceTranslationCapabilityUpdateListener(int, int, - * PendingIntent)} + * @deprecated Use {@link TranslationManager#addOnDeviceTranslationCapabilityUpdateListener( + * java.util.concurrent.Executor, java.util.function.Consumer)} */ @Deprecated public void addTranslationCapabilityUpdateListener( @@ -264,14 +300,38 @@ public final class TranslationManager { } /** - * Unregisters a {@link PendingIntent} to listen for updates on states of on-device - * {@link TranslationCapability}s. + * Removes a {@link TranslationCapability} Consumer to listen for updates on states of + * on-device {@link TranslationCapability}s. * - * @param sourceFormat data format for the input data to be translated. - * @param targetFormat data format for the expected translated output data. - * @param pendingIntent the pending intent to unregister + * @param capabilityListener the {@link TranslationCapability} Consumer to unregister */ public void removeOnDeviceTranslationCapabilityUpdateListener( + @NonNull Consumer<TranslationCapability> capabilityListener) { + Objects.requireNonNull(capabilityListener, "capability callback should not be null"); + + synchronized (mLock) { + final IRemoteCallback remoteCallback = mCapabilityCallbacks.get(capabilityListener); + if (remoteCallback == null) { + Log.w(TAG, "removeOnDeviceTranslationCapabilityUpdateListener: the capability " + + "listener not found; ignoring."); + return; + } + try { + mService.unregisterTranslationCapabilityCallback(remoteCallback, + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mCapabilityCallbacks.remove(capabilityListener); + } + } + + /** + * @deprecated Use {@link #removeOnDeviceTranslationCapabilityUpdateListener( + * java.util.function.Consumer)}. + */ + @Deprecated + public void removeOnDeviceTranslationCapabilityUpdateListener( @TranslationSpec.DataFormat int sourceFormat, @TranslationSpec.DataFormat int targetFormat, @NonNull PendingIntent pendingIntent) { @@ -296,8 +356,8 @@ public final class TranslationManager { } /** - * @deprecated Use {@link #removeOnDeviceTranslationCapabilityUpdateListener(int, int, - * PendingIntent)} + * @deprecated Use {@link #removeOnDeviceTranslationCapabilityUpdateListener( + * java.util.function.Consumer)}. */ @Deprecated public void removeTranslationCapabilityUpdateListener( @@ -308,8 +368,6 @@ public final class TranslationManager { sourceFormat, targetFormat, pendingIntent); } - //TODO: Add method to propagate updates to mTCapabilityUpdateListeners - /** * Returns an immutable PendingIntent which can be used to launch an activity to view/edit * on-device translation settings. @@ -360,4 +418,28 @@ public final class TranslationManager { return sAvailableRequestId; } } + + private static class TranslationCapabilityRemoteCallback extends + IRemoteCallback.Stub { + private final Executor mExecutor; + private final Consumer<TranslationCapability> mListener; + + TranslationCapabilityRemoteCallback(Executor executor, + Consumer<TranslationCapability> listener) { + mExecutor = executor; + mListener = listener; + } + + @Override + public void sendResult(Bundle bundle) { + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> onTranslationCapabilityUpdate(bundle))); + } + + private void onTranslationCapabilityUpdate(Bundle bundle) { + TranslationCapability capability = + (TranslationCapability) bundle.getParcelable(EXTRA_CAPABILITIES); + mListener.accept(capability); + } + } } diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java index eefc7fdc7d77..541b4941c62e 100644 --- a/core/java/android/view/translation/UiTranslationManager.java +++ b/core/java/android/view/translation/UiTranslationManager.java @@ -119,15 +119,31 @@ public final class UiTranslationManager { } /** + * @deprecated Use {@link #startTranslation(TranslationSpec, TranslationSpec, List, ActivityId, + * UiTranslationSpec)} instead. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) + @Deprecated + @SystemApi + public void startTranslation(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec targetSpec, @NonNull List<AutofillId> viewIds, + @NonNull ActivityId activityId) { + startTranslation( + sourceSpec, targetSpec, viewIds, activityId, + new UiTranslationSpec.Builder().setShouldPadContentForCompat(true).build()); + } + + /** * Request ui translation for a given Views. * * @param sourceSpec {@link TranslationSpec} for the data to be translated. * @param targetSpec {@link TranslationSpec} for the translated data. * @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated * @param activityId the identifier for the Activity which needs ui translation + * @param uiTranslationSpec configuration for translation of the specified views * @throws IllegalArgumentException if the no {@link View}'s {@link AutofillId} in the list - * @throws NullPointerException the sourceSpec, targetSpec, viewIds, activityId or - * {@link android.app.assist.ActivityId#getToken()} is {@code null} * * @hide */ @@ -135,19 +151,21 @@ public final class UiTranslationManager { @SystemApi public void startTranslation(@NonNull TranslationSpec sourceSpec, @NonNull TranslationSpec targetSpec, @NonNull List<AutofillId> viewIds, - @NonNull ActivityId activityId) { + @NonNull ActivityId activityId, @NonNull UiTranslationSpec uiTranslationSpec) { // TODO(b/177789967): Return result code or find a way to notify the status. Objects.requireNonNull(sourceSpec); Objects.requireNonNull(targetSpec); Objects.requireNonNull(viewIds); Objects.requireNonNull(activityId); Objects.requireNonNull(activityId.getToken()); + Objects.requireNonNull(uiTranslationSpec); if (viewIds.size() == 0) { throw new IllegalArgumentException("Invalid empty views: " + viewIds); } try { mService.updateUiTranslationState(STATE_UI_TRANSLATION_STARTED, sourceSpec, targetSpec, viewIds, activityId.getToken(), activityId.getTaskId(), + uiTranslationSpec, mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -172,7 +190,8 @@ public final class UiTranslationManager { Objects.requireNonNull(activityId.getToken()); mService.updateUiTranslationState(STATE_UI_TRANSLATION_FINISHED, null /* sourceSpec */, null /* targetSpec */, null /* viewIds */, - activityId.getToken(), activityId.getTaskId(), mContext.getUserId()); + activityId.getToken(), activityId.getTaskId(), null /* uiTranslationSpec */, + mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -196,7 +215,8 @@ public final class UiTranslationManager { Objects.requireNonNull(activityId.getToken()); mService.updateUiTranslationState(STATE_UI_TRANSLATION_PAUSED, null /* sourceSpec */, null /* targetSpec */, null /* viewIds */, - activityId.getToken(), activityId.getTaskId(), mContext.getUserId()); + activityId.getToken(), activityId.getTaskId(), null /* uiTranslationSpec */, + mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -220,7 +240,8 @@ public final class UiTranslationManager { Objects.requireNonNull(activityId.getToken()); mService.updateUiTranslationState(STATE_UI_TRANSLATION_RESUMED, null /* sourceSpec */, null /* targetSpec */, null /* viewIds */, - activityId.getToken(), activityId.getTaskId(), mContext.getUserId()); + activityId.getToken(), activityId.getTaskId(), null /* uiTranslationSpec */, + mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/view/translation/UiTranslationSpec.aidl b/core/java/android/view/translation/UiTranslationSpec.aidl new file mode 100644 index 000000000000..7fbeb66389a5 --- /dev/null +++ b/core/java/android/view/translation/UiTranslationSpec.aidl @@ -0,0 +1,19 @@ +/* + * 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.translation; + +parcelable UiTranslationSpec; diff --git a/core/java/android/view/translation/UiTranslationSpec.java b/core/java/android/view/translation/UiTranslationSpec.java new file mode 100644 index 000000000000..b43dbce312c3 --- /dev/null +++ b/core/java/android/view/translation/UiTranslationSpec.java @@ -0,0 +1,248 @@ +/* + * 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.translation; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +/** + * Specifications for configuring UI translation. + * + * @hide + */ +@DataClass( + genBuilder = true, genEqualsHashCode = true, genHiddenConstDefs = true, genToString = true) +@DataClass.Suppress("isShouldPadContentForCompat") +@SystemApi +public final class UiTranslationSpec implements Parcelable { + + /** + * Whether the original content of the view should be directly modified to include padding that + * makes it the same size as the translated content. Defaults to {@code false}. + * <p> + * For {@link android.widget.TextView}, the system does not directly modify the original text, + * rather changes the displayed content using a + * {@link android.text.method.TransformationMethod}. + * This can cause issues in apps that do not account for TransformationMethods. For example, an + * app using DynamicLayout may use the calculated text offsets to operate on the original text, + * but this can be problematic when the layout was calculated on translated text with a + * different length. + * <p> + * If this is {@code true}, for a TextView the default implementation will append spaces to the + * text to make the length the same as the translated text. + */ + private boolean mShouldPadContentForCompat = false; + + /** + * Whether the original content of the view should be directly modified to include padding that + * makes it the same size as the translated content. + * <p> + * For {@link android.widget.TextView}, the system does not directly modify the original text, + * rather changes the displayed content using a + * {@link android.text.method.TransformationMethod}. + * This can cause issues in apps that do not account for TransformationMethods. For example, an + * app using DynamicLayout may use the calculated text offsets to operate on the original text, + * but this can be problematic when the layout was calculated on translated text with a + * different length. + * <p> + * If this is {@code true}, for a TextView the default implementation will append spaces to the + * text to make the length the same as the translated text. + */ + public boolean shouldPadContentForCompat() { + return mShouldPadContentForCompat; + } + + + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/translation/UiTranslationSpec.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ UiTranslationSpec( + boolean shouldPadContentForCompat) { + this.mShouldPadContentForCompat = shouldPadContentForCompat; + + // onConstructed(); // You can define this method to get a callback + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "UiTranslationSpec { " + + "shouldPadContentForCompat = " + mShouldPadContentForCompat + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(UiTranslationSpec other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + UiTranslationSpec that = (UiTranslationSpec) o; + //noinspection PointlessBooleanExpression + return true + && mShouldPadContentForCompat == that.mShouldPadContentForCompat; + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + Boolean.hashCode(mShouldPadContentForCompat); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mShouldPadContentForCompat) flg |= 0x1; + dest.writeByte(flg); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ UiTranslationSpec(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + boolean shouldPadContentForCompat = (flg & 0x1) != 0; + + this.mShouldPadContentForCompat = shouldPadContentForCompat; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<UiTranslationSpec> CREATOR + = new Parcelable.Creator<UiTranslationSpec>() { + @Override + public UiTranslationSpec[] newArray(int size) { + return new UiTranslationSpec[size]; + } + + @Override + public UiTranslationSpec createFromParcel(@NonNull Parcel in) { + return new UiTranslationSpec(in); + } + }; + + /** + * A builder for {@link UiTranslationSpec} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder { + + private boolean mShouldPadContentForCompat; + + private long mBuilderFieldsSet = 0L; + + public Builder() { + } + + /** + * Whether the original content of the view should be directly modified to include padding that + * makes it the same size as the translated content. Defaults to {@code false}. + * <p> + * For {@link android.widget.TextView}, the system does not directly modify the original text, + * rather changes the displayed content using a + * {@link android.text.method.TransformationMethod}. + * This can cause issues in apps that do not account for TransformationMethods. For example, an + * app using DynamicLayout may use the calculated text offsets to operate on the original text, + * but this can be problematic when the layout was calculated on translated text with a + * different length. + * <p> + * If this is {@code true}, for a TextView the default implementation will append spaces to the + * text to make the length the same as the translated text. + */ + @DataClass.Generated.Member + public @NonNull Builder setShouldPadContentForCompat(boolean value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mShouldPadContentForCompat = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull UiTranslationSpec build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; // Mark builder used + + if ((mBuilderFieldsSet & 0x1) == 0) { + mShouldPadContentForCompat = false; + } + UiTranslationSpec o = new UiTranslationSpec( + mShouldPadContentForCompat); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x2) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1619034161701L, + codegenVersion = "1.0.23", + sourceFile = "frameworks/base/core/java/android/view/translation/UiTranslationSpec.java", + inputSignatures = "private boolean mShouldPadContentForCompat\npublic boolean shouldPadContentForCompat()\nclass UiTranslationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genToString=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index 18dd7995ae87..33890b80869d 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -939,6 +939,7 @@ public final class Magnifier { // The surface we allocate for the magnifier content + shadow. private final SurfaceSession mSurfaceSession; private final SurfaceControl mSurfaceControl; + private final SurfaceControl mBbqSurfaceControl; private final BLASTBufferQueue mBBQ; private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); private final Surface mSurface; @@ -1008,11 +1009,19 @@ public final class Magnifier { mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) .setName("magnifier surface") .setFlags(SurfaceControl.HIDDEN) - .setBLASTLayer() + .setContainerLayer() .setParent(parentSurfaceControl) .setCallsite("InternalPopupWindow") .build(); - mBBQ = new BLASTBufferQueue("magnifier surface", mSurfaceControl, + mBbqSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) + .setName("magnifier surface bbq wrapper") + .setHidden(false) + .setBLASTLayer() + .setParent(mSurfaceControl) + .setCallsite("InternalPopupWindow") + .build(); + + mBBQ = new BLASTBufferQueue("magnifier surface", mBbqSurfaceControl, surfaceWidth, surfaceHeight, PixelFormat.TRANSLUCENT); mSurface = mBBQ.createSurface(); @@ -1073,7 +1082,7 @@ public final class Magnifier { } if (mContentHeight < contentHeight) { // Grows the surface height as necessary. - mBBQ.update(mSurfaceControl, mContentWidth, contentHeight, + mBBQ.update(mBbqSurfaceControl, mContentWidth, contentHeight, PixelFormat.TRANSLUCENT); mRenderer.setSurface(mSurface); @@ -1270,7 +1279,10 @@ public final class Magnifier { mRenderer.destroy(); mSurface.destroy(); mBBQ.destroy(); - new SurfaceControl.Transaction().remove(mSurfaceControl).apply(); + new SurfaceControl.Transaction() + .remove(mSurfaceControl) + .remove(mBbqSurfaceControl) + .apply(); mSurfaceSession.kill(); mHandler.removeCallbacks(mMagnifierUpdater); if (mBitmap != null) { diff --git a/core/java/android/window/PictureInPictureSurfaceTransaction.java b/core/java/android/window/PictureInPictureSurfaceTransaction.java index bde91d90b0f9..96a8ac8c0587 100644 --- a/core/java/android/window/PictureInPictureSurfaceTransaction.java +++ b/core/java/android/window/PictureInPictureSurfaceTransaction.java @@ -16,10 +16,13 @@ package android.window; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.graphics.Matrix; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; +import android.view.SurfaceControl; import java.util.Objects; @@ -127,6 +130,22 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable { + ")"; } + /** Applies {@link PictureInPictureSurfaceTransaction} to a given leash. */ + public static void apply(@NonNull PictureInPictureSurfaceTransaction surfaceTransaction, + @NonNull SurfaceControl surfaceControl, + @NonNull SurfaceControl.Transaction tx) { + final Matrix matrix = new Matrix(); + matrix.setScale(surfaceTransaction.mScaleX, surfaceTransaction.mScaleY); + if (surfaceTransaction.mRotation != 0) { + matrix.postRotate(surfaceTransaction.mRotation); + } + tx.setMatrix(surfaceControl, matrix, new float[9]) + .setPosition(surfaceControl, + surfaceTransaction.mPositionX, surfaceTransaction.mPositionY) + .setWindowCrop(surfaceControl, surfaceTransaction.getWindowCrop()) + .setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius); + } + public static final @android.annotation.NonNull Creator<PictureInPictureSurfaceTransaction> CREATOR = new Creator<PictureInPictureSurfaceTransaction>() { diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java index 18f29ae8eeca..7d222dbe7bd2 100644 --- a/core/java/android/window/SplashScreen.java +++ b/core/java/android/window/SplashScreen.java @@ -17,12 +17,17 @@ package android.window; import android.annotation.NonNull; +import android.annotation.StyleRes; import android.annotation.SuppressLint; import android.annotation.UiThread; import android.app.Activity; import android.app.ActivityThread; +import android.app.AppGlobals; import android.content.Context; +import android.content.res.Resources; import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; import android.util.Singleton; import android.util.Slog; @@ -60,6 +65,17 @@ public interface SplashScreen { */ void clearOnExitAnimationListener(); + + /** + * Overrides the theme used for the {@link SplashScreen}s of this application. + * <p> + * By default, the {@link SplashScreen} uses the theme set in the manifest. This method + * overrides and persists the theme used for the {@link SplashScreen} of this application. + * <p> + * To reset to the default theme, set this the themeId to {@link Resources#ID_NULL}. + */ + void setSplashScreenTheme(@StyleRes int themeId); + /** * Listens for the splash screen exit event. */ @@ -84,6 +100,8 @@ public interface SplashScreen { * @hide */ class SplashScreenImpl implements SplashScreen { + private static final String TAG = "SplashScreenImpl"; + private OnExitAnimationListener mExitAnimationListener; private final IBinder mActivityToken; private final SplashScreenManagerGlobal mGlobal; @@ -119,6 +137,29 @@ public interface SplashScreen { mGlobal.removeImpl(this); } } + + public void setSplashScreenTheme(@StyleRes int themeId) { + if (mActivityToken == null) { + Log.w(TAG, "Couldn't persist the starting theme. This instance is not an Activity"); + return; + } + + Activity activity = ActivityThread.currentActivityThread().getActivity( + mActivityToken); + if (activity == null) { + return; + } + String themeName = themeId != Resources.ID_NULL + ? activity.getResources().getResourceName(themeId) : null; + + try { + AppGlobals.getPackageManager().setSplashScreenTheme( + activity.getComponentName().getPackageName(), + themeName, activity.getUserId()); + } catch (RemoteException e) { + Log.w(TAG, "Couldn't persist the starting theme", e); + } + } } /** diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index f1515276e2c6..23b8ee4a019f 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -77,8 +77,11 @@ public final class TransitionInfo implements Parcelable { /** The container is the recipient of a transferred starting-window */ public static final int FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT = 1 << 3; + /** The container has voice session. */ + public static final int FLAG_IS_VOICE_INTERACTION = 1 << 4; + /** The first unused bit. This can be used by remotes to attach custom flags to this change. */ - public static final int FLAG_FIRST_CUSTOM = 1 << 4; + public static final int FLAG_FIRST_CUSTOM = 1 << 5; /** @hide */ @IntDef(prefix = { "FLAG_" }, value = { @@ -86,7 +89,9 @@ public final class TransitionInfo implements Parcelable { FLAG_SHOW_WALLPAPER, FLAG_IS_WALLPAPER, FLAG_TRANSLUCENT, - FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT + FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT, + FLAG_IS_VOICE_INTERACTION, + FLAG_FIRST_CUSTOM }) public @interface ChangeFlags {} @@ -249,6 +254,12 @@ public final class TransitionInfo implements Parcelable { if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { sb.append((sb.length() == 0 ? "" : "|") + "STARTING_WINDOW_TRANSFER"); } + if ((flags & FLAG_IS_VOICE_INTERACTION) != 0) { + sb.append((sb.length() == 0 ? "" : "|") + "IS_VOICE_INTERACTION"); + } + if ((flags & FLAG_FIRST_CUSTOM) != 0) { + sb.append((sb.length() == 0 ? "" : "|") + "FIRST_CUSTOM"); + } return sb.toString(); } diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 26a6f0dcf470..c0af57214e5e 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -109,8 +109,8 @@ public final class WindowContainerTransaction implements Parcelable { } /** - * Notify activities within the hierarchy of a container that they have entered picture-in-picture - * mode with the given bounds. + * Notify {@link com.android.server.wm.PinnedTaskController} that the picture-in-picture task + * has finished the enter animation with the given bounds. */ @NonNull public WindowContainerTransaction scheduleFinishEnterPip( @@ -339,6 +339,33 @@ public final class WindowContainerTransaction implements Parcelable { } /** + * Sets the container as launch adjacent flag root. Task starting with + * {@link FLAG_ACTIVITY_LAUNCH_ADJACENT} will be launching to. + * + * @hide + */ + @NonNull + public WindowContainerTransaction setLaunchAdjacentFlagRoot( + @NonNull WindowContainerToken container) { + mHierarchyOps.add(HierarchyOp.createForSetLaunchAdjacentFlagRoot(container.asBinder(), + false /* clearRoot */)); + return this; + } + + /** + * Clears launch adjacent flag root for the display area of passing container. + * + * @hide + */ + @NonNull + public WindowContainerTransaction clearLaunchAdjacentFlagRoot( + @NonNull WindowContainerToken container) { + mHierarchyOps.add(HierarchyOp.createForSetLaunchAdjacentFlagRoot(container.asBinder(), + true /* clearRoot */)); + return this; + } + + /** * Starts a task by id. The task is expected to already exist (eg. as a recent task). * @param taskId Id of task to start. * @param options bundle containing ActivityOptions for the task's top activity. @@ -677,6 +704,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT = 3; public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4; public static final int HIERARCHY_OP_TYPE_LAUNCH_TASK = 5; + public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT = 6; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -734,6 +762,14 @@ public final class WindowContainerTransaction implements Parcelable { fullOptions); } + /** Create a hierarchy op for setting launch adjacent flag root. */ + public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container, + boolean clearRoot) { + return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT, container, null, + null, null, clearRoot, null); + } + + private HierarchyOp(int type, @Nullable IBinder container, @Nullable IBinder reparent, int[] windowingModes, int[] activityTypes, boolean toTop, @Nullable Bundle launchOptions) { @@ -829,6 +865,9 @@ public final class WindowContainerTransaction implements Parcelable { + " adjacentRoot=" + mReparent + "}"; case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "{LaunchTask: " + mLaunchOptions + "}"; + case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: + return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop + + "}"; default: return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java index 375e5036c083..f34aabbeded6 100644 --- a/core/java/com/android/internal/app/NetInitiatedActivity.java +++ b/core/java/com/android/internal/app/NetInitiatedActivity.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; + import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -69,6 +71,8 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + // Set up the "dialog" final Intent intent = getIntent(); final AlertController.AlertParams p = mAlertParams; diff --git a/core/java/com/android/internal/colorextraction/OWNERS b/core/java/com/android/internal/colorextraction/OWNERS new file mode 100644 index 000000000000..ffade1ec4ebd --- /dev/null +++ b/core/java/com/android/internal/colorextraction/OWNERS @@ -0,0 +1,3 @@ +dupin@google.com +cinek@google.com +jamesoleary@google.com diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index efce0a8487c7..06f68e8273f9 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -81,6 +81,12 @@ public final class SystemUiDeviceConfigFlags { public static final String SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS = "screenshot_notification_smart_actions_timeout_ms"; + /** + * (int) Timeout value in ms to get Quick Share actions for screenshot notification. + */ + public static final String SCREENSHOT_NOTIFICATION_QUICK_SHARE_ACTIONS_TIMEOUT_MS = + "screenshot_notification_quick_share_actions_timeout_ms"; + // Flags related to Smart Suggestions - these are read in SmartReplyConstants. /** (boolean) Whether to enable smart suggestions in notifications. */ diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java index 6776c27fc16e..bd908900fccc 100644 --- a/core/java/com/android/internal/display/BrightnessSynchronizer.java +++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java @@ -16,11 +16,11 @@ package com.android.internal.display; - import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManager.DisplayListener; import android.net.Uri; import android.os.Handler; import android.os.Looper; @@ -95,7 +95,7 @@ public class BrightnessSynchronizer { } final BrightnessSyncObserver brightnessSyncObserver; - brightnessSyncObserver = new BrightnessSyncObserver(mHandler); + brightnessSyncObserver = new BrightnessSyncObserver(); brightnessSyncObserver.startObserving(); final float currentFloatBrightness = getScreenBrightnessFloat(); @@ -232,47 +232,52 @@ public class BrightnessSynchronizer { } } - private class BrightnessSyncObserver extends ContentObserver { - /** - * Creates a content observer. - * @param handler The handler to run {@link #onChange} on, or null if none. - */ - BrightnessSyncObserver(Handler handler) { - super(handler); - } + private class BrightnessSyncObserver { + private final DisplayListener mListener = new DisplayListener() { + @Override + public void onDisplayAdded(int displayId) {} - @Override - public void onChange(boolean selfChange) { - onChange(selfChange, null); - } + @Override + public void onDisplayRemoved(int displayId) {} - @Override - public void onChange(boolean selfChange, Uri uri) { - if (selfChange) { - return; - } - if (BRIGHTNESS_URI.equals(uri)) { - int currentBrightness = getScreenBrightnessInt(mContext); - mHandler.removeMessages(MSG_UPDATE_FLOAT); - mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget(); - } else if (BRIGHTNESS_FLOAT_URI.equals(uri)) { + @Override + public void onDisplayChanged(int displayId) { float currentFloat = getScreenBrightnessFloat(); int toSend = Float.floatToIntBits(currentFloat); mHandler.removeMessages(MSG_UPDATE_INT); mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget(); } - } + }; + + private final ContentObserver mContentObserver = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange, Uri uri) { + if (selfChange) { + return; + } + if (BRIGHTNESS_URI.equals(uri)) { + int currentBrightness = getScreenBrightnessInt(mContext); + mHandler.removeMessages(MSG_UPDATE_FLOAT); + mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget(); + } + } + }; public void startObserving() { final ContentResolver cr = mContext.getContentResolver(); - cr.unregisterContentObserver(this); - cr.registerContentObserver(BRIGHTNESS_URI, false, this, UserHandle.USER_ALL); - cr.registerContentObserver(BRIGHTNESS_FLOAT_URI, false, this, UserHandle.USER_ALL); + cr.unregisterContentObserver(mContentObserver); + cr.registerContentObserver(BRIGHTNESS_URI, false, mContentObserver, + UserHandle.USER_ALL); + + mDisplayManager.registerDisplayListener(mListener, mHandler, + DisplayManager.EVENT_FLAG_DISPLAY_CHANGED + | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS); } public void stopObserving() { final ContentResolver cr = mContext.getContentResolver(); - cr.unregisterContentObserver(this); + cr.unregisterContentObserver(mContentObserver); + mDisplayManager.unregisterDisplayListener(mListener); } } } diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index 135c076275ba..4d3f7745decf 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -102,6 +102,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener private boolean mMetricsFinalized; private boolean mCancelled = false; private FrameTrackerListener mListener; + private boolean mTracingStarted = false; private static class JankInfo { long frameVsyncId; @@ -207,7 +208,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener public synchronized void begin() { mBeginVsyncId = mChoreographer.getVsyncId() + 1; mSession.setTimeStamp(System.nanoTime()); - Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId); + mChoreographer.mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, () -> { + synchronized (FrameTracker.this) { + if (mCancelled || mEndVsyncId != INVALID_ID) { + return; + } + mTracingStarted = true; + Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId); + } + }, null); mRendererWrapper.addObserver(mObserver); if (DEBUG) { Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId); @@ -255,7 +264,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener */ public synchronized void cancel(@Reasons int reason) { // We don't need to end the trace section if it never begun. - if (mBeginVsyncId != INVALID_ID) { + if (mTracingStarted) { Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId); } mCancelled = true; diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 5ab2a82ffa3c..04415941c5ce 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -79,6 +79,12 @@ import java.util.concurrent.TimeUnit; /** * This class let users to begin and end the always on tracing mechanism. + * + * Enabling for local development: + * + * adb shell device_config put interaction_jank_monitor enabled true + * adb shell device_config put interaction_jank_monitor sampling_interval 1 + * * @hide */ public class InteractionJankMonitor { diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java index aa416c568dbc..73d962effc00 100644 --- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java +++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java @@ -53,7 +53,7 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { builder.getOrCreateSystemBatteryConsumerBuilder( SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, powerMah, powerModel) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, durationMs); + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, durationMs); } /** diff --git a/core/java/com/android/internal/os/AudioPowerCalculator.java b/core/java/com/android/internal/os/AudioPowerCalculator.java index 79b331da9c8a..9da8191f3747 100644 --- a/core/java/com/android/internal/os/AudioPowerCalculator.java +++ b/core/java/com/android/internal/os/AudioPowerCalculator.java @@ -42,7 +42,7 @@ public class AudioPowerCalculator extends PowerCalculator { final long durationMs = mPowerEstimator.calculateDuration(u.getAudioTurnedOnTimer(), rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); final double powerMah = mPowerEstimator.calculatePower(durationMs); - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO, durationMs) + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO, powerMah); } } diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index 9ad7c15b9a9c..babcea14d0ea 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -124,16 +124,11 @@ public class BatteryUsageStatsProvider { final long realtimeUs = elapsedRealtime() * 1000; final long uptimeUs = uptimeMillis() * 1000; - final String[] customPowerComponentNames = mStats.getCustomEnergyConsumerNames(); - - // TODO(b/174186358): read extra time component number from configuration - final int customTimeComponentCount = 0; - final boolean includePowerModels = (query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder( - customPowerComponentNames, customTimeComponentCount, includePowerModels); + mStats.getCustomEnergyConsumerNames(), includePowerModels); batteryUsageStatsBuilder.setStatsStartTimestamp(mStats.getStartClockTime()); SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats(); diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java index a418dfff5d8f..2c32e48c9685 100644 --- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java +++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java @@ -88,7 +88,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { + " power=" + formatCharge(systemPowerMah)); } systemBatteryConsumerBuilder - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH, + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, systemComponentDurationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, Math.max(systemPowerMah, total.powerMah), powerModel) @@ -105,7 +105,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { final long durationMs = calculateDuration(activityCounter); final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter); - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH, durationMs) + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah, powerModel); total.durationMs += durationMs; diff --git a/core/java/com/android/internal/os/CameraPowerCalculator.java b/core/java/com/android/internal/os/CameraPowerCalculator.java index 6f8e9271d198..e56e7beddecb 100644 --- a/core/java/com/android/internal/os/CameraPowerCalculator.java +++ b/core/java/com/android/internal/os/CameraPowerCalculator.java @@ -42,7 +42,7 @@ public class CameraPowerCalculator extends PowerCalculator { mPowerEstimator.calculateDuration(u.getCameraTurnedOnTimer(), rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); final double powerMah = mPowerEstimator.calculatePower(durationMs); - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CAMERA, durationMs) + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA, powerMah); } diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index 4aafec555182..2a55aa9daf6c 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -97,9 +97,7 @@ public class CpuPowerCalculator extends PowerCalculator { result); app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah, powerModel) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, result.durationMs) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, - result.durationFgMs) + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU, result.durationMs) .setPackageWithHighestDrain(result.packageWithHighestDrain); } diff --git a/core/java/com/android/internal/os/FlashlightPowerCalculator.java b/core/java/com/android/internal/os/FlashlightPowerCalculator.java index 6c29a91f081f..cbe0cde2f5b7 100644 --- a/core/java/com/android/internal/os/FlashlightPowerCalculator.java +++ b/core/java/com/android/internal/os/FlashlightPowerCalculator.java @@ -39,7 +39,7 @@ public class FlashlightPowerCalculator extends PowerCalculator { final long durationMs = mPowerEstimator.calculateDuration(u.getFlashlightTurnedOnTimer(), rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); final double powerMah = mPowerEstimator.calculatePower(durationMs); - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_FLASHLIGHT, durationMs) + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, powerMah); } diff --git a/core/java/com/android/internal/os/GnssPowerCalculator.java b/core/java/com/android/internal/os/GnssPowerCalculator.java index 0e0870de4f5f..7eb4b4a9bb1c 100644 --- a/core/java/com/android/internal/os/GnssPowerCalculator.java +++ b/core/java/com/android/internal/os/GnssPowerCalculator.java @@ -74,7 +74,7 @@ public class GnssPowerCalculator extends PowerCalculator { powerMah = computePower(durationMs, averageGnssPowerMa); } - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS, durationMs) + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_GNSS, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS, powerMah, powerModel); } diff --git a/core/java/com/android/internal/os/IdlePowerCalculator.java b/core/java/com/android/internal/os/IdlePowerCalculator.java index 5cb54bd7fbc9..0c80deb49259 100644 --- a/core/java/com/android/internal/os/IdlePowerCalculator.java +++ b/core/java/com/android/internal/os/IdlePowerCalculator.java @@ -55,7 +55,7 @@ public class IdlePowerCalculator extends PowerCalculator { if (mPowerMah != 0) { builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_IDLE) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_IDLE, mPowerMah) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_IDLE, mDurationMs); + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_IDLE, mDurationMs); } } diff --git a/core/java/com/android/internal/os/MemoryPowerCalculator.java b/core/java/com/android/internal/os/MemoryPowerCalculator.java index 9ec40c6f6eed..5d5c1558f716 100644 --- a/core/java/com/android/internal/os/MemoryPowerCalculator.java +++ b/core/java/com/android/internal/os/MemoryPowerCalculator.java @@ -32,7 +32,7 @@ public class MemoryPowerCalculator extends PowerCalculator { final double powerMah = calculatePower(batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_MEMORY) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MEMORY, durationMs) + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MEMORY, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MEMORY, powerMah); } diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java index d441d4529aca..4db15a44231e 100644 --- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java +++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java @@ -107,7 +107,7 @@ public class MobileRadioPowerCalculator extends PowerCalculator { if (total.remainingPowerMah != 0 || total.totalAppPowerMah != 0) { builder.getOrCreateSystemBatteryConsumerBuilder( SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO, + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, total.durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, total.remainingPowerMah + total.totalAppPowerMah, @@ -128,7 +128,7 @@ public class MobileRadioPowerCalculator extends PowerCalculator { radioActiveDurationMs, consumptionUC); total.totalAppPowerMah += powerMah; - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO, + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, radioActiveDurationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, powerMah, powerModel); diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java index 6f279d99a5c9..2e3bff32cc38 100644 --- a/core/java/com/android/internal/os/PhonePowerCalculator.java +++ b/core/java/com/android/internal/os/PhonePowerCalculator.java @@ -46,7 +46,7 @@ public class PhonePowerCalculator extends PowerCalculator { if (phoneOnPower != 0) { builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_PHONE) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnPower) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_PHONE, phoneOnTimeMs); + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnTimeMs); } } diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java index 0743c897a91f..dc0f719c042b 100644 --- a/core/java/com/android/internal/os/ScreenPowerCalculator.java +++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java @@ -81,7 +81,7 @@ public class ScreenPowerCalculator extends PowerCalculator { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.getBatteryStatsUid(), rawRealtimeUs); - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, appPowerAndDuration.durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, appPowerAndDuration.powerMah, powerModel); @@ -96,7 +96,7 @@ public class ScreenPowerCalculator extends PowerCalculator { } builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, totalPowerAndDuration.durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, Math.max(totalPowerAndDuration.powerMah, totalAppPower), powerModel) @@ -251,7 +251,7 @@ public class ScreenPowerCalculator extends PowerCalculator { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); final long durationMs = activityTimeArray.get(app.getUid(), 0); final double powerMah = totalScreenPowerMah * durationMs / totalActivityTimeMs; - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, durationMs) + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, powerMah, BatteryConsumer.POWER_MODEL_POWER_PROFILE); } diff --git a/core/java/com/android/internal/os/SensorPowerCalculator.java b/core/java/com/android/internal/os/SensorPowerCalculator.java index 78c4fe2060f1..d18b7b1f69c2 100644 --- a/core/java/com/android/internal/os/SensorPowerCalculator.java +++ b/core/java/com/android/internal/os/SensorPowerCalculator.java @@ -40,7 +40,7 @@ public class SensorPowerCalculator extends PowerCalculator { @Override protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SENSORS, + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SENSORS, calculateDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED)) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SENSORS, calculatePowerMah(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED)); diff --git a/core/java/com/android/internal/os/VideoPowerCalculator.java b/core/java/com/android/internal/os/VideoPowerCalculator.java index 5d6caf578475..0cad9a72ceec 100644 --- a/core/java/com/android/internal/os/VideoPowerCalculator.java +++ b/core/java/com/android/internal/os/VideoPowerCalculator.java @@ -39,7 +39,7 @@ public class VideoPowerCalculator extends PowerCalculator { final long durationMs = mPowerEstimator.calculateDuration(u.getVideoTurnedOnTimer(), rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); final double powerMah = mPowerEstimator.calculatePower(durationMs); - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO, durationMs) + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_VIDEO, powerMah); } } diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java index 0f4767b859a3..194b6b82cd53 100644 --- a/core/java/com/android/internal/os/WakelockPowerCalculator.java +++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java @@ -57,7 +57,7 @@ public class WakelockPowerCalculator extends PowerCalculator { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); calculateApp(result, app.getBatteryStatsUid(), rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK, result.durationMs) + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah); totalAppDurationMs += result.durationMs; @@ -74,7 +74,7 @@ public class WakelockPowerCalculator extends PowerCalculator { if (osBatteryConsumer != null) { calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs, BatteryStats.STATS_SINCE_CHARGED, osPowerMah, osDurationMs, totalAppDurationMs); - osBatteryConsumer.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK, + osBatteryConsumer.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah); } diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java index 11219eccddad..ef5b147e00e4 100644 --- a/core/java/com/android/internal/os/WifiPowerCalculator.java +++ b/core/java/com/android/internal/os/WifiPowerCalculator.java @@ -100,7 +100,7 @@ public class WifiPowerCalculator extends PowerCalculator { totalAppDurationMs += powerDurationAndTraffic.durationMs; totalAppPowerMah += powerDurationAndTraffic.powerMah; - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI, + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI, powerDurationAndTraffic.durationMs); app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI, powerDurationAndTraffic.powerMah, powerModel); @@ -118,7 +118,7 @@ public class WifiPowerCalculator extends PowerCalculator { totalAppDurationMs, totalAppPowerMah, consumptionUC); systemBatteryConsumerBuilder - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI, + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI, powerDurationAndTraffic.durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI, totalAppPowerMah + powerDurationAndTraffic.powerMah, powerModel) diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java index 93ba0372df08..60a8d802861f 100644 --- a/core/java/com/android/internal/policy/TransitionAnimation.java +++ b/core/java/com/android/internal/policy/TransitionAnimation.java @@ -21,7 +21,6 @@ import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; -import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; @@ -76,6 +75,8 @@ public class TransitionAnimation { /** Fraction of animation at which the recents thumbnail becomes completely transparent */ private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f; + private static final String DEFAULT_PACKAGE = "android"; + private final Context mContext; private final String mTag; @@ -132,7 +133,8 @@ public class TransitionAnimation { windowStyle.recycle(); } - public Animation loadKeyguardExitAnimation(int transit, int transitionFlags) { + /** Loads keyguard animation by transition flags and check it is on wallpaper or not. */ + public Animation loadKeyguardExitAnimation(int transitionFlags, boolean onWallpaper) { if ((transitionFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) != 0) { return null; } @@ -140,25 +142,24 @@ public class TransitionAnimation { (transitionFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0; final boolean subtle = (transitionFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0; - return createHiddenByKeyguardExit(mContext, mInterpolator, - transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER, toShade, subtle); + return createHiddenByKeyguardExit(mContext, mInterpolator, onWallpaper, toShade, subtle); } @Nullable - public Animation loadKeyguardUnoccludeAnimation(LayoutParams lp) { - return loadAnimationRes(lp, com.android.internal.R.anim.wallpaper_open_exit); + public Animation loadKeyguardUnoccludeAnimation() { + return loadDefaultAnimationRes(com.android.internal.R.anim.wallpaper_open_exit); } @Nullable - public Animation loadVoiceActivityOpenAnimation(LayoutParams lp, boolean enter) { - return loadAnimationRes(lp, enter + public Animation loadVoiceActivityOpenAnimation(boolean enter) { + return loadDefaultAnimationRes(enter ? com.android.internal.R.anim.voice_activity_open_enter : com.android.internal.R.anim.voice_activity_open_exit); } @Nullable - public Animation loadVoiceActivityExitAnimation(LayoutParams lp, boolean enter) { - return loadAnimationRes(lp, enter + public Animation loadVoiceActivityExitAnimation(boolean enter) { + return loadDefaultAnimationRes(enter ? com.android.internal.R.anim.voice_activity_close_enter : com.android.internal.R.anim.voice_activity_close_exit); } @@ -170,33 +171,19 @@ public class TransitionAnimation { @Nullable public Animation loadCrossProfileAppEnterAnimation() { - return loadAnimationRes("android", + return loadAnimationRes(DEFAULT_PACKAGE, com.android.internal.R.anim.task_open_enter_cross_profile_apps); } @Nullable public Animation loadCrossProfileAppThumbnailEnterAnimation() { return loadAnimationRes( - "android", com.android.internal.R.anim.cross_profile_apps_thumbnail_enter); - } - - /** Load animation by resource Id from specific LayoutParams. */ - @Nullable - private Animation loadAnimationRes(LayoutParams lp, int resId) { - Context context = mContext; - if (ResourceId.isValid(resId)) { - AttributeCache.Entry ent = getCachedAnimations(lp); - if (ent != null) { - context = ent.context; - } - return loadAnimationSafely(context, resId, mTag); - } - return null; + DEFAULT_PACKAGE, com.android.internal.R.anim.cross_profile_apps_thumbnail_enter); } /** Load animation by resource Id from specific package. */ @Nullable - private Animation loadAnimationRes(String packageName, int resId) { + public Animation loadAnimationRes(String packageName, int resId) { if (ResourceId.isValid(resId)) { AttributeCache.Entry ent = getCachedAnimations(packageName, resId); if (ent != null) { @@ -209,7 +196,7 @@ public class TransitionAnimation { /** Load animation by resource Id from android package. */ @Nullable public Animation loadDefaultAnimationRes(int resId) { - return loadAnimationRes("android", resId); + return loadAnimationRes(DEFAULT_PACKAGE, resId); } /** Load animation by attribute Id from specific LayoutParams */ @@ -237,7 +224,7 @@ public class TransitionAnimation { int resId = Resources.ID_NULL; Context context = mContext; if (animAttr >= 0) { - AttributeCache.Entry ent = getCachedAnimations("android", + AttributeCache.Entry ent = getCachedAnimations(DEFAULT_PACKAGE, mDefaultWindowAnimationStyleResId); if (ent != null) { context = ent.context; @@ -261,10 +248,10 @@ public class TransitionAnimation { // If this is a system resource, don't try to load it from the // application resources. It is nice to avoid loading application // resources if we can. - String packageName = lp.packageName != null ? lp.packageName : "android"; + String packageName = lp.packageName != null ? lp.packageName : DEFAULT_PACKAGE; int resId = getAnimationStyleResId(lp); if ((resId & 0xFF000000) == 0x01000000) { - packageName = "android"; + packageName = DEFAULT_PACKAGE; } if (mDebug) { Slog.v(mTag, "Loading animations: picked package=" + packageName); @@ -283,7 +270,7 @@ public class TransitionAnimation { } if (packageName != null) { if ((resId & 0xFF000000) == 0x01000000) { - packageName = "android"; + packageName = DEFAULT_PACKAGE; } if (mDebug) { Slog.v(mTag, "Loading animations: picked package=" diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl index 49dbbaaa17f7..a61e86bcf2a6 100644 --- a/core/java/com/android/internal/view/IInputMethodClient.aidl +++ b/core/java/com/android/internal/view/IInputMethodClient.aidl @@ -30,4 +30,5 @@ oneway interface IInputMethodClient { void reportFullscreenMode(boolean fullscreen); void updateActivityViewToScreenMatrix(int bindSequence, in float[] matrixValues); void setImeTraceEnabled(boolean enabled); + void throwExceptionFromSystem(String message); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 93cd4e9046c6..772e3449355d 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -68,6 +68,12 @@ interface IInputMethodManager { int unverifiedTargetSdkVersion, in IInputBindResultResultCallback inputBindResult); + oneway void reportWindowGainedFocusAsync( + boolean nextFocusHasConnection, in IInputMethodClient client, in IBinder windowToken, + /* @StartInputFlags */ int startInputFlags, + /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode, + int windowFlags, int unverifiedTargetSdkVersion); + oneway void showInputMethodPickerFromClient(in IInputMethodClient client, int auxiliarySubtypeMode, in IVoidResultCallback resultCallback); oneway void showInputMethodPickerFromSystem(in IInputMethodClient client, diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index bab4e93bdd9a..ea0f704829c8 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -1082,15 +1082,18 @@ public class ConversationLayout extends FrameLayout private void updateExpandButton() { int buttonGravity; - int containerHeight; ViewGroup newContainer; if (mIsCollapsed) { buttonGravity = Gravity.CENTER; - containerHeight = ViewGroup.LayoutParams.WRAP_CONTENT; + // NOTE(b/182474419): In order for the touch target of the expand button to be the full + // height of the notification, we would want the mExpandButtonContainer's height to be + // set to WRAP_CONTENT (or 88dp) when in the collapsed state. Unfortunately, that + // causes an unstable remeasuring infinite loop when the unread count is visible, + // causing the layout to occasionally hide the messages. As an aside, that naive + // solution also causes an undesirably large gap between content and smart replies. newContainer = mExpandButtonAndContentContainer; } else { buttonGravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; - containerHeight = ViewGroup.LayoutParams.MATCH_PARENT; newContainer = this; } mExpandButton.setExpanded(!mIsCollapsed); @@ -1099,7 +1102,6 @@ public class ConversationLayout extends FrameLayout // content when collapsed, but allows the content to flow under it when expanded. if (newContainer != mExpandButtonContainer.getParent()) { ((ViewGroup) mExpandButtonContainer.getParent()).removeView(mExpandButtonContainer); - mExpandButtonContainer.getLayoutParams().height = containerHeight; newContainer.addView(mExpandButtonContainer); } diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java index 940979d7cd1f..0a2c18f83fef 100644 --- a/core/java/com/android/internal/widget/LockSettingsInternal.java +++ b/core/java/com/android/internal/widget/LockSettingsInternal.java @@ -110,7 +110,7 @@ public abstract class LockSettingsInternal { * #setRebootEscrowListener}, then {@link #armRebootEscrow()} should be called before * rebooting to apply the update. */ - public abstract void prepareRebootEscrow(); + public abstract boolean prepareRebootEscrow(); /** * Registers a listener for when the RebootEscrow HAL has stored its data needed for rebooting @@ -124,7 +124,7 @@ public abstract class LockSettingsInternal { /** * Requests that any data needed for rebooting is cleared from the RebootEscrow HAL. */ - public abstract void clearRebootEscrow(); + public abstract boolean clearRebootEscrow(); /** * Should be called immediately before rebooting for an update. This depends on {@link diff --git a/core/proto/android/providers/OWNERS b/core/proto/android/providers/OWNERS new file mode 100644 index 000000000000..1f5cd9a5c3b8 --- /dev/null +++ b/core/proto/android/providers/OWNERS @@ -0,0 +1 @@ +include /packages/SettingsProvider/OWNERS diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index 8ee0e397191c..c3d159659622 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -285,6 +285,7 @@ message GlobalSettingsProto { // Deprecated, use enable_non_resizable_multi_window optional SettingProto enable_sizecompat_freeform = 7 [ (android.privacy).dest = DEST_AUTOMATIC, deprecated = true ]; optional SettingProto enable_non_resizable_multi_window = 8 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto disable_window_blurs = 9 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Development development = 39; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index bf98a2eeccbb..9c65dac8efb9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2709,10 +2709,9 @@ <permission android:name="android.permission.CREATE_USERS" android:protectionLevel="signature" /> - <!-- @SystemApi @hide Allows an application to access data blobs across users. - This permission is not available to third party applications. --> + <!-- Allows an application to access data blobs across users. --> <permission android:name="android.permission.ACCESS_BLOBS_ACROSS_USERS" - android:protectionLevel="signature|privileged|development" /> + android:protectionLevel="signature|privileged|development|role" /> <!-- @hide Allows an application to set the profile owners and the device owner. This permission is not available to third party applications.--> @@ -5638,7 +5637,7 @@ android:protectionLevel="signature|recents" /> <!-- Allows the caller to change the associations between input devices and displays. Very dangerous! @hide --> - <permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT" + <permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY" android:protectionLevel="signature" /> <!-- Allows query of any normal app on the device, regardless of manifest declarations. diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 445bac5dffa8..2a28f0cdad8d 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1622,7 +1622,7 @@ <string name="media_route_button_content_description" msgid="2299223698196869956">"Saai uit"</string> <string name="media_route_chooser_title" msgid="6646594924991269208">"Koppel aan toestel"</string> <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Saai skerm uit na toestel"</string> - <string name="media_route_chooser_searching" msgid="6119673534251329535">"Soek tans vir toestelle…"</string> + <string name="media_route_chooser_searching" msgid="6119673534251329535">"Soek tans vir toestelle …"</string> <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Instellings"</string> <string name="media_route_controller_disconnect" msgid="7362617572732576959">"Ontkoppel"</string> <string name="media_route_status_scanning" msgid="8045156315309594482">"Skandeer tans..."</string> @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarg"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Onbekende portret"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Onbekende landskap"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Gekanselleer"</string> @@ -1852,9 +1876,11 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Opgedateer deur jou administrateur"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Uitgevee deur jou administrateur"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Batterybespaarder skakel Donkertema aan en beperk of skakel agtergrondaktiwiteit, sommige visuele effekte en ander kenmerke, soos \"Ok Google\", af\n\n"<annotation id="url">"Kom meer te wete"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Batterybespaarder skakel Donkertema aan en beperk of skakel agtergrondaktiwiteit, sommige visuele effekte en ander kenmerke, soos \"Ok Google\", af."</string> - <string name="data_saver_description" msgid="4995164271550590517">"Databespaarder verhoed sommige programme om data in die agtergrond te stuur of te aanvaar om datagebruik te help verminder. \'n Program wat jy tans gebruik kan by data ingaan, maar sal dit dalk minder gereeld doen. Dit kan byvoorbeeld beteken dat prente nie wys totdat jy op hulle tik nie."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> + <string name="data_saver_description" msgid="4995164271550590517">"Databespaarder verhoed sommige programme om data in die agtergrond te stuur of te aanvaar om datagebruik te help verminder. \'n Program wat jy tans gebruik kan by data ingaan, maar sal dit dalk minder gereeld doen. Dit kan byvoorbeeld beteken dat prente nie wys voordat jy op hulle tik nie."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Skakel Databespaarder aan?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Skakel aan"</string> <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273"> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index ee286bdd9d71..f5a8547f0b9c 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"ሞናርክ"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"ኳርቶ"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"ፉልስካፕ"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"አር ኦ ሲ 8ኬ"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"አር ኦ ሲ 16ኬ"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"ፒ አር ሲ 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"ካሁ"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"ካኩ2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"ዩ4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"የማይታወቅ የቁም"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"የማይታወቅ የወርድ"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"ተትቷል"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"በእርስዎ አስተዳዳሪ ተዘምኗል"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"በእርስዎ አስተዳዳሪ ተሰርዟል"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"እሺ"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"የባትሪ ኃይል ቆጣቢ የጠቆር ያለ ገጽታን ያበራል እና የጀርባ እንቅስቃሴን፣ አንዳንድ የእይታ ውጤቶችን እና እንደ «Hey Google» ያሉ ባህሪያትን ይገድባል ወይም ያጠፋል።\n\n"<annotation id="url">"የበለጠ ለመረዳት"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"የባትሪ ኃይል ቆጣቢ የጠቆር ያለ ገጽታን ያበራል እና የጀርባ እንቅስቃሴን፣ አንዳንድ የእይታ ውጤቶችን እና እንደ «Hey Google» ያሉ ባህሪያትን ይገድባል ወይም ያጠፋል።"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"የውሂብ አጠቃቀም እንዲቀንስ ለማገዝ ውሂብ ቆጣቢ አንዳንድ መተግበሪያዎች ከበስተጀርባ ሆነው ውሂብ እንዳይልኩ ወይም እንዳይቀበሉ ይከለክላቸዋል። በአሁኑ ጊዜ እየተጠቀሙበት ያለ መተግበሪያ ውሂብ ሊደርስ ይችላል፣ ነገር ግን ባነሰ ተደጋጋሚነት ሊሆን ይችላል። ይሄ ማለት ለምሳሌ ምስሎችን መታ እስኪያደርጓቸው ድረስ ላይታዩ ይችላሉ ማለት ነው።"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ውሂብ ቆጣቢ ይጥፋ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"አብራ"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index ad295cbb98eb..984069a0fd85 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -1861,6 +1861,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"فولسكاب"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1897,6 +1919,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"عمودي غير معروف"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"أفقي غير معروف"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"ملغاة"</string> @@ -1944,8 +1968,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"تم التحديث بواسطة المشرف"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"تم الحذف بواسطة المشرف"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"حسنًا"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"يؤدي استخدام خيار \"توفير شحن البطارية\" إلى تفعيل \"المظهر الداكن\" وتقييد أو إيقاف النشاط في الخلفية وبعض التأثيرات المرئية والميزات الأخرى، مثلاً \"Ok Google\".\n\n"<annotation id="url">"مزيد من المعلومات"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"يؤدي استخدام خيار \"توفير شحن البطارية\" إلى تفعيل \"المظهر الداكن\" وتقييد أو إيقاف النشاط في الخلفية وبعض التأثيرات المرئية والميزات الأخرى، مثلاً \"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"للمساعدة في خفض استخدام البيانات، تمنع ميزة \"توفير البيانات\" بعض التطبيقات من إرسال البيانات وتلقّيها في الخلفية. يمكن للتطبيقات المتاحة لديك الآن استخدام البيانات، ولكن لا يمكنها الإكثار من ذلك. وهذا يعني أن الصور مثلاً لا تظهر حتى تنقر عليها."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"هل تريد تفعيل توفير البيانات؟"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"تفعيل"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 5eac3d06f60d..7d86f6c7afa5 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"ম\'নাৰ্ক"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"কুৱাট্ৰো"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"ফুলস্কেপ"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"অজ্ঞাত প\'ৰ্ট্ৰেইট"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"অজ্ঞাত লেণ্ডস্কেইপ"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"বাতিল কৰা হ’ল"</string> @@ -1852,9 +1876,11 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"আপোনাৰ প্ৰশাসকে আপেডট কৰিছে"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"আপোনাৰ প্ৰশাসকে মচিছে"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ঠিক আছে"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"বেটাৰী সঞ্চয়কাৰীয়ে গাঢ় থীম অন কৰে আৰু নেপথ্যৰ কাৰ্যকলাপ, কিছুমান ভিজুৱেল ইফেক্ট আৰু “Hey Google”ৰ দৰে সুবিধাসমূহ অফ কৰে অথবা সীমাবদ্ধ কৰে\n\n"<annotation id="url">"অধিক জানক"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"বেটাৰী সঞ্চয়কাৰীয়ে গাঢ় থীম অন কৰে আৰু নেপথ্যৰ কাৰ্যকলাপ, কিছুমান ভিজুৱেল ইফেক্ট আৰু “Hey Google”ৰ দৰে সুবিধাসমূহ অফ কৰে অথবা সীমাবদ্ধ কৰে।"</string> - <string name="data_saver_description" msgid="4995164271550590517">"ডেটা ব্য়ৱহাৰ মাত্ৰা কম কৰিবৰ বাবে ডেটা সঞ্চয়কাৰীয়ে কিছুমান এপক নেপথ্য়ত ডেটা প্ৰেৰণ বা সংগ্ৰহ কৰাত বাধা প্ৰদান কৰে। আপুনি বৰ্তমান ব্য়ৱহাৰ কৰি থকা এটা এপে ডেটা ব্য়ৱহাৰ কৰিব পাৰে, কিন্তু সঘনাই এই কার্য কৰিব নোৱাৰিব পাৰে। ইয়াৰ অৰ্থ এইয়ে হ\'ব পাৰে যে, উদাহৰণস্বৰূপে, আপুনি নিটিপা পর্যন্ত প্ৰতিচ্ছবিসমূহ দেখুওৱা নহ’ব।"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> + <string name="data_saver_description" msgid="4995164271550590517">"ডেটা ব্য়ৱহাৰৰ হ্ৰাস কৰিবলৈ ডেটা সঞ্চয়কাৰীয়ে কিছুমান এপক নেপথ্য়ত ডেটা প্ৰেৰণ বা সংগ্ৰহ কৰাত বাধা প্ৰদান কৰে। আপুনি বৰ্তমান ব্য়ৱহাৰ কৰি থকা এটা এপে ডেটা এক্সেছ কৰিব পাৰে, কিন্তু সঘনাই এক্সেছ কৰিব নোৱাৰিব পাৰে। ইয়াৰ অৰ্থ উদাহৰণস্বৰূপে এয়া হ\'ব পাৰে যে, আপুনি নিটিপা পর্যন্ত প্ৰতিচ্ছবিসমূহ দেখুওৱা নহ’ব।"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ডেটা সঞ্চয়কাৰী অন কৰিবনে?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"অন কৰক"</string> <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273"> @@ -1929,8 +1955,7 @@ <string name="close_button_text" msgid="10603510034455258">"বন্ধ কৰক"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> <string name="call_notification_answer_action" msgid="5999246836247132937">"উত্তৰ দিয়ক"</string> - <!-- no translation found for call_notification_answer_video_action (2086030940195382249) --> - <skip /> + <string name="call_notification_answer_video_action" msgid="2086030940195382249">"ভিডিঅ’"</string> <string name="call_notification_decline_action" msgid="3700345945214000726">"প্ৰত্যাখ্যান কৰক"</string> <string name="call_notification_hang_up_action" msgid="9130720590159188131">"কল কাটি দিয়ক"</string> <string name="call_notification_incoming_text" msgid="6143109825406638201">"অন্তৰ্গামী কল"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 0a1c880be165..3dba1f9ebf8c 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Naməlum portret"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Naməlum mənzərə"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Ləğv edildi"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Admin tərəfindən yeniləndi"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Admin tərəfindən silindi"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Enerjiyə Qənaət funksiyası Qaranlıq temanı aktiv edir və arxa fondakı fəaliyyəti, bəzi vizual effektləri və “Hey Google” kimi digər funksiyaları məhdudlaşdırır və ya deaktiv edir\n\n"<annotation id="url">"Ətraflı məlumat"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Enerjiyə Qənaət funksiyası Qaranlıq temanı aktiv edir və arxa fondakı fəaliyyəti, bəzi vizual effektləri və “Hey Google” kimi digər funksiyaları məhdudlaşdırır və ya deaktiv edir."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Mobil interneti qənaətlə işlətmək məqsədilə Data Qanaəti bəzi tətbiqlərin fonda data göndərməsinin və qəbulunun qarşısını alır. Hazırda işlətdiyiniz tətbiq nisbətən az müntəzəmliklə data istifadə edə bilər. Örnək olaraq bu, o deməkdir ki, şəkil fayllarına toxunmadıqca onlar açılmayacaq."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Trafikə qənaət edilsin?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivləşdirin"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 5a7e890bfce1..86f60a830ba5 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -1795,6 +1795,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1831,6 +1842,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Nepoznata veličina, uspravno"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Nepoznata veličina, vodoravno"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Otkazano je"</string> @@ -1875,8 +1888,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je administrator"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je administrator"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Potvrdi"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizuelne efekte i funkcije, na primer, „Hej Google“.\n\n"<annotation id="url">"Saznajte više"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizuelne efekte i funkcije, na primer, „Hej Google“."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Da bi se smanjila potrošnja podataka, Ušteda podataka sprečava neke aplikacije da šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može da pristupa podacima, ali će to činiti ređe. Na primer, slike se neće prikazivati dok ih ne dodirnete."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Želite da uključite Uštedu podataka?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index c0915d2c12f6..1f6088abd32a 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -1817,6 +1817,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1853,6 +1875,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Невядомы (кніжная арыентацыя)"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Невядомы (альбомная арыентацыя)"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Скасавана"</string> @@ -1898,8 +1922,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Абноўлены вашым адміністратарам"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Выдалены вашым адміністратарам"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"У рэжыме эканоміі зараду ўключаецца цёмная тэма і абмяжоўваюцца ці выключаюцца дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты і функцыі, напрыклад \"Ok Google\"\n\n"<annotation id="url">"Даведацца больш"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"У рэжыме эканоміі зараду ўключаецца цёмная тэма і абмяжоўваюцца ці выключаюцца дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты і функцыі, напрыклад \"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"У рэжыме \"Эканомія трафіка\" фонавая перадача для некаторых праграмам адключана. Праграма, якую вы зараз выкарыстоўваеце, можа атрымліваць доступ да даных, але радзей, чым звычайна. Напрыклад, відарысы могуць не загружацца, пакуль вы не націсніце на іх."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Уключыць Эканомію трафіка?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Уключыць"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 5b8a81cba403..7f17c2c7e01e 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Неизвестен вертикален формат"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Неизвестен хоризонтален формат"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Анулирано"</string> @@ -1852,10 +1876,12 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Актуализирано от администратора ви"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Изтрито от администратора ви"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Режимът за запазване на батерията включва тъмната тема и ограничава или изключва активността на заден план, някои визуални ефекти и различни функции, като например „Ok Google“.\n\n"<annotation id="url">"Научете повече"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Режимът за запазване на батерията включва тъмната тема и ограничава или изключва активността на заден план, някои визуални ефекти и различни функции, като например „Ok Google“."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"С цел намаляване на преноса на данни функцията за икономия на данни не позволява на някои приложения да изпращат или получават данни на заден план. Понастоящем използвано от вас приложение може да използва данни, но по-рядко. Това например може да означава, че изображенията не се показват, докато не ги докоснете."</string> - <string name="data_saver_enable_title" msgid="7080620065745260137">"Ще вкл. ли „Икономия на данни“?"</string> + <string name="data_saver_enable_title" msgid="7080620065745260137">"Включване на „Икономия на данни“?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Включване"</string> <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273"> <item quantity="other">За %1$d минути (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 21cf14142625..04cdcf574abc 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"অজানা পোর্ট্রেট"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"অজানা ল্যান্ডস্কেপ"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"বাতিল করা হয়েছে"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"আপনার প্রশাসক আপডেট করেছেন"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"আপনার প্রশাসক মুছে দিয়েছেন"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ঠিক আছে"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ব্যাটারি সেভার ডার্ক থিম চালু করে এবং ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট এবং “Ok Google”-এর মতো ফিচার সীমিত করে বা বন্ধ করে দেয়\n\n"<annotation id="url">"আরও জানুন"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"ব্যাটারি সেভার ডার্ক থিম চালু করে এবং ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট এবং “Ok Google”-এর মতো ফিচার সীমিত করে বা বন্ধ করে দেয়।"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"ডেটার ব্যবহার কমাতে সহায়তা করার জন্য, ডেটা সেভার ব্যাকগ্রাউন্ডে কিছু অ্যাপ্লিকেশনকে ডেটা পাঠাতে বা গ্রহণ করতে বাধা দেয়৷ আপনি বর্তমানে এমন একটি অ্যাপ্লিকেশন ব্যবহার করছেন যেটি ডেটা অ্যাক্সেস করতে পারে, তবে সেটি কমই করে৷ এর ফলে যা হতে পারে, উদাহরণস্বরূপ, আপনি ছবির উপর ট্যাপ না করা পর্যন্ত সেগুলি দেখানো হবে না৷"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ডেটা সেভার চালু করবেন?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"চালু করুন"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index b0a9726666cc..dd00bd0fb453 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -1795,6 +1795,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1831,6 +1842,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Neodređeni uspravni format"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Neodređeni vodoravni format"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Otkazano"</string> @@ -1875,8 +1888,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je vaš administrator"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je vaš administrator"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Uredu"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnosti u pozadini, određene vizuelne efekte i funkcije kao što je \"Ok Google\"\n\n"<annotation id="url">"Saznajte više"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnosti u pozadini, određene vizuelne efekte i funkcije kao što je \"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Radi smanjenja prijenosa podataka, Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupiti podacima, ali će to činiti rjeđe. Naprimjer, to može značiti da se slike ne prikazuju dok ih ne dodirnete."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Uključiti Uštedu podataka?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index f098fbe1238e..ecdc94f2a65a 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quart"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foli"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Vertical desconegut"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Horitzontal desconegut"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Cancel·lada"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualitzat per l\'administrador"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Suprimit per l\'administrador"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"D\'acord"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals i funcions com \"Hey Google\".\n\n"<annotation id="url">"Més informació"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals i funcions com \"Hey Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Per reduir l\'ús de dades, la funció Economitzador de dades evita que determinades aplicacions enviïn o rebin dades en segon pla. L\'aplicació que estiguis fent servir podrà accedir a les dades, però menys sovint. Això vol dir, per exemple, que les imatges no es mostraran fins que no les toquis."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Activar l\'Economitzador de dades?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activa"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index de6debb5d1c9..d0494bd9f378 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -1817,6 +1817,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1853,6 +1864,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Neznámý formát na výšku"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Neznámý formát na šířku"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Zrušeno"</string> @@ -1898,8 +1911,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Aktualizováno administrátorem"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Smazáno administrátorem"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Spořič baterie zapne tmavý motiv a omezí nebo vypne aktivitu na pozadí, některé vizuální efekty a funkce jako „Ok Google“\n\n"<annotation id="url">"Další informace"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Spořič baterie zapne tmavý motiv a omezí nebo vypne aktivitu na pozadí, některé vizuální efekty a funkce jako „Ok Google“."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Z důvodu snížení využití dat brání spořič dat některým aplikacím v odesílání nebo příjmu dat na pozadí. Aplikace, kterou právě používáte, data přenášet může, ale může tak činit méně často. V důsledku toho se například obrázky nemusejí zobrazit, dokud na ně neklepnete."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Chcete zapnout Spořič dat?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Zapnout"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 557484d20f0c..39e061d0c5cb 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Ukendt stående format"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Ukendt liggende format"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Annulleret"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Opdateret af din administrator"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Slettet af din administrator"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Batterisparefunktionen aktiverer Mørkt tema og deaktiverer eller begrænser aktivitet i baggrunden, visse visuelle effekter og funktioner som f.eks. \"Hey Google\"\n\n"<annotation id="url">"Få flere oplysninger"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Batterisparefunktionen aktiverer Mørkt tema og deaktiverer eller begrænser aktivitet i baggrunden, visse visuelle effekter og funktioner som f.eks. \"Hey Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Datasparefunktionen forhindrer nogle apps i at sende eller modtage data i baggrunden for at reducere dataforbruget. En app, der er i brug, kan få adgang til data, men gør det måske ikke så ofte. Dette kan f.eks. betyde, at billeder ikke vises, før du trykker på dem."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Vil du aktivere Datasparefunktion?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivér"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 04b5747de562..a92de5aa986e 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Unbekannt – Hochformat"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Unbekannt – Querformat"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Abgebrochen"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Der Energiesparmodus aktiviert das dunkle Design und schränkt Hintergrundaktivitäten, einige optische Effekte und Funktionen wie „Hey Google“ ein oder schaltet diese ab\n\n"<annotation id="url">"Weitere Informationen"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Der Energiesparmodus aktiviert das dunkle Design und schränkt Hintergrundaktivitäten, einige optische Effekte und Funktionen wie „Hey Google“ ein oder schaltet diese ab."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert zum einen, dass manche Apps im Hintergrund Daten senden oder empfangen, sodass weniger Daten verbraucht werden. Zum anderen werden die Datenzugriffe der gerade aktiven App eingeschränkt, was z. B. dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Datensparmodus aktivieren?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivieren"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 417b8f2b2d0d..3e0dda79841d 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Άγνωστος κατακόρυφος προσανατολισμός"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Άγνωστος οριζόντιος προσανατολισμός"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Ακυρώθηκε"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ενημερώθηκε από τον διαχειριστή σας"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Διαγράφηκε από τον διαχειριστή σας"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Η Εξοικονόμηση μπαταρίας ενεργοποιεί το Σκούρο θέμα και περιορίζει ή απενεργοποιεί τη δραστηριότητα στο παρασκήνιο, ορισμένα οπτικά εφέ και λειτουργίες όπως την εντολή \"Ok Google\"\n\n"<annotation id="url">"Μάθετε περισσότερα"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Η Εξοικονόμηση μπαταρίας ενεργοποιεί το Σκούρο θέμα και περιορίζει ή απενεργοποιεί τη δραστηριότητα στο παρασκήνιο, ορισμένα οπτικά εφέ και λειτουργίες όπως την εντολή \"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Προκειμένου να μειωθεί η χρήση δεδομένων, η Εξοικονόμηση δεδομένων αποτρέπει την αποστολή ή λήψη δεδομένων από ορισμένες εφαρμογές στο παρασκήνιο. Μια εφαρμογή που χρησιμοποιείτε αυτήν τη στιγμή μπορεί να χρησιμοποιήσει δεδομένα αλλά με μικρότερη συχνότητα. Για παράδειγμα, οι εικόνες μπορεί να μην εμφανίζονται μέχρι να τις πατήσετε."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Ενεργ.Εξοικονόμησης δεδομένων;"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Ενεργοποίηση"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index f8ca0b1890d9..bb7b1fac0d21 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Unknown portrait"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Unknown landscape"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Cancelled"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'\n\n"<annotation id="url">"Learn more"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you\'re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index dc993feb24f2..4af88dbad44c 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Unknown portrait"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Unknown landscape"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Cancelled"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'\n\n"<annotation id="url">"Learn more"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you\'re currently using can access data, but may do so less frequently. This may mean, for example, that images don\'t display until you tap them."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 5b80a905c6a9..f322d0583c28 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Unknown portrait"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Unknown landscape"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Cancelled"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'\n\n"<annotation id="url">"Learn more"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app that you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index dad96b466ce9..1eb402da80dd 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Unknown portrait"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Unknown landscape"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Cancelled"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'\n\n"<annotation id="url">"Learn more"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app that you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 2cdf5ba6189d..65b00c812826 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Unknown portrait"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Unknown landscape"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Cancelled"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, and features like “Hey Google”\n\n"<annotation id="url">"Learn more"</annotation>""</string> - <string name="battery_saver_description" msgid="7695751399533397741">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, and features like “Hey Google”."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 6a1cddc88da6..b3612404cf86 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -266,7 +266,7 @@ <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Modo silencioso"</string> <string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"El sonido está Desactivado"</string> <string name="global_action_silent_mode_off_status" msgid="6608006545950920042">"El sonido está Activado"</string> - <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"Modo avión"</string> + <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"Modo de avión"</string> <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"El modo avión está Activado"</string> <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"El modo avión está Desactivado"</string> <string name="global_action_settings" msgid="4671878836947494217">"Configuración"</string> @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarca"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Cuartilla"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Folio"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Cualquier tamaño vertical"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Cualquier tamaño horizontal"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Cancelada"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Tu administrador actualizó este paquete"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Tu administrador borró este paquete"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"El Ahorro de batería activa el Tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales y otras funciones, como \"Hey Google\"\n\n"<annotation id="url">"Más información"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"El Ahorro de batería activa el Tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales y otras funciones, como \"Hey Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Para reducir el uso de datos, el modo Ahorro de datos evita que algunas apps envíen y reciban datos en segundo plano. La app que estés usando podrá acceder a los datos, pero con menor frecuencia. De esta forma, por ejemplo, las imágenes no se mostrarán hasta que las presiones."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"¿Deseas activar Ahorro de datos?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 7519b5a9a591..13ed8ffbcafe 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Cualquier tamaño vertical"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Cualquier tamaño horizontal"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Cancelado"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizado por el administrador"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado por el administrador"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales y funciones como \"Hey Google\"\n\n"<annotation id="url">"Más información"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales y funciones como \"Hey Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Ahorro de datos evita que algunas aplicaciones envíen o reciban datos en segundo plano, lo que puede reducir el uso de datos. Una aplicación que estés usando de forma activa puede acceder a los datos, aunque con menos frecuencia. Esto significa que es posible que, por ejemplo, algunas imágenes no se muestren hasta que las toques."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"¿Activar Ahorro de datos?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 1e58aedc5c45..9d63c3d14af0 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Tundmatu vertikaalpaigutuses"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Tundmatu horisontaalpaigutuses"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Tühistatud"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Administraator on seda värskendanud"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administraator on selle kustutanud"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Akusäästja lülitab sisse tumeda teema ning piirab taustategevusi, teatud visuaalseid efekte ja funktsioone, nagu „Ok Google“ (või lülitab need välja)\n\n"<annotation id="url">"Lisateave"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Akusäästja lülitab sisse tumeda teema ning piirab taustategevusi, teatud visuaalseid efekte ja funktsioone, nagu „Ok Google“ (või lülitab need välja)."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Andmekasutuse vähendamiseks keelab andmemahu säästja mõne rakenduse puhul andmete taustal saatmise ja vastuvõtmise. Rakendus, mida praegu kasutate, pääseb andmesidele juurde, kuid võib seda teha väiksema sagedusega. Seetõttu võidakse näiteks pildid kuvada alles siis, kui neid puudutate."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Lülitada andmemahu säästja sisse?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Lülita sisse"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index ce1058478fbc..a8a5d1f514a4 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch (AEB)"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto (AEB)"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap (AEB)"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K (Txina)"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K (Txina)"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1 (Txina)"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu (Japonia)"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2 (Japonia)"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4 (Japonia)"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Bertikal ezezaguna"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Horizontal ezezaguna"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Bertan behera utzi da"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Administratzaileak eguneratu du"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administratzaileak ezabatu du"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Ados"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Bateria-aurrezleak gai iluna aktibatzen du, eta murriztu edo desaktibatu egiten ditu atzeko planoko jarduerak, zenbait efektu bisual eta beste eginbide batzuk, hala nola \"Ok Google\".\n\n"<annotation id="url">"Lortu informazio gehiago"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Bateria-aurrezleak gai iluna aktibatzen du, eta murriztu edo desaktibatu egiten ditu atzeko planoko jarduerak, zenbait efektu bisual eta beste eginbide batzuk, hala nola \"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Datuen erabilera murrizteko, atzeko planoan datuak bidaltzea eta jasotzea galarazten die datu-aurrezleak aplikazio batzuei. Une honetan erabiltzen ari zaren aplikazio batek datuak atzitu ahal izango ditu, baina baliteke maiztasun txikiagoarekin atzitzea. Horrela, adibidez, baliteke irudiak ez erakustea haiek sakatu arte."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Datu-aurrezlea aktibatu nahi duzu?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktibatu"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 280c71f9bbf4..dc0b0155e77a 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"عمودی نامشخص"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"افقی نامشخص"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"لغو شد"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"توسط سرپرست سیستم بهروزرسانی شد"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"توسط سرپرست سیستم حذف شد"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"تأیید"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"«بهینهسازی باتری» «طرح زمینه تیره» را روشن میکند و فعالیت پسزمینه، برخی از جلوههای بصری، و ویژگیهایی مثل «Ok Google» را محدود یا خاموش میکند.\n\n"<annotation id="url">"بیشتر بدانید"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"«بهینهسازی باتری» «طرح زمینه تیره» را روشن میکند و فعالیت پسزمینه، برخی از جلوههای بصری، و ویژگیهایی مثل «Ok Google» را محدود یا خاموش میکند."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"برای کمک به کاهش مصرف داده، «صرفهجویی داده» از ارسال و دریافت داده در پسزمینه در بعضی برنامهها جلوگیری میکند. برنامهای که درحالحاضر استفاده میکنید میتواند به دادهها دسترسی داشته باشد اما دفعات دسترسی آن محدود است. این میتواند به این معنی باشد که، برای مثال، تصاویر تازمانیکه روی آنها ضربه نزنید نشان داده نمیشوند."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"«صرفهجویی داده» روشن شود؟"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"روشن کردن"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 2538e86fd222..310ce66dc7b3 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch (184 mm x 267 mm)"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto (203 mm x 254 mm)"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap (203 mm x 330 mm)"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K (270 mm x 390 mm)"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K (195 mm x 270 mm)"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1 (102 mm x 165 mm)"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu (240 mm x 322,1 mm)"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2 (240 mm x 332 mm)"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4 (105 mm x 235 mm)"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Tuntematon pystykoko"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Tuntematon vaakakoko"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Peruutettu"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Järjestelmänvalvoja päivitti tämän."</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Järjestelmänvalvoja poisti tämän."</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Virransäästö laittaa tumman teeman päälle ja rajoittaa tai laittaa pois päältä taustatoimintoja, joitakin visuaalisia tehosteita ja muita ominaisuuksia (esim. Ok Google).\n\n"<annotation id="url">"Lue lisää"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Virransäästö laittaa tumman teeman päälle ja rajoittaa tai laittaa pois päältä taustatoimintoja, joitakin visuaalisia tehosteita ja muita ominaisuuksia (esim. Ok Google)."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Data Saver estää joitakin sovelluksia lähettämästä tai vastaanottamasta tietoja taustalla, jotta datan käyttöä voidaan vähentää. Käytössäsi oleva sovellus voi yhä käyttää dataa, mutta se saattaa tehdä niin tavallista harvemmin. Tämä voi tarkoittaa esimerkiksi sitä, että kuva ladataan vasta, kun kosketat sitä."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Otetaanko Data Saver käyttöön?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Ota käyttöön"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index a5b12508a223..cb5e61645999 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarque"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"In-quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Taille inconnue au format portrait"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Taille inconnue au format paysage"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Annulé"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Mise à jour par votre administrateur"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Supprimé par votre administrateur"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"La fonctionnalité Économiseur de pile active le mode sombre et limite ou désactive l\'activité en arrière-plan, certains effets visuels et certaines fonctionnalités, comme « Ok Google »\n\n"<annotation id="url">"En savoir plus"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"La fonctionnalité Économiseur de pile active le mode sombre et limite ou désactive l\'activité en arrière-plan, certains effets visuels et certaines fonctionnalités, comme « Ok Google »."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Pour aider à diminuer l\'utilisation des données, la fonctionnalité Économiseur de données empêche certaines applications d\'envoyer ou de recevoir des données en arrière-plan. Une application que vous utilisez actuellement peut accéder à des données, mais peut le faire moins souvent. Cela peut signifier, par exemple, que les images ne s\'affichent pas jusqu\'à ce que vous les touchiez."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'économiseur de données?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 86cc13a6d91a..d68d00d29eb2 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Taille inconnue au format portrait"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Taille inconnue au format paysage"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Tâche annulée."</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Mis à jour par votre administrateur"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Supprimé par votre administrateur"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"L\'économiseur de batterie active le thème sombre et limite ou désactive les activités en arrière-plan, certains effets visuels et d\'autres fonctionnalités comme \"Hey Google\"\n\n"<annotation id="url">"En savoir plus"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"L\'économiseur de batterie active le thème sombre et limite ou désactive les activités en arrière-plan, certains effets visuels et d\'autres fonctionnalités comme \"Hey Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Pour réduire la consommation des données, l\'Économiseur de données empêche certaines applis d\'envoyer ou de recevoir des données en arrière-plan. Les applis que vous utiliserez pourront toujours accéder aux données, mais le feront moins fréquemment. Par exemple, les images pourront ne pas s\'afficher tant que vous n\'aurez pas appuyé pas dessus."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'économiseur de données ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 25fb55c93304..9c089228f719 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Tamaño folio"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Retrato descoñecido"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Paisaxe descoñecida"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Cancelada"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizado polo teu administrador"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado polo teu administrador"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Coa función Aforro de batería actívase o tema escuro e restrínxense ou desactívanse a actividade en segundo plano, algúns efectos visuais e outras funcións, como “Hey Google”\n\n"<annotation id="url">"Máis información"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Coa función Aforro de batería actívase o tema escuro e restrínxense ou desactívanse a actividade en segundo plano, algúns efectos visuais e outras funcións, como “Hey Google”."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Para contribuír a reducir o uso de datos, o aforro de datos impide que algunhas aplicacións envíen ou reciban datos en segundo plano. Cando esteas utilizando unha aplicación, esta poderá acceder aos datos, pero é posible que o faga con menos frecuencia. Por exemplo, poida que as imaxes non se mostren ata que as toques."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Queres activar o aforro de datos?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index f8de931f84ef..843fe4c40b63 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"મોનાર્ક"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"ક્વાર્ટો"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"ફૂલસ્કેપ"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"રૉક 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"કહુ"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"કાકુ2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"અજાણ્યું પોર્ટ્રેટ"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"અજાણ્યું લેન્ડસ્કેપ"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"રદ થઈ"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"તમારા વ્યવસ્થાપક દ્વારા અપડેટ કરવામાં આવેલ છે"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"તમારા વ્યવસ્થાપક દ્વારા કાઢી નાખવામાં આવેલ છે"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ઓકે"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"બૅટરી સેવર ઘેરી થીમની સુવિધા ચાલુ કરે છે અને બૅકગ્રાઉન્ડમાં થતી પ્રવૃત્તિ, કેટલીક વિઝ્યુઅલ ઇફેક્ટ અને “Ok Google” જેવી સુવિધાઓને મર્યાદિત કે બંધ કરે છે\n\n"<annotation id="url">"વધુ જાણો"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"બૅટરી સેવર ઘેરી થીમની સુવિધા ચાલુ કરે છે અને બૅકગ્રાઉન્ડમાં થતી પ્રવૃત્તિ, કેટલીક વિઝ્યુઅલ ઇફેક્ટ અને “Ok Google” જેવી સુવિધાઓને મર્યાદિત કે બંધ કરે છે."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"ડેટા વપરાશને ઘટાડવામાં સહાય માટે, ડેટા સેવર કેટલીક ઍપને બૅકગ્રાઉન્ડમાં ડેટા મોકલવા અથવા પ્રાપ્ત કરવાથી અટકાવે છે. તમે હાલમાં ઉપયોગ કરી રહ્યાં છો તે ઍપ ડેટાને ઍક્સેસ કરી શકે છે, પરંતુ તે આ ક્યારેક જ કરી શકે છે. આનો અર્થ એ હોઈ શકે છે, ઉદાહરણ તરીકે, છબીઓ ત્યાં સુધી પ્રદર્શિત થશે નહીં જ્યાં સુધી તમે તેને ટૅપ નહીં કરો."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ડેટા સેવર ચાલુ કરીએ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ચાલુ કરો"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 4ab4a7a9bafb..1419396aa9e0 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"मोनार्क"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"क्वार्टो"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"फ़ूल्ज़कैप"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"काहु"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"काकु2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"यौ4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"अज्ञात पोर्ट्रेट"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"अज्ञात लैंडस्केप"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"रद्द कर दी गई"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"आपके व्यवस्थापक ने अपडेट किया है"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"आपके व्यवस्थापक ने हटा दिया है"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ठीक है"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"बैटरी सेवर गहरे रंग वाली थीम को चालू कर देता है. साथ ही, यह बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और \"Hey Google\" जैसी दूसरी सुविधाएं इस्तेमाल करने से डिवाइस को रोकता है या इन्हें बंद कर देता है\n\n"<annotation id="url">"ज़्यादा जानें"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"बैटरी सेवर गहरे रंग वाली थीम को चालू कर देता है. साथ ही, यह बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और \"Hey Google\" जैसी दूसरी सुविधाएं इस्तेमाल करने से डिवाइस को रोकता है या इन्हें बंद कर देता है."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"डेटा खर्च को कम करने के लिए, डेटा बचाने की सेटिंग कुछ ऐप्लिकेशन को बैकग्राउंड में डेटा भेजने या डेटा पाने से रोकती है. फ़िलहाल, आप जिस ऐप्लिकेशन का इस्तेमाल कर रहे हैं वह डेटा ऐक्सेस कर सकता है, लेकिन ऐसा कभी-कभी ही हो पाएगा. उदाहरण के लिए, इमेज तब तक दिखाई नहीं देंगी जब तक कि आप उन पर टैप नहीं करते."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा बचाने की सेटिंग चालू करें?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"चालू करें"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 28fb4003004e..c88fcbdf7302 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -1795,6 +1795,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1831,6 +1842,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Nepoznati portret"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Nepoznati pejzaž"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Otkazano"</string> @@ -1875,8 +1888,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao administrator"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao administrator"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"U redu"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Štednja baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizualne efekte i značajke kao što je Hey Google\n\n"<annotation id="url">"Saznajte više"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Štednja baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizualne efekte i značajke kao što je Hey Google."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Da bi se smanjio podatkovni promet, značajka Štednja podatkovnog prometa onemogućuje nekim aplikacijama slanje ili primanje podataka u pozadini. Aplikacija koju trenutačno upotrebljavate može pristupiti podacima, no možda će to činiti rjeđe. To može značiti da se, na primjer, slike neće prikazivati dok ih ne dodirnete."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Uključiti Štednju podatkovnog prometa?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 09e56d804ac8..dcfcfb932550 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"„Monarch” méret"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"„Quarto” méret"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"„Foolscap” méret"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"„ROC 8K” méret"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"„ROC 16K” méret"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"„PRC 1” méret"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"„Kahu” méret"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"„Kaku2” méret"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"„You4” méret"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Ismeretlen álló"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Ismeretlen fekvő"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Törölve"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"A rendszergazda által frissítve"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"A rendszergazda által törölve"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Az Akkumulátorkímélő mód bekapcsolja a Sötét témát, és korlátozza vagy kikapcsolja a háttérbeli tevékenységeket, bizonyos vizuális effekteket és olyan funkciókat, mint az „Ok Google”.\n\n"<annotation id="url">"További információ"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Az Akkumulátorkímélő mód bekapcsolja a Sötét témát, és korlátozza vagy kikapcsolja a háttérbeli tevékenységeket, bizonyos vizuális effekteket és olyan funkciókat, mint az „Ok Google”."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Az adatforgalom csökkentése érdekében az Adatforgalom-csökkentő megakadályozza, hogy egyes alkalmazások adatokat küldjenek vagy fogadjanak a háttérben. Az Ön által jelenleg használt alkalmazások hozzáférhetnek az adatokhoz, de csak ritkábban. Ez például azt jelentheti, hogy a képek csak rákoppintás után jelennek meg."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Bekapcsolja az Adatforgalom-csökkentőt?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Bekapcsolás"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index d4d18c0957f0..67560f70d7b4 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Անհայտ դիմանկար"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Անհայտ բնապատկեր"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Չեղարկված է"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Թարմացվել է ձեր ադմինիստրատորի կողմից"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Ջնջվել է ձեր ադմինիստրատորի կողմից"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Եղավ"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Մարտկոցի տնտեսումը միացնում է մուգ թեման և սահմանափակում կամ անջատում է ֆոնային գործընթացները, որոշ վիզուալ էֆեկտներ և այլ գործառույթներ, օրինակ՝ «Ok Google» հրահանգի ճանաչումը։\n\n"<annotation id="url">"Իմանալ ավելին"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Մարտկոցի տնտեսումը միացնում է մուգ թեման և սահմանափակում կամ անջատում է ֆոնային գործընթացները, որոշ վիզուալ էֆեկտներ և այլ գործառույթներ, օրինակ՝ «Ok Google» հրահանգի ճանաչումը։"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Թրաֆիկի տնտեսման ռեժիմում որոշ հավելվածների համար տվյալների ֆոնային փոխանցումն անջատված է։ Հավելվածը, որն օգտագործում եք, կարող է տվյալներ փոխանցել և ստանալ, սակայն ոչ այնքան հաճախ: Օրինակ՝ պատկերները կցուցադրվեն միայն դրանց վրա սեղմելուց հետո։"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Միացնե՞լ թրաֆիկի տնտեսումը"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Միացնել"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 5491214a8446..d85a4ba36da6 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Kuarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Folio"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Potret tidak diketahui"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Lanskap tidak diketahui"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Dibatalkan"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Diupdate oleh admin Anda"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Dihapus oleh admin Anda"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Oke"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Penghemat Baterai mengaktifkan Tema gelap dan membatasi atau menonaktifkan aktivitas di latar belakang, beberapa efek visual, dan fitur seperti “Ok Google”\n\n"<annotation id="url">"Pelajari lebih lanjut"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Penghemat Baterai mengaktifkan Tema gelap dan membatasi atau menonaktifkan aktivitas di latar belakang, beberapa efek visual, dan fitur seperti “Ok Google”."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Untuk membantu mengurangi penggunaan data, Penghemat Data mencegah beberapa aplikasi mengirim atau menerima data di latar belakang. Aplikasi yang sedang digunakan dapat mengakses data, tetapi frekuensinya agak lebih jarang. Misalnya saja, gambar hanya akan ditampilkan setelah diketuk."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Aktifkan Penghemat Data?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktifkan"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 4ed6fcc4d078..5ae655405d5a 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Óþekkt skammsnið"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Óþekkt langsnið"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Hætt við"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Kerfisstjóri uppfærði"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Kerfisstjóri eyddi"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Í lagi"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Rafhlöðusparnaður kveikir á dökku þema og takmarkar eða slekkur á bakgrunnsvirkni, tilteknum myndbrellum og eiginleikum eins og „Ok Google“.\n\n"<annotation id="url">"Nánar"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Rafhlöðusparnaður kveikir á dökku þema og takmarkar eða slekkur á bakgrunnsvirkni, tilteknum myndbrellum og eiginleikum eins og „Ok Google“."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Gagnasparnaður getur hjálpað til við að draga úr gagnanotkun með því að hindra forrit í að senda eða sækja gögn í bakgrunni. Forrit sem er í notkun getur náð í gögn, en gerir það kannski sjaldnar. Niðurstaðan getur verið að myndir eru ekki birtar fyrr en þú ýtir á þær, svo dæmi sé tekið."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Kveikja á gagnasparnaði?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Kveikja"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 23acc8bc9ce1..0a3e014e433f 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Carta protocollo"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Verticale sconosciuto"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Orizzontale sconosciuto"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Annullato"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Aggiornato dall\'amministratore"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminato dall\'amministratore"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"L\'opzione Risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, alcuni effetti visivi e funzionalità come \"Hey Google\"\n\n"<annotation id="url">"Scopri di più"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"L\'opzione Risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, alcuni effetti visivi e funzionalità come \"Hey Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Per contribuire a ridurre l\'utilizzo dei dati, la funzione Risparmio dati impedisce ad alcune app di inviare o ricevere dati in background. Un\'app in uso può accedere ai dati, ma potrebbe farlo con meno frequenza. Esempio: le immagini non vengono visualizzate finché non le tocchi."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Attivare Risparmio dati?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Attiva"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 5269501ec7e2..6bfb7ebf1db9 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1817,6 +1817,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1853,6 +1875,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"הדפסה לאורך בגודל לא ידוע"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"הדפסה לרוחב בגודל לא ידוע"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"בוטלה"</string> @@ -1898,8 +1922,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"עודכנה על ידי מנהל המערכת"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"נמחקה על ידי מנהל המערכת"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"אישור"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"התכונה \'חיסכון בסוללה\' מפעילה עיצוב כהה ומגבילה או מכבה פעילות ברקע, חלק מהאפקטים החזותיים ותכונות כמו \"Hey Google\"\n\n"<annotation id="url">"מידע נוסף"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"התכונה \'חיסכון בסוללה\' מפעילה עיצוב כהה ומגבילה או מכבה פעילות ברקע, חלק מהאפקטים החזותיים ותכונות כמו \"Hey Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"כדי לסייע בהפחתת השימוש בנתונים, חוסך הנתונים (Data Saver) מונע מאפליקציות מסוימות לשלוח או לקבל נתונים ברקע. אפליקציות שבהן נעשה שימוש כרגע יכולות לגשת לנתונים, אבל בתדירות נמוכה יותר. המשמעות היא, למשל, שתמונות יוצגו רק לאחר שמקישים עליהן."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"להפעיל את חוסך הנתונים?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"הפעלה"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 37834115176e..bed82eb1042a 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"モナーク"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"クォート"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"フールスキャップ"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"角2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"洋4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"縦向き不明"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"横向き不明"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"キャンセルされました"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"管理者により更新されています"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"管理者により削除されています"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"バッテリー セーバーを有効にすると、ダークテーマが ON になり、バックグラウンド アクティビティ、一部の視覚効果や、「OK Google」などの機能が制限されるか OFF になります\n\n"<annotation id="url">"詳細"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"バッテリー セーバーを有効にすると、ダークテーマが ON になり、バックグラウンド アクティビティ、一部の視覚効果や、「OK Google」などの機能が制限されるか OFF になります。"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"データセーバーは、一部のアプリによるバックグラウンドでのデータ送受信を停止することでデータ使用量を抑制します。使用中のアプリからデータを送受信することはできますが、その頻度は低くなる場合があります。この影響として、たとえば画像はタップしないと表示されないようになります。"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"データセーバーを ON にしますか?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ON にする"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index ba7beac095a4..a7d11c251f17 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"უცნობი პორტრეტი"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"უცნობი ლანდშაფტი"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"გაუქმებული"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"განახლებულია თქვენი ადმინისტრატორის მიერ"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"წაიშალა თქვენი ადმინისტრატორის მიერ"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"კარგი"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ბატარეის დამზოგი ჩართავს მუქ თემას და შეზღუდავს ან გამორთავს ფონურ აქტივობას, ზოგიერთ ვიზუალურ ეფექტს და ისეთ ფუნქციებს, როგორიცაა „Ok Google“\n\n"<annotation id="url">"შეიტყვეთ მეტი"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"ბატარეის დამზოგი ჩართავს მუქ თემას და შეზღუდავს ან გამორთავს ფონურ აქტივობას, ზოგიერთ ვიზუალურ ეფექტს და ისეთ ფუნქციებს, როგორიცაა „Ok Google“."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"მობილური ინტერნეტის მოხმარების შემცირების მიზნით, მონაცემთა დამზოგველი ზოგიერთ აპს ფონურ რეჟიმში მონაცემთა გაგზავნასა და მიღებას შეუზღუდავს. თქვენ მიერ ამჟამად გამოყენებული აპი მაინც შეძლებს მობილურ ინტერნეტზე წვდომას, თუმცა ამას ნაკლები სიხშირით განახორციელებს. ეს ნიშნავს, რომ, მაგალითად, სურათები არ გამოჩნდება მანამ, სანამ მათ საგანგებოდ არ შეეხებით."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ჩაირთოს მონაცემთა დამზოგველი?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ჩართვა"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 4e10167b254b..402256e5e51e 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Монарх"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Кварто"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Белгісіз портреттік"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Белгісіз ландшафт"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Тоқтатылды"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Әкімші жаңартқан"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Әкімші жойған"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Жарайды"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Батареяны үнемдеу режимі қараңғы тақырыпты іске қосады және фондық әрекеттерге, кейбір визуалдық әсерлерге, \"Ok Google\" сияқты функцияларға шектеу қояды немесе оларды өшіреді.\n\n"<annotation id="url">"Толығырақ"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Батареяны үнемдеу режимі қараңғы тақырыпты іске қосады және фондық әрекеттерге, кейбір визуалдық әсерлерге, \"Ok Google\" сияқты функцияларға шектеу қояды немесе оларды өшіреді."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Дерек шығынын азайту үшін Трафикті үнемдеу режимінде кейбір қолданбаларға деректі фондық режимде жіберуге және алуға тыйым салынады. Ашық тұрған қолданба деректі шектеулі шамада пайдаланады (мысалы, кескіндер оларды түрткенге дейін көрсетілмейді)."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Трафикті үнемдеу режимі қосылсын ба?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Қосу"</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 81dc8919f469..47946ddfaae1 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"មិនស្គាល់បញ្ឈរ"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"មិនស្គាល់ទេសភាព"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"បានបោះបង់"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"ធ្វើបច្ចុប្បន្នភាពដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"លុបដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"យល់ព្រម"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"មុខងារសន្សំថ្មបើករចនាប័ទ្មងងឹត និងបិទឬដាក់កំហិតលើសកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពលរូបភាពមួយចំនួន និងមុខងារដូចជា “Ok Google” ជាដើម\n\n"<annotation id="url">"ស្វែងយល់បន្ថែម"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"មុខងារសន្សំថ្មបើករចនាប័ទ្មងងឹត និងបិទឬដាក់កំហិតលើសកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពលរូបភាពមួយចំនួន និងមុខងារដូចជា “Ok Google” ជាដើម។"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"ដើម្បីជួយកាត់បន្ថយការប្រើប្រាស់ទិន្នន័យ កម្មវិធីសន្សំសំចៃទិន្នន័យរារាំងកម្មវិធីមួយចំនួនមិនឲ្យបញ្ជូន ឬទទួលទិន្នន័យនៅផ្ទៃខាងក្រោយទេ។ កម្មវិធីដែលអ្នកកំពុងប្រើនាពេលបច្ចុប្បន្នអាចចូលប្រើប្រាស់ទិន្នន័យបាន ប៉ុន្តែអាចនឹងមិនញឹកញាប់ដូចមុនទេ។ ឧទាហរណ៍ រូបភាពមិនបង្ហាញទេ លុះត្រាតែអ្នកប៉ះរូបភាពទាំងនោះ។"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"បើកកម្មវិធីសន្សំសំចៃទិន្នន័យ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"បើក"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 02448bd9cb29..c4606d65e9e2 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"ಮೊನಾರ್ಕ್"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"ಕ್ವಾರ್ಟೊ"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"ಅಪರಿಚಿತ ಪೋರ್ಟ್ರೇಟ್"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"ಅಪರಿಚಿತ ಲ್ಯಾಂಡ್ಸ್ಕೇಪ್"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"ರದ್ದುಮಾಡಲಾಗಿದೆ"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರಿಂದ ಅಪ್ಡೇಟ್ ಮಾಡಲ್ಪಟ್ಟಿದೆ"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ಅಳಿಸಿದ್ದಾರೆ"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ಸರಿ"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಡಾರ್ಕ್ ಥೀಮ್ ಅನ್ನು ಆನ್ ಮಾಡುತ್ತದೆ ಮತ್ತು ಹಿನ್ನೆಲೆ ಚಟುವಟಿಕೆ, ಕೆಲವು ವಿಷುವಲ್ ಎಫೆಕ್ಟ್ಗಳು ಮತ್ತು “Ok Google” ನಂತಹ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಮಿತಿಗೊಳಿಸುತ್ತದೆ ಅಥವಾ ಆಫ್ ಮಾಡುತ್ತದೆ.\n\n"<annotation id="url">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಡಾರ್ಕ್ ಥೀಮ್ ಅನ್ನು ಆನ್ ಮಾಡುತ್ತದೆ ಮತ್ತು ಹಿನ್ನೆಲೆ ಚಟುವಟಿಕೆ, ಕೆಲವು ವಿಷುವಲ್ ಎಫೆಕ್ಟ್ಗಳು ಮತ್ತು “Ok Google” ನಂತಹ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಮಿತಿಗೊಳಿಸುತ್ತದೆ ಅಥವಾ ಆಫ್ ಮಾಡುತ್ತದೆ."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"ಡೇಟಾ ಬಳಕೆ ಕಡಿಮೆ ಮಾಡುವ ನಿಟ್ಟಿನಲ್ಲಿ, ಡೇಟಾ ಸೇವರ್ ಕೆಲವು ಅಪ್ಲಿಕೇಶನ್ಗಳು ಹಿನ್ನೆಲೆಯಲ್ಲಿ ಡೇಟಾ ಕಳುಹಿಸುವುದನ್ನು ಅಥವಾ ಸ್ವೀಕರಿಸುವುದನ್ನು ತಡೆಯುತ್ತದೆ. ನೀವು ಪ್ರಸ್ತುತ ಬಳಸುತ್ತಿರುವ ಅಪ್ಲಿಕೇಶನ್ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಬಹುದು ಆದರೆ ಪದೇ ಪದೇ ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. ಇದರರ್ಥ, ಉದಾಹರಣೆಗೆ, ನೀವು ಅವುಗಳನ್ನು ಟ್ಯಾಪ್ ಮಾಡುವವರೆಗೆ ಆ ಚಿತ್ರಗಳು ಕಾಣಿಸಿಕೊಳ್ಳುವುದಿಲ್ಲ."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ಡೇಟಾ ಸೇವರ್ ಆನ್ ಮಾಡಬೇಕೇ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ಆನ್ ಮಾಡಿ"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index da8f4ab04921..43963afc6b04 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"모나크"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"쿼토"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"풀스캡"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"지정되지 않은 세로 방향"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"지정되지 않은 가로 방향"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"취소됨"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"관리자에 의해 업데이트되었습니다."</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"관리자에 의해 삭제되었습니다."</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"확인"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"절전 기능은 어두운 테마를 사용 설정하고 백그라운드 활동, 일부 시각 효과, \'Hey Google\'과 같은 기능을 제한하거나 사용 중지합니다.\n\n"<annotation id="url">"자세히 알아보기"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"절전 기능은 어두운 테마를 사용 설정하고 백그라운드 활동, 일부 시각 효과, \'Hey Google\'과 같은 기능을 제한하거나 사용 중지합니다."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"데이터 사용량을 줄이기 위해 데이터 절약 모드는 일부 앱이 백그라운드에서 데이터를 전송하거나 수신하지 못하도록 합니다. 현재 사용 중인 앱에서 데이터에 액세스할 수 있지만 빈도가 줄어듭니다. 예를 들면, 이미지를 탭하기 전에는 이미지가 표시되지 않습니다."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"데이터 절약 모드를 사용 설정하시겠습니까?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"사용 설정"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 8feb675e3675..a2b199c0f37d 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch (184mm x 267mm)"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto (203mm x 254mm)"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap (203mm x 330mm)"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K (270mm x 390mm)"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K (195mm x 270mm)"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1 (102mm x 165mm)"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu (240mm x 322.1mm)"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2 (240mm x 332mm)"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4 (105mm x 235mm)"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Белгисиз, тикесинен"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Белгисиз, туурасынан"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Токтотулду"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Администраторуңуз жаңыртып койгон"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Администраторуңуз жок кылып салган"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ЖАРАЙТ"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Батареяны үнөмдөгүч режиминде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер жана \"Окей, Google\" сыяктуу функциялар чектелип же өчүрүлөт\n\n"<annotation id="url">"Кеңири маалымат"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Батареяны үнөмдөгүч режиминде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер жана \"Окей, Google\" сыяктуу функциялар чектелип же өчүрүлөт."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Трафикти үнөмдөө режиминде айрым колдонмолор маалыматтарды фондо өткөрө алышпайт. Учурда сиз пайдаланып жаткан колдонмо маалыматтарды жөнөтүп/ала алат, бирок адаттагыдан азыраак өткөргөндүктөн, анын айрым функциялары талаптагыдай иштебей коюшу мүмкүн. Мисалы, сүрөттөр басылмайынча жүктөлбөйт."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Трафикти үнөмдөө режимин иштетесизби?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Күйгүзүү"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index caa91544290a..9426514e7425 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Unknown portrait"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Unknown landscape"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"ຍົກເລີກແລ້ວ"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"ຖືກອັບໂຫລດໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"ຖືກລຶບອອກໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ຕົກລົງ"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ຕົວປະຢັດແບັດເຕີຣີຈະເປີດໃຊ້ຮູບແບບສີສັນມືດ ແລະ ຈຳກັດ ຫຼື ປິດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກດ້ານພາບບາງຢ່າງ ແລະ ຄຸນສົມບັດຕ່າງໆ ເຊັ່ນ: “Ok Google”\n\n"<annotation id="url">"ສຶກສາເພີ່ມເຕີມ"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"ຕົວປະຢັດແບັດເຕີຣີຈະເປີດໃຊ້ຮູບແບບສີສັນມືດ ແລະ ຈຳກັດ ຫຼື ປິດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກດ້ານພາບບາງຢ່າງ ແລະ ຄຸນສົມບັດຕ່າງໆ ເຊັ່ນ: “Ok Google”."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"ເພື່ອຊ່ວຍຫຼຸດຜ່ອນການນຳໃຊ້ຂໍ້ມູນ, ຕົວປະຢັດອິນເຕີເນັດຈະປ້ອງກັນບໍ່ໃຫ້ບາງແອັບສົ່ງ ຫຼື ຮັບຂໍ້ມູນໃນພື້ນຫຼັງ. ແອັບໃດໜຶ່ງທີ່ທ່ານກຳລັງໃຊ້ຢູ່ຈະສາມາດເຂົ້າເຖິງຂໍ້ມູນໄດ້ ແຕ່ອາດເຂົ້າເຖິງໄດ້ຖີ່ໜ້ອຍລົງ. ນີ້ອາດໝາຍຄວາມວ່າ ຮູບພາບຕ່າງໆອາດບໍ່ສະແດງຈົນກວ່າທ່ານຈະແຕະໃສ່ກ່ອນ."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ເປີດຕົວປະຢັດອິນເຕີເນັດບໍ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ເປີດໃຊ້"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index ed4f15ea85cb..b6f6975efac4 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -1817,6 +1817,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1853,6 +1875,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Nežinomas stačias"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Nežinomas gulsčias"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Atšaukta"</string> @@ -1898,8 +1922,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Atnaujino administratorius"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Ištrynė administratorius"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Gerai"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Akumuliatoriaus tausojimo priemonė įjungia tamsiąją temą ir apriboja arba išjungia veiklą fone, kai kuriuos vizualinius efektus ir funkcijas, pvz., „Ok Google“\n\n"<annotation id="url">"Sužinokite daugiau"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Akumuliatoriaus tausojimo priemonė įjungia tamsiąją temą ir apriboja arba išjungia veiklą fone, kai kuriuos vizualinius efektus ir funkcijas, pvz., „Ok Google“."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Kad padėtų sumažinti duomenų naudojimą, Duomenų taupymo priemonė neleidžia kai kurioms programoms siųsti ar gauti duomenų fone. Šiuo metu naudojama programa gali pasiekti duomenis, bet tai bus daroma rečiau. Tai gali reikšti, kad, pvz., vaizdai nebus pateikiami, jei jų nepaliesite."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Įj. Duomenų taupymo priemonę?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Įjungti"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 94a50ea3e50e..70b37bd2c27f 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -1795,6 +1795,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1831,6 +1853,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Nezināma izmēra portrets"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Nezināma izmēra ainava"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Atcelts"</string> @@ -1875,8 +1899,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Atjaunināja administrators"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Dzēsa administrators"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Labi"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Akumulatora enerģijas taupīšanas režīmā tiek ieslēgts tumšais motīvs un ierobežotas vai izslēgtas darbības fonā, konkrēti vizuālie efekti un tādas funkcijas kā īsinājumvārda “Hey Google” atpazīšana.\n\n"<annotation id="url">"Uzzināt vairāk"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Akumulatora enerģijas taupīšanas režīmā tiek ieslēgts tumšais motīvs un ierobežotas vai izslēgtas darbības fonā, konkrēti vizuālie efekti un tādas funkcijas kā īsinājumvārda “Hey Google” atpazīšana."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Lai samazinātu datu lietojumu, datu lietojuma samazinātājs neļauj dažām lietotnēm fonā nosūtīt vai saņemt datus. Lietotne, kuru pašlaik izmantojat, var piekļūt datiem, bet, iespējams, piekļūs tiem retāk (piemēram, attēli tiks parādīti tikai tad, kad tiem pieskarsieties)."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Vai ieslēgt datu lietojuma samazinātāju?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Ieslēgt"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 5e90faa2555e..df0afb0270cd 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -1566,7 +1566,7 @@ <string name="action_menu_overflow_description" msgid="4579536843510088170">"Повеќе опции"</string> <string name="action_bar_home_description_format" msgid="5087107531331621803">"%1$s, %2$s"</string> <string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s, %2$s, %3$s"</string> - <string name="storage_internal" msgid="8490227947584914460">"Внатрешно заедничко место за складирање"</string> + <string name="storage_internal" msgid="8490227947584914460">"Внатрешен споделен капацитет"</string> <string name="storage_sd_card" msgid="3404740277075331881">"СД картичка"</string> <string name="storage_sd_card_label" msgid="7526153141147470509">"<xliff:g id="MANUFACTURER">%s</xliff:g> СД-картичка"</string> <string name="storage_usb_drive" msgid="448030813201444573">"USB-меморија"</string> @@ -1621,7 +1621,7 @@ <string name="wireless_display_route_description" msgid="8297563323032966831">"Безжичен приказ"</string> <string name="media_route_button_content_description" msgid="2299223698196869956">"Емитувај"</string> <string name="media_route_chooser_title" msgid="6646594924991269208">"Поврзи се со уред"</string> - <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Префрли екран на уред"</string> + <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Емитување екран на уред"</string> <string name="media_route_chooser_searching" msgid="6119673534251329535">"Се бараат уреди..."</string> <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Поставки"</string> <string name="media_route_controller_disconnect" msgid="7362617572732576959">"Прекини врска"</string> @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Монарх"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Кварто"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Непознат портрет"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Непознат пејзаж"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Откажано"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирано од администраторот"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Избришано од администраторот"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Во ред"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"„Штедачот на батерија“ вклучува темна тема и исклучува или ограничува активност во заднина, некои визуелни ефекти и функции како „Ok Google“\n\n"<annotation id="url">"Дознајте повеќе"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"„Штедачот на батерија“ вклучува темна тема и исклучува или ограничува активност во заднина, некои визуелни ефекти и функции како „Ok Google“."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"За да се намали користењето интернет, „Штедачот на интернет“ спречува дел од апликациите да испраќаат или да примаат податоци во заднина. Одредена апликација што ја користите ќе може да користи интернет, но можеби тоа ќе го прави поретко. Ова значи, на пример, дека сликите нема да се прикажуваат додека не ги допрете."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Да се вклучи „Штедач на интернет“?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Вклучи"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 5a47e602cb20..d479d30093ca 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"മൊണാർക്ക്"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"ക്വാർട്ടോ"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"ഫൂൾസ്കെയ്പ്പ്"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"കഹു"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"അജ്ഞാത പോർട്രെയ്റ്റ്"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"അജ്ഞാത ലാൻഡ്സ്കെയ്പ്പ്"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"റദ്ദാക്കി"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"നിങ്ങളുടെ അഡ്മിൻ അപ്ഡേറ്റ് ചെയ്യുന്നത്"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"നിങ്ങളുടെ അഡ്മിൻ ഇല്ലാതാക്കുന്നത്"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ശരി"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ബാറ്ററി ലാഭിക്കൽ, ഡാർക്ക് തീം ഓണാക്കുന്നു, പശ്ചാത്തല പ്രവർത്തനവും ചില വിഷ്വൽ ഇഫക്റ്റുകളും “Ok Google” പോലുള്ള ഫീച്ചറുകളും നിയന്ത്രിക്കുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു\n\n"<annotation id="url">"കൂടുതലറിയുക"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"ബാറ്ററി ലാഭിക്കൽ, ഡാർക്ക് തീം ഓണാക്കുന്നു, പശ്ചാത്തല പ്രവർത്തനവും ചില വിഷ്വൽ ഇഫക്റ്റുകളും “Ok Google” പോലുള്ള ഫീച്ചറുകളും നിയന്ത്രിക്കുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"ഡാറ്റാ ഉപയോഗം കുറയ്ക്കാൻ സഹായിക്കുന്നതിനായി പശ്ചാത്തലത്തിൽ ഡാറ്റ അയയ്ക്കുകയോ സ്വീകരിക്കുകയോ ചെയ്യുന്നതിൽ നിന്ന് ചില ആപ്പുകളെ ഡാറ്റാ സേവർ തടയുന്നു. നിങ്ങൾ നിലവിൽ ഉപയോഗിക്കുന്ന ഒരു ആപ്പിന് ഡാറ്റ ആക്സസ് ചെയ്യാനാകും, എന്നാൽ വല്ലപ്പോഴും മാത്രമെ സംഭവിക്കുന്നുള്ളു. ഇതിനർത്ഥം, ഉദാഹരണമായി നിങ്ങൾ ടാപ്പ് ചെയ്യുന്നത് വരെ ചിത്രങ്ങൾ പ്രദർശിപ്പിക്കുകയില്ല എന്നാണ്."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ഡാറ്റ സേവർ ഓണാക്കണോ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ഓണാക്കുക"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 30ab6920fe28..6e8abd146ae5 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Тодорхойгүй босоо цаас"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Тодорхойгүй хөндлөн цаас"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Цуцлагдсан"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Таны админ шинэчилсэн"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Таны админ устгасан"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Батарей хэмнэгч нь Бараан загварыг асааж, дэвсгэрийн үйл ажиллагаа, зарим визуал эффект болон “Hey Google” зэрэг онцлогуудыг хязгаарлаж эсвэл унтраана\n\n"<annotation id="url">"Нэмэлт мэдээлэл авах"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Батарей хэмнэгч нь Бараан загварыг асааж, дэвсгэрийн үйл ажиллагаа, зарим визуал эффект болон “Hey Google” зэрэг онцлогуудыг хязгаарлаж эсвэл унтраана."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Дата ашиглалтыг багасгахын тулд дата хэмнэгч нь ар талд ажиллаж буй зарим апп-н өгөгдлийг илгээх болон авахаас сэргийлдэг. Таны одоогийн ашиглаж буй апп нь өгөгдөлд хандах боломжтой хэдий ч тогтмол хандахгүй. Энэ нь жишээлбэл зургийг товших хүртэл харагдахгүй гэсэн үг юм."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Дата хэмнэгчийг асаах уу?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Асаах"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 9beb59f1c89f..44ca94e8abb8 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"अज्ञात पोट्रेट"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"अज्ञात लँडस्केप"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"रद्द केले"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"आपल्या प्रशासकाने अपडेट केले"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"आपल्या प्रशासकाने हटवले"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ओके"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"बॅटरी सेव्हर गडद थीम सुरू करते आणि बॅकग्राउंड ॲक्टिव्हिटी, काही व्हिज्युअल इफेक्ट व “Ok Google” सारखी वैशिष्ट्ये मर्यादित किंवा बंद करते\n\n"<annotation id="url">"अधिक जाणून घ्या"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"बॅटरी सेव्हर गडद थीम सुरू करते आणि बॅकग्राउंड ॲक्टिव्हिटी, काही व्हिज्युअल इफेक्ट व “Ok Google” सारखी वैशिष्ट्ये मर्यादित किंवा बंद करते."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"डेटाचा वापर कमी करण्यात मदत करण्यासाठी काही अॅप्सना बॅकग्राउंडमध्ये डेटा पाठवण्यास किंवा मिळवण्यास डेटा सर्व्हर प्रतिबंध करतो. तुम्ही सध्या वापरत असलेले अॅप डेटा अॅक्सेस करू शकते, पण तसे खूप कमी वेळा होते. याचाच अर्थ असा की, तुम्ही इमेजवर टॅप करेपर्यंत त्या डिस्प्ले होणार नाहीत असे होऊ शकते."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा सेव्हर सुरू करायचे?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"सुरू करा"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 62298a99df3b..ddb4816fd942 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarki"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Kertas kajang"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Potret tidak diketahui"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Landskap tidak diketahui"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Dibatalkan"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Dikemas kini oleh pentadbir anda"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Dipadamkan oleh pentadbir anda"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Penjimat Bateri menghidupkan Tema gelap dan mengehadkan atau mematikan aktiviti latar, sesetengah kesan visual dan ciri seperti \"Ok Google\"\n\n"<annotation id="url">"Ketahui lebih lanjut"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Penjimat Bateri menghidupkan Tema gelap dan mengehadkan atau mematikan aktiviti latar, sesetengah kesan visual dan ciri seperti \"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Untuk membantu penggunaan data dikurangkan, Penjimat Data menghalang sesetengah apl daripada menghantar atau menerima data di latar. Apl yang sedang digunakan boleh mengakses data tetapi mungkin tidak secara kerap. Perkara ini mungkin bermaksud bahawa imej tidak dipaparkan sehingga anda mengetik pada imej itu, contohnya."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Hidupkan Penjimat Data?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Hidupkan"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 9c6593ec51fe..d1575b917f86 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"မိုနာချ့်"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"ကွာတို"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"ဖူးစကဒ်"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"အာအိုစီ ၈ကေ"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"အာအိုစီ ၁၆ကေ"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"ပီအာစီ ၁"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"ကဟူ"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"ကဟူ၂"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"ယူ၄"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"ဒေါင်လိုက် အရွယ်မသိ"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"အလျားလိုက် အရွယ်မသိ"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"ဖျက်သိမ်းလိုက်ပြီး"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"သင်၏ စီမံခန့်ခွဲသူက အပ်ဒိတ်လုပ်ထားသည်"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"သင်၏ စီမံခန့်ခွဲသူက ဖျက်လိုက်ပါပြီ"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"‘ဘက်ထရီ အားထိန်း’ က ‘မှောင်သည့် အပြင်အဆင်’ ကို ဖွင့်ပြီး နောက်ခံလုပ်ဆောင်ချက်၊ ပြသမှုဆိုင်ရာ အထူးပြုလုပ်ချက်အချို့နှင့် “Ok Google” ကဲ့သို့ ဝန်ဆောင်မှုများကို ကန့်သတ်သည် (သို့) ပိတ်သည်\n\n"<annotation id="url">"ပိုမိုလေ့လာရန်"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"‘ဘက်ထရီ အားထိန်း’ က ‘မှောင်သည့် အပြင်အဆင်’ ကို ဖွင့်ပြီး နောက်ခံလုပ်ဆောင်ချက်၊ ပြသမှုဆိုင်ရာ အထူးပြုလုပ်ချက်အချို့နှင့် “Ok Google” ကဲ့သို့ ဝန်ဆောင်မှုများကို ကန့်သတ်သည် (သို့) ပိတ်သည်။"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"ဒေတာအသုံးလျှော့ချနိုင်ရန်အတွက် အက်ပ်များကို နောက်ခံတွင် ဒေတာပို့ခြင်းနှင့် လက်ခံခြင်းမပြုရန် \'ဒေတာချွေတာမှု\' စနစ်က တားဆီးထားပါသည်။ ယခုအက်ပ်ဖြင့် ဒေတာအသုံးပြုနိုင်သော်လည်း အကြိမ်လျှော့၍သုံးရပါမည်။ ဥပမာ၊ သင်က မတို့မချင်း ပုံများပေါ်လာမည် မဟုတ်ပါ။"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ဒေတာချွေတာမှုစနစ် ဖွင့်မလား။"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ဖွင့်ပါ"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 4720e295cd7f..3635fb02531c 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Ukjent portrett"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Ukjent landskap"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Kansellert"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Oppdatert av administratoren din"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Slettet av administratoren din"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Batterisparing slår på mørkt tema og begrenser eller slår av bakgrunnsaktivitet, enkelte visuelle effekter og funksjoner, for eksempel «Hey Google»\n\n"<annotation id="url">"Finn ut mer"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Batterisparing slår på mørkt tema og begrenser eller slår av bakgrunnsaktivitet, enkelte visuelle effekter og funksjoner, for eksempel «Hey Google»."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Datasparing hindrer noen apper fra å sende og motta data i bakgrunnen, for å redusere dataforbruket. Aktive apper kan bruke data, men kanskje ikke så mye som ellers – for eksempel vises ikke bilder før du trykker på dem."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Vil du slå på Datasparing?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Slå på"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 458e09b7f7b2..90e0d24fa8ed 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"मोनार्क"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"क्वार्टो"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"फुलस्केप"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"अज्ञात चित्र"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"अज्ञात परिदृश्य"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"रद्द गरियो"</string> @@ -1852,9 +1876,9 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"तपाईंका प्रशासकले अद्यावधिक गर्नुभएको"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"तपाईंका प्रशासकले मेट्नुभएको"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ठिक छ"</string> - <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) --> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> <skip /> - <!-- no translation found for battery_saver_description (7695751399533397741) --> + <!-- no translation found for battery_saver_description (5693741424234005958) --> <skip /> <string name="data_saver_description" msgid="4995164271550590517">"डेटा सेभरले डेटा खपत कम गर्न केही एपहरूलाई ब्याकग्राउन्डमा डेटा पठाउन वा प्राप्त गर्न दिँदैन। तपाईंले अहिले प्रयोग गरिरहनुभएको एपले सीमित रूपमा मात्र डेटा चलाउन पाउँछ। उदाहरणका लागि, तपाईंले फोटोमा ट्याप गर्नुभयो भने मात्र फोटो देखिन्छ नत्र देखिँदैन।"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा सेभर अन गर्ने हो?"</string> @@ -1902,7 +1926,7 @@ <string name="zen_mode_downtime_feature_name" msgid="5886005761431427128">"डाउनटाइम"</string> <string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"हरेक हप्तादिनको राति"</string> <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"शनिबार"</string> - <string name="zen_mode_default_events_name" msgid="2280682960128512257">"घटना"</string> + <string name="zen_mode_default_events_name" msgid="2280682960128512257">"कार्यक्रम"</string> <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"शयन अवस्था"</string> <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ले केही ध्वनिहरू म्युट गर्दै छ"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"तपाईंको यन्त्रसँग आन्तरिक समस्या छ, र तपाईंले फ्याक्ट्री डाटा रिसेट नगर्दासम्म यो अस्थिर रहन्छ।"</string> @@ -1931,8 +1955,7 @@ <string name="close_button_text" msgid="10603510034455258">"बन्द गर्नुहोस्"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> <string name="call_notification_answer_action" msgid="5999246836247132937">"कलको जवाफ दिनु…"</string> - <!-- no translation found for call_notification_answer_video_action (2086030940195382249) --> - <skip /> + <string name="call_notification_answer_video_action" msgid="2086030940195382249">"भिडियो"</string> <string name="call_notification_decline_action" msgid="3700345945214000726">"अस्वीकार गर्नुहोस्"</string> <string name="call_notification_hang_up_action" msgid="9130720590159188131">"फोन राख्नुहोस्"</string> <string name="call_notification_incoming_text" msgid="6143109825406638201">"आगमन कल"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index fcc526497b2b..af56ee3f6571 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Onbekend staand"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Onbekend liggend"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Geannuleerd"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Geüpdatet door je beheerder"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Verwijderd door je beheerder"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Batterijbesparing zet het donkere thema aan en beperkt achtergrondactiviteit, bepaalde visuele effecten en functies zoals Hey Google of zet dit uit\n\n"<annotation id="url">"Meer informatie"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Batterijbesparing zet het donkere thema aan en beperkt achtergrondactiviteit, bepaalde visuele effecten en functies zoals Hey Google of zet dit uit."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Databesparing beperkt het datagebruik door te voorkomen dat sommige apps gegevens sturen of ontvangen op de achtergrond. De apps die je open hebt, kunnen nog steeds data verbruiken, maar doen dit minder vaak. Afbeeldingen worden dan bijvoorbeeld niet weergegeven totdat je erop tikt."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Databesparing aanzetten?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aanzetten"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 55bb3151a138..734a81e00069 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"ମୋନାର୍କ"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"କ୍ୱାର୍ଟୋ"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"ଅଜଣା ପୋର୍ଟ୍ରେଟ୍"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"ଅଜଣା ଲ୍ୟାଣ୍ଡସ୍କେପ୍"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"ବାତିଲ୍ କରାଗଲା"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"ଆପଣଙ୍କ ଆଡମିନ୍ ଅପଡେଟ୍ କରିଛନ୍ତି"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"ଆପଣଙ୍କ ଆଡମିନ୍ ଡିଲିଟ୍ କରିଛନ୍ତି"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ଠିକ୍ ଅଛି"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ବ୍ୟାଟେରୀ ସେଭର୍ ଗାଢ଼ା ଥିମକୁ ଚାଲୁ କରେ ଏବଂ ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ଇଫେକ୍ଟ ଏବଂ “Hey Google” ପରି ଫିଚରଗୁଡ଼ିକୁ ସୀମିତ କିମ୍ବା ବନ୍ଦ କରେ\n\n"<annotation id="url">"ଅଧିକ ଜାଣନ୍ତୁ"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"ବ୍ୟାଟେରୀ ସେଭର୍ ଗାଢ଼ା ଥିମକୁ ଚାଲୁ କରେ ଏବଂ ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ଇଫେକ୍ଟ ଏବଂ “Hey Google” ପରି ଫିଚରଗୁଡ଼ିକୁ ସୀମିତ କିମ୍ବା ବନ୍ଦ କରେ।"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"ଡାଟା ବ୍ୟବହାର କମ୍ କରିବାରେ ସାହାଯ୍ୟ କରିବାକୁ, ଡାଟା ସେଭର୍ ବ୍ୟାକ୍ଗ୍ରାଉଣ୍ଡରେ ଡାଟା ପଠାଇବା କିମ୍ବା ପ୍ରାପ୍ତ କରିବାକୁ କିଛି ଆପ୍କୁ ବାରଣ କରେ। ଆପଣ ବର୍ତ୍ତମାନ ବ୍ୟବହାର କରୁଥିବା ଆପ୍, ଡାଟା ଆକ୍ସେସ୍ କରିପାରେ, କିନ୍ତୁ ଏହା କମ୍ ଥର କରିପାରେ। ଏହାର ଅର୍ଥ ହୋଇପାରେ ଯେମିତି ଆପଣ ଇମେଜଗୁଡ଼ିକୁ ଟାପ୍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ସେଗୁଡ଼ିକ ଡିସପ୍ଲେ ହୁଏ ନାହିଁ।"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ଡାଟା ସେଭର୍ ଚାଲୁ କରିବେ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ଚାଲୁ କରନ୍ତୁ"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index bfa576033543..a95244ae68e8 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"ਸਮਰਾਟ"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"ਚੁਪੱਤਰੀ"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"ਅਗਿਆਤ ਪੋਰਟਰੇਟ"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"ਅਗਿਆਤ ਲੈਂਡਸਕੇਪ"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"ਰੱਦ ਕੀਤਾ ਗਿਆ"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਮਿਟਾਇਆ ਗਿਆ"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ਠੀਕ ਹੈ"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ਬੈਟਰੀ ਸੇਵਰ ਗੂੜ੍ਹੇ ਥੀਮ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ ਅਤੇ \"Ok Google\" ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਸੀਮਤ ਕਰਦਾ ਹੈ ਜਾਂ ਬੰਦ ਕਰਦਾ ਹੈ\n\n"<annotation id="url">"ਹੋਰ ਜਾਣੋ"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"ਬੈਟਰੀ ਸੇਵਰ ਗੂੜ੍ਹੇ ਥੀਮ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ ਅਤੇ \"Ok Google\" ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਸੀਮਤ ਕਰਦਾ ਹੈ ਜਾਂ ਬੰਦ ਕਰਦਾ ਹੈ।"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"ਡਾਟਾ ਵਰਤੋਂ ਘਟਾਉਣ ਵਿੱਚ ਮਦਦ ਲਈ, ਡਾਟਾ ਸੇਵਰ ਕੁਝ ਐਪਾਂ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਡਾਟਾ ਭੇਜਣ ਜਾਂ ਪ੍ਰਾਪਤ ਕਰਨ ਤੋਂ ਰੋਕਦਾ ਹੈ। ਤੁਹਾਡੇ ਵੱਲੋਂ ਵਰਤਮਾਨ ਤੌਰ \'ਤੇ ਵਰਤੀ ਜਾ ਰਹੀ ਐਪ ਡਾਟਾ \'ਤੇ ਪਹੁੰਚ ਕਰ ਸਕਦੀ ਹੈ, ਪਰ ਉਹ ਇੰਝ ਕਦੇ-ਕਦਾਈਂ ਕਰ ਸਕਦੀ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਸ ਦਾ ਮਤਲਬ ਇਹ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਚਿੱਤਰ ਤਦ ਤੱਕ ਨਹੀਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕੀਤੇ ਜਾਂਦੇ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਉਹਨਾਂ \'ਤੇ ਟੈਪ ਨਹੀਂ ਕਰਦੇ।"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ਕੀ ਡਾਟਾ ਸੇਵਰ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ਚਾਲੂ ਕਰੋ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 57037ad5bbd9..8233accfa3d3 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -1817,6 +1817,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1853,6 +1875,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Nieznany pionowy"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Nieznany poziomy"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Anulowane"</string> @@ -1898,8 +1922,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Zaktualizowany przez administratora"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Usunięty przez administratora"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Oszczędzanie baterii uruchamia ciemny motyw oraz wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne i inne funkcje, np. „OK Google”\n\n"<annotation id="url">"Więcej informacji"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Oszczędzanie baterii uruchamia ciemny motyw oraz wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne i inne funkcje, np. „OK Google”."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Oszczędzanie danych uniemożliwia niektórym aplikacjom wysyłanie i odbieranie danych w tle, zmniejszając w ten sposób ich użycie. Aplikacja, z której w tej chwili korzystasz, może uzyskiwać dostęp do danych, ale rzadziej. Może to powodować, że obrazy będą się wyświetlać dopiero po kliknięciu."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Włączyć Oszczędzanie danych?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Włącz"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 102073e5921e..d0d05226de6a 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Retrato desconhecido"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Paisagem desconhecido"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Cancelado"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu administrador"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Excluído pelo seu administrador"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"A Economia de bateria ativa o tema escuro e limita ou desativa as atividades em segundo plano, alguns efeitos visuais e recursos como o \"Ok Google\"\n\n"<annotation id="url">"Saiba mais"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"A Economia de bateria ativa o tema escuro e limita ou desativa as atividades em segundo plano, alguns efeitos visuais e recursos como o \"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você esteja usando no momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não sejam exibidas até que você toque nelas."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Ativar \"Economia de dados\"?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index d0c2beb61c79..dd3d1b414d4f 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Vertical desconhecido"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Horizontal desconhecido"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Cancelada"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu gestor"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado pelo seu gestor"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"A Poupança de bateria ativa o tema escuro e limita ou desativa a atividade em segundo plano, alguns efeitos visuais e funcionalidades como \"Ok Google\".\n\n"<annotation id="url">"Saiba mais"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"A Poupança de bateria ativa o tema escuro e limita ou desativa a atividade em segundo plano, alguns efeitos visuais e funcionalidades como \"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir a utilização de dados, a Poupança de dados impede que algumas aplicações enviem ou recebam dados em segundo plano. Uma determinada app que esteja a utilizar atualmente pode aceder aos dados, mas é possível que o faça com menos frequência. Isto pode significar, por exemplo, que as imagens não são apresentadas até que toque nas mesmas."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Pretende ativar a Poupança de dados?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 102073e5921e..d0d05226de6a 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Retrato desconhecido"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Paisagem desconhecido"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Cancelado"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu administrador"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Excluído pelo seu administrador"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"A Economia de bateria ativa o tema escuro e limita ou desativa as atividades em segundo plano, alguns efeitos visuais e recursos como o \"Ok Google\"\n\n"<annotation id="url">"Saiba mais"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"A Economia de bateria ativa o tema escuro e limita ou desativa as atividades em segundo plano, alguns efeitos visuais e recursos como o \"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você esteja usando no momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não sejam exibidas até que você toque nelas."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Ativar \"Economia de dados\"?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index e6b7ff668057..e5e960f95db8 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -1795,6 +1795,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1831,6 +1853,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Portret necunoscut"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Peisaj necunoscut"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Anulat"</string> @@ -1875,8 +1899,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizat de administratorul dvs."</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Șters de administratorul dvs."</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Economisirea bateriei activează Tema întunecată și limitează sau dezactivează activitatea din fundal, anumite efecte vizuale și funcții precum „Ok Google”\n\n"<annotation id="url">"Aflați mai multe"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Economisirea bateriei activează Tema întunecată și limitează sau dezactivează activitatea din fundal, anumite efecte vizuale și funcții precum „Ok Google”."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Pentru a contribui la reducerea utilizării de date, Economizorul de date împiedică unele aplicații să trimită sau să primească date în fundal. O aplicație pe care o folosiți poate accesa datele, însă mai rar. Aceasta poate însemna, de exemplu, că imaginile se afișează numai după ce le atingeți."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Activați Economizorul de date?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activați"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 7d87d7c4313f..55d6ac8f65de 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1817,6 +1817,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1853,6 +1875,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kaku"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Неизвестный вертикальный формат"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Неизвестный горизонтальный формат"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Печать отменена"</string> @@ -1898,8 +1922,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Обновлено администратором"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Удалено администратором"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"В режиме энергосбережения включается тёмная тема. Кроме того, отключаются или ограничиваются фоновые процессы, некоторые визуальные эффекты и различные функции, например распознавание команды \"Окей, Google\".\n\n"<annotation id="url">"Подробнее…"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"В режиме энергосбережения включается тёмная тема. Кроме того, отключаются или ограничиваются фоновые процессы, некоторые визуальные эффекты и различные функции, например распознавание команды \"Окей, Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"В режиме экономии трафика фоновая передача данных для некоторых приложений отключена. Приложение, которым вы пользуетесь, может получать и отправлять данные, но реже, чем обычно. Например, изображения могут не загружаться, пока вы не нажмете на них."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Включить экономию трафика?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Включить"</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index c0577d68ab97..0130d1ef484a 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"ෆුල්ස්කැප්"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super-B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"නොදන්නා සිරස් දිශානතිය"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"නොදන්නා තිරස් දිශානතිය"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"අවලංගු කරන ලදි"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"ඔබගේ පරිපාලක මඟින් යාවත්කාලීන කර ඇත"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"ඔබගේ පරිපාලක මඟින් මකා දමා ඇත"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"හරි"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"බැටරි සුරැකුම අඳුරු තේමාව ක්රියාත්මක කර පසුබිම් ක්රියාකාරකම්, සමහර දෘශ්ය ප්රයෝග සහ “Hey Google” වැනි විශේෂාංග සීමා කරයි\n\n"<annotation id="url">"තව දැන ගන්න"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"බැටරි සුරැකුම අඳුරු තේමාව ක්රියාත්මක කර පසුබිම් ක්රියාකාරකම්, සමහර දෘශ්ය ප්රයෝග සහ “Hey Google” වැනි විශේෂාංග සීමා කරයි."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"දත්ත භාවිතය අඩු කිරීමට උදවු වීමට, දත්ත සුරැකුම සමහර යෙදුම් පසුබිමින් දත්ත යැවීම සහ ලබා ගැනීම වළක්වයි. ඔබ දැනට භාවිත කරන යෙදුමකට දත්ත වෙත පිවිසීමට හැකිය, නමුත් එසේ කරන්නේ කලාතුරකින් විය හැකිය. මෙයින් අදහස් වන්නේ, උදාහරණයක් ලෙස, එම රූප ඔබ ඒවාට තට්ටු කරන තෙක් සංදර්ශනය නොවන බවය."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"දත්ත සුරැකුම ක්රියාත්මක කරන්නද?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ක්රියාත්මක කරන්න"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 5c9ddf7bf7a7..e2da90da685d 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -1817,6 +1817,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1853,6 +1875,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Neznáma veľkosť papiera na výšku"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Neznáma veľkosť papiera na šírku"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Zrušené"</string> @@ -1898,8 +1922,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Aktualizoval správca"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Odstránil správca"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Šetrič batérie zapne tmavý motív a obmedzí alebo vypne aktivitu na pozadí, niektoré vizuálne efekty a funkcie, napríklad „Hey Google“\n\n"<annotation id="url">"Ďalšie informácie"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Šetrič batérie zapne tmavý motív a obmedzí alebo vypne aktivitu na pozadí, niektoré vizuálne efekty a funkcie, napríklad „Hey Google“."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"S cieľom znížiť spotrebu dát bráni šetrič dát niektorým aplikáciám odosielať alebo prijímať dáta na pozadí. Aplikácia, ktorú práve používate, môže využívať dáta, ale možno to bude robiť menej často. Môže to napríklad znamenať, že sa obrázky zobrazia, až keď na ne klepnete."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Chcete zapnúť šetrič dát?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Zapnúť"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index e98d0ae2ce6a..01eaed689f06 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -1817,6 +1817,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1853,6 +1864,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Neznano pokončno"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Neznano ležeče"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Preklicano"</string> @@ -1898,11 +1911,13 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Posodobil skrbnik"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisal skrbnik"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"V redu"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Varčevanje z energijo baterije vklopi temno temo in omeji ali izklopi dejavnost v ozadju, nekatere vizualne učinke in druge funkcije, kot je »Hey Google«.\n\n"<annotation id="url">"Več o tem"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Varčevanje z energijo baterije vklopi temno temo in omeji ali izklopi dejavnost v ozadju, nekatere vizualne učinke in druge funkcije, kot je »Hey Google«."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Zaradi zmanjševanja prenesene količine podatkov funkcija varčevanja s podatki nekaterim aplikacijam preprečuje, da bi v ozadju pošiljale ali prejemale podatke. Aplikacija, ki jo trenutno uporabljate, lahko prenaša podatke, vendar to morda počne manj pogosto. To na primer pomeni, da se slike ne prikažejo, dokler se jih ne dotaknete."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Vklop varčevanja s podatki?"</string> - <string name="data_saver_enable_button" msgid="4399405762586419726">"Vklop"</string> + <string name="data_saver_enable_button" msgid="4399405762586419726">"Vklopi"</string> <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273"> <item quantity="one">%d minuto (do <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item> <item quantity="two">%d minuti (do <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 30e13a4ebfe3..387bb4e11aea 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"\"Monarch\""</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"\"Quatro\""</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Vertikalisht i panjohur"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Orientim i panjohur horizontal"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Anuluar"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Përditësuar nga administratori"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Fshirë nga administratori"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Në rregull"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"\"Kursyesi i baterisë\" aktivizon \"Temën e errët\" dhe kufizon ose çaktivizon aktivitetin në sfond, disa efekte vizuale dhe veçoritë si \"Ok Google\"\n\n"<annotation id="url">"Mëso më shumë"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"\"Kursyesi i baterisë\" aktivizon \"Temën e errët\" dhe kufizon ose çaktivizon aktivitetin në sfond, disa efekte vizuale dhe veçoritë si \"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Për të ndihmuar në reduktimin e përdorimit të të dhënave, \"Kursyesi i të dhënave\" pengon që disa aplikacione të dërgojnë apo të marrin të dhëna në sfond. Një aplikacion që po përdor aktualisht mund të ketë qasje te të dhënat, por këtë mund ta bëjë më rrallë. Kjo mund të nënkuptojë, për shembull, se imazhet nuk shfaqen kur troket mbi to."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Të aktivizohet \"Kursyesi i të dhënave\"?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivizo"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 96fa82de9a4b..00fe38da88ff 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -1795,6 +1795,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1831,6 +1842,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Непозната величина, усправно"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Непозната величина, водоравно"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Отказано је"</string> @@ -1875,8 +1888,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирао је администратор"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Избрисао је администратор"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Потврди"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Уштеда батерије укључује Тамну тему и ограничава или искључује активности у позадини, неке визуелне ефекте и функције, на пример, „Хеј Google“.\n\n"<annotation id="url">"Сазнајте више"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Уштеда батерије укључује Тамну тему и ограничава или искључује активности у позадини, неке визуелне ефекте и функције, на пример, „Хеј Google“."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Да би се смањила потрошња података, Уштеда података спречава неке апликације да шаљу или примају податке у позадини. Апликација коју тренутно користите може да приступа подацима, али ће то чинити ређе. На пример, слике се неће приказивати док их не додирнете."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Желите да укључите Уштеду података?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Укључи"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 0ddf34d86bca..a3c350bc71cb 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Porträtt – okänd storlek"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Landskap – okänd storlek"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Inställd"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Administratören uppdaterade paketet"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administratören raderade paketet"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"I batterisparläget aktiveras mörkt tema och bakgrundsaktivitet som vissa visuella effekter och funktioner som ”Hey Google” begränsas eller inaktiveras\n\n"<annotation id="url">"Läs mer"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"I batterisparläget aktiveras mörkt tema och bakgrundsaktivitet som vissa visuella effekter och funktioner som ”Hey Google” begränsas eller inaktiveras."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Med Databesparing kan du minska dataanvändningen genom att hindra en del appar från att skicka eller ta emot data i bakgrunden. Appar som du använder kan komma åt data, men det sker kanske inte lika ofta. Detta innebär t.ex. att bilder inte visas förrän du trycker på dem."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Vill du aktivera Databesparing?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivera"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index ea069e191f75..7eb9ddaa912f 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Mkao wima usiojulikana"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Mandhari yasiyojulikana"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Imeghairiwa"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Imesasishwa na msimamizi wako"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Imefutwa na msimamizi wako"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Sawa"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Kiokoa Betri huwasha Mandhari meusi na kudhibiti au kuzima shughuli za chinichini, baadhi ya madoido yanayoonekana na vipengele kama vile \"Ok Google\"\n\n"<annotation id="url">"Pata maelezo zaidi"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Kiokoa Betri huwasha Mandhari meusi na kudhibiti au kuzima shughuli za chinichini, baadhi ya madoido yanayoonekana na vipengele kama vile \"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Ili kusaidia kupunguza matumizi ya data, Kiokoa Data huzuia baadhi ya programu kupokea na kutuma data chinichini. Programu ambayo unatumia sasa inaweza kufikia data, lakini si kila wakati. Kwa mfano, haitaonyesha picha hadi utakapozifungua."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Ungependa Kuwasha Kiokoa Data?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Washa"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 962b9a3727bd..e68a865f9cd4 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"மோனார்க்"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"குவார்டோ"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"பூல்ஸ்கேப்"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"அறியப்படாத நிலைபதிப்பு"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"அறியப்படாத நிலைபரப்பு"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"ரத்துசெய்யப்பட்டது"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"உங்கள் நிர்வாகி புதுப்பித்துள்ளார்"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"உங்கள் நிர்வாகி நீக்கியுள்ளார்"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"சரி"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"பேட்டரி சேமிப்பான் டார்க் தீமினை ஆன் செய்து பின்னணிச் செயல்பாடு, சில விஷுவல் எஃபெக்ட்கள், “Ok Google” போன்ற அம்சங்களை ஆஃப் செய்யும் அல்லது கட்டுப்படுத்தும்\n\n"<annotation id="url">"மேலும் அறிக"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"பேட்டரி சேமிப்பான் டார்க் தீமினை ஆன் செய்து பின்னணிச் செயல்பாடு, சில விஷுவல் எஃபெக்ட்கள், “Ok Google” போன்ற அம்சங்களை ஆஃப் செய்யும் அல்லது கட்டுப்படுத்தும்."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"டேட்டா உபயோகத்தைக் குறைப்பதற்கு உதவ, பின்புலத்தில் டேட்டாவை அனுப்புவது அல்லது பெறுவதிலிருந்து சில ஆப்ஸை டேட்டா சேமிப்பான் தடுக்கும். தற்போது பயன்படுத்தும் ஆப்ஸானது எப்போதாவது டேட்டாவை அணுகலாம். எடுத்துக்காட்டாக, படங்களை நீங்கள் தட்டும் வரை அவை காட்டப்படாது."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"டேட்டா சேமிப்பானை இயக்கவா?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"இயக்கு"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 2fa3710fc8c9..210ed4f1d190 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"మోనార్క్"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"క్వార్టో"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"ఫుల్స్కేప్"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"కాహు"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"కాకు2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"యు4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"తెలియని పొర్ట్రెయిట్"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"తెలియని ల్యాండ్స్కేప్"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"రద్దు చేయబడింది"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"మీ నిర్వాహకులు నవీకరించారు"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"మీ నిర్వాహకులు తొలగించారు"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"సరే"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"బ్యాటరీ సేవర్ ముదురు రంగు రూపంను ఆన్ చేస్తుంది అలాగే బ్యాక్గ్రౌండ్ యాక్టివిటీని, కొన్ని విజువల్ ఎఫెక్ట్లు, అలాగే “Ok Google” వంటి ఫీచర్లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది\n\n"<annotation id="url">"మరింత తెలుసుకోండి"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"బ్యాటరీ సేవర్ ముదురు రంగు రూపంను ఆన్ చేస్తుంది అలాగే బ్యాక్గ్రౌండ్ యాక్టివిటీని, కొన్ని విజువల్ ఎఫెక్ట్లు, అలాగే “Ok Google” వంటి ఫీచర్లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"డేటా వినియోగాన్ని తగ్గించడంలో డేటా సేవర్ సహాయకరంగా ఉంటుంది. బ్యాక్గ్రౌండ్లో కొన్ని యాప్లు డేటాను పంపకుండా లేదా స్వీకరించకుండా నిరోధిస్తుంది. మీరు ప్రస్తుతం ఉపయోగిస్తోన్న యాప్, డేటాను యాక్సెస్ చేయగలదు. కానీ తక్కువ సార్లు మాత్రమే అలా చేయవచ్చు. ఉదాహరణకు, మీరు నొక్కే వరకు ఫోటోలు ప్రదర్శించబడవు."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"డేటా సేవర్ను ఆన్ చేయాలా?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ఆన్ చేయి"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index db3876548c96..5b00f7341fa2 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"กระดาษฟุลสแก๊ป"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"แนวตั้งไม่ทราบขนาด"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"แนวนอนไม่ทราบขนาด"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"ยกเลิก"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"อัปเดตโดยผู้ดูแลระบบ"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"ลบโดยผู้ดูแลระบบ"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ตกลง"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"โหมดประหยัดแบตเตอรี่จะเปิดธีมมืดและจำกัดหรือปิดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง และฟีเจอร์อย่างเช่น \"Ok Google\"\n\n"<annotation id="url">"ดูข้อมูลเพิ่มเติม"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"โหมดประหยัดแบตเตอรี่จะเปิดธีมมืดและจำกัดหรือปิดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง และฟีเจอร์อย่างเช่น \"Ok Google\""</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"เพื่อช่วยลดปริมาณการใช้อินเทอร์เน็ต โปรแกรมประหยัดอินเทอร์เน็ตจะช่วยป้องกันไม่ให้บางแอปส่งหรือรับข้อมูลโดยการใช้อินเทอร์เน็ตอยู่เบื้องหลัง แอปที่คุณกำลังใช้งานสามารถเข้าถึงอินเทอร์เน็ตได้ แต่อาจไม่บ่อยเท่าเดิม ตัวอย่างเช่น ภาพต่างๆ จะไม่แสดงจนกว่าคุณจะแตะที่ภาพเหล่านั้น"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"เปิดการประหยัดอินเทอร์เน็ตไหม"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"เปิด"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index adaa774d8786..c2409c4f41f6 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Hindi alam na portrait"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Hindi alam na landscape"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Kinansela"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Na-update ng iyong admin"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Na-delete ng iyong admin"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Ino-on ng Pantipid ng Baterya ang Madilim na tema at nililimitahan o ino-off nito ang aktibidad sa background, ilang visual effect, at mga feature gaya ng “Hey Google”\n\n"<annotation id="url">"Matuto pa"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Ino-on ng Pantipid ng Baterya ang Madilim na tema at nililimitahan o ino-off nito ang aktibidad sa background, ilang visual effect, at mga feature gaya ng “Hey Google.”"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Upang makatulong na mabawasan ang paggamit ng data, pinipigilan ng Data Saver ang ilang app na magpadala o makatanggap ng data sa background. Maaaring mag-access ng data ang isang app na ginagamit mo sa kasalukuyan, ngunit mas bihira na nito magagawa iyon. Halimbawa, maaaring hindi lumabas ang mga larawan hangga\'t hindi mo nata-tap ang mga ito."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"I-on ang Data Saver?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"I-on"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index bd8532207d88..935e489cb8ad 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Bilinmeyen dikey"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Bilinmeyen yatay"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"İptal edildi"</string> @@ -1852,9 +1876,11 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Yöneticiniz tarafından güncellendi"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Yöneticiniz tarafından silindi"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Tamam"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Pil Tasarrufu, Koyu temayı açıp arka plan etkinliğini, bazı görsel efektleri ve \"Ok Google\" gibi özellikleri kapatır veya kısıtlar.\n\n"<annotation id="url">"Daha fazla bilgi"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Pil Tasarrufu, Koyu temayı açıp arka plan etkinliğini, bazı görsel efektleri ve \"Ok Google\" gibi özellikleri kapatır veya kısıtlar."</string> - <string name="data_saver_description" msgid="4995164271550590517">"Veri kullanımını azaltmaya yardımcı olması için Veri Tasarrufu, bazı uygulamaların arka planda veri göndermesini veya almasını engeller. Şu anda kullandığınız bir uygulama veri bağlantısına erişebilir, ancak bunu daha seyrek yapabilir. Bu durumda örneğin, siz resimlere dokunmadan resimler görüntülenmez."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> + <string name="data_saver_description" msgid="4995164271550590517">"Veri kullanımını azaltmaya yardımcı olması için Veri Tasarrufu, bazı uygulamaların arka planda veri göndermesini veya almasını engeller. Kullanmakta olduğunuz bir uygulama veri bağlantısına erişebilir, ancak bunu daha seyrek yapabilir. Bu durumda örneğin, siz resimlere dokunmadan resimler görüntülenmez."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Veri Tasarrufu açılsın mı?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aç"</string> <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273"> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index b95aa88ba674..ca3b5899e5f4 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -1817,6 +1817,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch (Пн. Америка)"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto (Пн. Америка)"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap (Пн. Америка)"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K (Китай)"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K (Китай)"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1 (Китай)"</string> @@ -1853,6 +1875,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu (Японія)"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2 (Японія)"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4 (Японія)"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Невідома книжкова орієнтація"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Невідома альбомна орієнтація"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Скасовано"</string> @@ -1898,8 +1922,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Оновлено адміністратором"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Видалено адміністратором"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"У режимі енергозбереження вмикається темна тема й обмежуються чи вимикаються фонова робота додатків, деякі візуальні ефекти та інші функції, як-от \"Ok Google\"\n\n"<annotation id="url">"Докладніше"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"У режимі енергозбереження вмикається темна тема й обмежуються чи вимикаються фонова робота додатків, деякі візуальні ефекти та інші функції, як-от \"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Щоб зменшити використання трафіку, функція \"Заощадження трафіку\" не дозволяє деяким додаткам надсилати чи отримувати дані у фоновому режимі. Поточний додаток зможе отримувати доступ до таких даних, але рідше. Наприклад, зображення не відображатиметься, доки ви не торкнетеся його."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Увімкнути заощадження трафіку?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Увімкнути"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index f067641bceff..88786165f18e 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"مونارک"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"کوارٹو"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"فل اسکیپ"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"نامعلوم پورٹریٹ"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"نامعلوم لینڈ اسکیپ"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"منسوخ کر دیا گیا"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"آپ کے منتظم کے ذریعے اپ ڈیٹ کیا گیا"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"آپ کے منتظم کے ذریعے حذف کیا گیا"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ٹھیک ہے"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"بیٹری سیور گہری تھیم کو آن کرتی ہے اور پس منظر کی سرگرمی، کچھ بصری اثرات اور \"Hey Google\" جیسی خصوصیات کو محدود یا آف کرتی ہے\n\n"<annotation id="url">"مزید جانیں"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"بیٹری سیور گہری تھیم کو آن کرتی ہے اور پس منظر کی سرگرمی، کچھ بصری اثرات اور \"Hey Google\" جیسی خصوصیات کو محدود یا آف کرتی ہے۔"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"ڈیٹا کے استعمال کو کم کرنے میں مدد کیلئے، ڈیٹا سیور پس منظر میں کچھ ایپس کو ڈیٹا بھیجنے یا موصول کرنے سے روکتی ہے۔ آپ جو ایپ فی الحال استعمال کر رہے ہیں وہ ڈیٹا تک رسائی کر سکتی ہے مگر ہو سکتا ہے ایسا اکثر نہ ہو۔ اس کا مطلب مثال کے طور پر یہ ہو سکتا ہے کہ تصاویر تھپتھپانے تک ظاہر نہ ہوں۔"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ڈیٹا سیور آن کریں؟"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"آن کریں"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 012f31eba20c..90bbd47b02a1 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarx"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Noma’lum bo‘yiga"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Noma’lum eniga"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Bekor qilindi"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Administrator tomonidan yangilangan"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administrator tomonidan o‘chirilgan"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Quvvat tejash funksiyasi Tungi mavzuni va cheklovlarni yoqadi va fondagi harakatlar, vizual effektlar va “Ok Google” kabi boshqa funksiyalarni faolsizlantiradi yoki cheklaydi.\n\n"<annotation id="url">"Batafsil"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Quvvat tejash funksiyasi Tungi mavzuni va cheklovlarni yoqadi va fondagi harakatlar, vizual effektlar va “Ok Google” kabi boshqa funksiyalarni faolsizlantiradi yoki cheklaydi."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Trafik tejash rejimida ayrim ilovalar uchun orqa fonda internetdan foydalanish imkoniyati cheklanadi. Siz ishlatayotgan ilova zaruratga qarab internet-trafik sarflashi mumkin, biroq cheklangan miqdorda. Masalan, rasmlar ustiga bosmaguningizcha ular yuklanmaydi."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Trafik tejash yoqilsinmi?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Yoqish"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index ccbcd0adbb67..44247332d037 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Giấy khổ rộng"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Khổ dọc không xác định"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Khổ ngang không xác định"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Đã hủy"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Do quản trị viên của bạn cập nhật"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Do quản trị viên của bạn xóa"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Tính năng Tiết kiệm pin sẽ bật Giao diện tối, đồng thời tắt hoặc hạn chế hoạt động chạy trong nền, một số hiệu ứng hình ảnh và các tính năng như lệnh “Ok Google”\n\n"<annotation id="url">"Tìm hiểu thêm"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Tính năng Tiết kiệm pin sẽ bật Giao diện tối, đồng thời tắt hoặc hạn chế hoạt động chạy trong nền, một số hiệu ứng hình ảnh và các tính năng như lệnh “Ok Google”."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Để giúp giảm mức sử dụng dữ liệu, Trình tiết kiệm dữ liệu sẽ chặn một số ứng dụng gửi hoặc nhận dữ liệu trong nền. Ứng dụng mà bạn hiện sử dụng có thể dùng dữ liệu nhưng tần suất sẽ giảm. Ví dụ: hình ảnh sẽ không hiển thị cho đến khi bạn nhấn vào hình ảnh đó."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Bật Trình tiết kiệm dữ liệu?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Bật"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index d7f316577e5f..2ffca206106e 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"未知(纵向)"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"未知(横向)"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"已取消"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"已由您的管理员更新"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"已由您的管理员删除"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"确定"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"省电模式会开启深色主题,并限制或关闭后台活动、部分视觉效果和“Ok Google”等功能\n\n"<annotation id="url">"了解详情"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"省电模式会开启深色主题,并限制或关闭后台活动、部分视觉效果和“Ok Google”等功能。"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"为了减少流量消耗,流量节省程序会阻止某些应用在后台收发数据。您当前使用的应用可以收发数据,但频率可能会降低。举例而言,这可能意味着图片只有在您点按之后才会显示。"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"要开启流量节省程序吗?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"开启"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 858c179ceb25..30619859af87 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -1773,6 +1773,17 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <string name="mediasize_na_ansi_c" msgid="3104916921818289618">"ANSI C"</string> + <string name="mediasize_na_ansi_d" msgid="254005964819282724">"ANSI D"</string> + <string name="mediasize_na_ansi_e" msgid="4424174989686785675">"ANSI E"</string> + <string name="mediasize_na_ansi_f" msgid="146980362213260987">"ANSI F"</string> + <string name="mediasize_na_arch_a" msgid="5280681822380032361">"Arch A"</string> + <string name="mediasize_na_arch_b" msgid="2113344093437297427">"Arch B"</string> + <string name="mediasize_na_arch_c" msgid="971002546186856623">"Arch C"</string> + <string name="mediasize_na_arch_d" msgid="6450996075335049806">"Arch D"</string> + <string name="mediasize_na_arch_e" msgid="6782708486949266519">"Arch E"</string> + <string name="mediasize_na_arch_e1" msgid="4707138568738504275">"Arch E1"</string> + <string name="mediasize_na_super_b" msgid="6964127155618393178">"Super B"</string> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1820,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"不明直向紙張"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"不明橫向紙張"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"已取消"</string> @@ -1852,8 +1865,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"已由您的管理員更新"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"已由您的管理員刪除"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"好"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"「省電模式」會開啟深色主題背景並限制或關閉背景活動、部分視覺效果,以及「Ok Google」啟動字詞等功能。\n\n"<annotation id="url">"瞭解詳情"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"「省電模式」會開啟深色主題背景並限制或關閉背景活動、部分視覺效果,以及「Ok Google」啟動字詞等功能。"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。您正在使用的應用程式可存取資料,但次數可能會減少。例如,圖片可能需要輕按才會顯示。"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟「數據節省模式」嗎?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 8770db7b02c6..6239b51fcd65 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"不明縱向紙張"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"不明橫向紙張"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"已取消"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"已由你的管理員更新"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"已由你的管理員刪除"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"確定"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"省電模式會開啟深色主題並限制或關閉背景活動、部分視覺效果,以及「Ok Google」啟動字詞等功能\n\n"<annotation id="url">"瞭解詳情"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"省電模式會開啟深色主題並限制或關閉背景活動、部分視覺效果,以及「Ok Google」啟動字詞等功能。"</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。你目前使用的應用程式可以存取資料,但存取頻率可能不如平時高。舉例來說,圖片可能不會自動顯示,在你輕觸後才會顯示。"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟數據節省模式嗎?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 4a912d4b9784..18481fce4181 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1773,6 +1773,28 @@ <string name="mediasize_na_monarch" msgid="4396943937986136896">"I-Monarch"</string> <string name="mediasize_na_quarto" msgid="2119101847712239885">"I-Quarto"</string> <string name="mediasize_na_foolscap" msgid="5011612828564394648">"I-Foolscap"</string> + <!-- no translation found for mediasize_na_ansi_c (3104916921818289618) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_d (254005964819282724) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_e (4424174989686785675) --> + <skip /> + <!-- no translation found for mediasize_na_ansi_f (146980362213260987) --> + <skip /> + <!-- no translation found for mediasize_na_arch_a (5280681822380032361) --> + <skip /> + <!-- no translation found for mediasize_na_arch_b (2113344093437297427) --> + <skip /> + <!-- no translation found for mediasize_na_arch_c (971002546186856623) --> + <skip /> + <!-- no translation found for mediasize_na_arch_d (6450996075335049806) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e (6782708486949266519) --> + <skip /> + <!-- no translation found for mediasize_na_arch_e1 (4707138568738504275) --> + <skip /> + <!-- no translation found for mediasize_na_super_b (6964127155618393178) --> + <skip /> <string name="mediasize_chinese_roc_8k" msgid="411670106572707544">"I-ROC 8K"</string> <string name="mediasize_chinese_roc_16k" msgid="7496706798725321898">"I-ROC 16K"</string> <string name="mediasize_chinese_prc_1" msgid="946949835711037253">"I-PRC 1"</string> @@ -1809,6 +1831,8 @@ <string name="mediasize_japanese_kahu" msgid="7290232592648122042">"I-Kahu"</string> <string name="mediasize_japanese_kaku2" msgid="7477551750461028312">"I-Kaku2"</string> <string name="mediasize_japanese_you4" msgid="5552111912684384833">"I-You4"</string> + <!-- no translation found for mediasize_japanese_l (1326765321473431817) --> + <skip /> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"Ukuma ngobude obungaziwa"</string> <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"Ukwakheka kwezwe okungaziwa"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"Kukhanseliwe"</string> @@ -1852,8 +1876,10 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Kubuyekezwe umlawuli wakho"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Kususwe umlawuli wakho"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"KULUNGILE"</string> - <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Isilondolozi Sebhethri sivula Itimu Emnyama futhi sikhawulele noma sivale umsebenzi wendawo engemuva, izakhi ezithile ezibonakalayo, nezakhi ezinjenge-\"Ok Google\"\n\n"<annotation id="url">"Funda kabanzi"</annotation></string> - <string name="battery_saver_description" msgid="7695751399533397741">"Isilondolozi Sebhethri sivula Itimu Emnyama futhi sikhawulele noma sivale umsebenzi wendawo engemuva, izakhi ezithile ezibonakalayo, nezakhi ezinjenge-\"Ok Google\"."</string> + <!-- no translation found for battery_saver_description_with_learn_more (750683025714899363) --> + <skip /> + <!-- no translation found for battery_saver_description (5693741424234005958) --> + <skip /> <string name="data_saver_description" msgid="4995164271550590517">"Ukusiza ukwehlisa ukusetshenziswa kwedatha, iseva yedatha igwema ezinye izinhlelo zokusebenza ukuthi zithumele noma zamukele idatha ngasemuva. Uhlelo lokusebenza olisebenzisa okwamanje lingafinyelela idatha, kodwa lingenza kanjalo kancane. Lokhu kungachaza, isibonelo, ukuthi izithombe azibonisi uze uzithephe."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Vula iseva yedatha?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Vula"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 529aa6641c68..fbc96783597d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -657,6 +657,12 @@ <!-- Indicate the display area rect for foldable devices in folded state. --> <string name="config_foldedArea"></string> + <!-- Indicates that the device supports having more than one internal display on at the same + time. Only applicable to devices with more than one internal display. If this option is + set to false, DisplayManager will make additional effort to ensure no more than 1 internal + display is powered on at the same time. --> + <bool name="config_supportsConcurrentInternalDisplays">true</bool> + <!-- Desk dock behavior --> <!-- The number of degrees to rotate the display when the device is in a desk dock. @@ -4854,17 +4860,30 @@ <!-- Whether app hibernation deletes OAT artifact files as part of global hibernation. --> <bool name="config_hibernationDeletesOatArtifactsEnabled">true</bool> - <!-- On-device intelligent processor for system UI features. --> + <!-- Package name of the on-device intelligent processor for system UI + features. Examples include the search functionality or the app + predictor. --> <string name="config_systemUiIntelligence" translatable="false"></string> - <!-- On-device intelligent processor for ambient audio. --> + <!-- Package name of the on-device intelligent processor for ambient audio. + Ambient audio is the sound surrounding the device captured by the DSP + or the microphone. In other words, the device is continuously + processing audio data in background. --> <string name="config_systemAmbientAudioIntelligence" translatable="false"></string> - <!-- On-device intelligent processor for audio. --> + <!-- Package name of the on-device intelligent processor for audio. The + difference of 'ambient audio' and 'audio' is that in 'audio', the + user intentionally and consciously aware that the device is recording + or using the microphone. + --> <string name="config_systemAudioIntelligence" translatable="false"></string> - <!-- On-device intelligent processor for notification. --> + <!-- Package name of the on-device intelligent processor for notification. + --> <string name="config_systemNotificationIntelligence" translatable="false"></string> - <!-- On-device intelligent processor for text. --> + <!-- Package name of the on-device intelligent processor for text. Examples + include providing autofill functionality based on incoming text + messages. --> <string name="config_systemTextIntelligence" translatable="false"></string> - <!-- On-device intelligent processor for visual features. --> + <!-- Package name of the on-device intelligent processor for visual + features. Examples include the autorotate feature. --> <string name="config_systemVisualIntelligence" translatable="false"></string> <!-- On-device package for providing companion device associations. --> <string name="config_systemCompanionDeviceProvider" translatable="false"></string> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index cc347f9b2c8d..fd33ccdfb8c5 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5539,19 +5539,19 @@ <!-- Content description of the demoted feedback icon in the notification. [CHAR LIMIT=NONE] --> <string name="notification_feedback_indicator_demoted">This notification was ranked lower. Tap to provide feedback.</string> - <!-- Notification Intelligence --> - <!-- Title of notification indicating notification intelligence settings have changed when upgrading to S [CHAR LIMIT=30] --> - <string name="nas_upgrade_notification_title">Try enhanced notifications</string> + <!-- Enhanced Notifications --> + <!-- Title of notification indicating adaptive notifications setting need migration when upgrading to S [CHAR LIMIT=30] --> + <string name="nas_upgrade_notification_title">Enhanced notifications</string> <!-- Content of notification indicating users need to update the settings [CHAR LIMIT=NONE] --> - <string name="nas_upgrade_notification_content">To keep getting suggested actions, replies, and more, turn on enhanced notifications. Android Adaptive Notifications are no longer supported.</string> - <!-- Label of notification action button to turn on the notification intelligence [CHAR LIMIT=20] --> - <string name="nas_upgrade_notification_enable_action">Turn on</string> - <!-- Label of notification action button to turn off the notification intelligence [CHAR LIMIT=20] --> - <string name="nas_upgrade_notification_disable_action">Not now</string> - <!-- Label of notification action button to learn more about the notification intelligence settings [CHAR LIMIT=20] --> + <string name="nas_upgrade_notification_content">Suggested actions and replies are now provided by enhanced notifications. Android Adaptive Notifications are no longer supported.</string> + <!-- Label of notification action button to turn on the enhanced notifications [CHAR LIMIT=20] --> + <string name="nas_upgrade_notification_enable_action">OK</string> + <!-- Label of notification action button to turn off the enhanced notifications [CHAR LIMIT=20] --> + <string name="nas_upgrade_notification_disable_action">Turn off</string> + <!-- Label of notification action button to learn more about the enhanced notifications [CHAR LIMIT=20] --> <string name="nas_upgrade_notification_learn_more_action">Learn more</string> - <!-- Content of notification learn more dialog about the notification intelligence settings [CHAR LIMIT=NONE] --> - <string name="nas_upgrade_notification_learn_more_content">Enhanced notifications can read all notification content, including personal information like contact names and messages. This feature can also dismiss notifications or take actions on buttons in notifications, such as answering phone calls.\n\nThis feature can also turn Priority mode on or off and change related settings.</string> + <!-- Content of notification learn more dialog about the enhanced notifications [CHAR LIMIT=NONE] --> + <string name="nas_upgrade_notification_learn_more_content">Enhanced notifications replaced Android Adaptive Notifications in Android 12. This feature shows suggested actions and replies, and organizes your notifications.\n\nEnhanced notifications can access notification content, including personal information like contact names and messages. This feature can also dismiss or respond to notifications, such as answering phone calls and controlling Do Not Disturb.</string> <!-- Dynamic mode battery saver strings --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 78a794a35bba..9ac9e8e0ffa7 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3800,6 +3800,7 @@ <!-- For Foldables --> <java-symbol type="array" name="config_foldedDeviceStates" /> <java-symbol type="string" name="config_foldedArea" /> + <java-symbol type="bool" name="config_supportsConcurrentInternalDisplays" /> <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" /> <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" /> diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetrics.java b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetrics.java index 0f3bb1db6ed9..81ec3f37c465 100644 --- a/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetrics.java +++ b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetrics.java @@ -58,11 +58,8 @@ public class PowerMetrics { for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) { - totalPowerPerComponentMah[component] += uidBatteryConsumer.getConsumedPower( - component); - } - - for (int component = 0; component < BatteryConsumer.TIME_COMPONENT_COUNT; component++) { + totalPowerPerComponentMah[component] += + uidBatteryConsumer.getConsumedPower(component); totalDurationPerComponentMs[component] += uidBatteryConsumer.getUsageDurationMillis(component); } @@ -76,18 +73,15 @@ public class PowerMetrics { addMetric(getPowerMetricName(component), MetricKind.POWER, selectedBatteryConsumer.getConsumedPower(component), totalPowerPerComponentMah[component]); - } - - for (int component = 0; component < BatteryConsumer.TIME_COMPONENT_COUNT; component++) { - addMetric(getTimeMetricName(component), MetricKind.DURATION, + addMetric(getDurationMetricName(component), MetricKind.DURATION, selectedBatteryConsumer.getUsageDurationMillis(component), totalDurationPerComponentMs[component]); } } - static String getTimeMetricName(int componentId) { - return "TIME_" + DebugUtils.constantToString(BatteryConsumer.class, - "TIME_COMPONENT_", componentId); + static String getDurationMetricName(int componentId) { + return "DURATION_" + DebugUtils.constantToString(BatteryConsumer.class, + "POWER_COMPONENT_", componentId); } static String getPowerMetricName(int componentId) { diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetricsCollector.java b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetricsCollector.java index 5b5da603b199..6fc10dd5264a 100644 --- a/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetricsCollector.java +++ b/core/tests/batterystatstests/BatteryStatsLoadTests/src/com/android/frameworks/core/batterystatsloadtests/PowerMetricsCollector.java @@ -306,8 +306,8 @@ public class PowerMetricsCollector implements TestRule { return null; } - public PowerMetrics.Metric getTimeMetric(@BatteryConsumer.TimeComponent int component) { - final String name = PowerMetrics.getTimeMetricName(component); + public PowerMetrics.Metric getTimeMetric(@BatteryConsumer.PowerComponent int component) { + final String name = PowerMetrics.getDurationMetricName(component); for (PowerMetrics.Metric metric : mPowerMetricsDelta) { if (metric.metricName.equals(name)) { return metric; diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml index 52a77a7862a1..1b1f64afcbae 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml @@ -27,6 +27,7 @@ android:label="Battery Stats Viewer"> <activity android:name=".BatteryConsumerPickerActivity" android:label="Battery Stats" + android:icon="@mipmap/ic_launcher" android:launchMode="singleTop" android:exported="true"> <intent-filter> @@ -36,7 +37,6 @@ </activity> <activity android:name=".BatteryStatsViewerActivity" - android:icon="@mipmap/ic_launcher" android:label="Battery Stats" android:parentActivityName=".BatteryConsumerPickerActivity"/> </application> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java index f7d7098b1726..a15a8d8b3f47 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java @@ -68,7 +68,7 @@ public class BatteryConsumerData { double[] totalPowerByComponentMah = new double[BatteryConsumer.POWER_COMPONENT_COUNT]; double[] totalModeledPowerByComponentMah = new double[BatteryConsumer.POWER_COMPONENT_COUNT]; - long[] totalDurationByComponentMs = new long[BatteryConsumer.TIME_COMPONENT_COUNT]; + long[] totalDurationByComponentMs = new long[BatteryConsumer.POWER_COMPONENT_COUNT]; final int customComponentCount = requestedBatteryConsumer.getCustomPowerComponentCount(); double[] totalCustomPowerByComponentMah = new double[customComponentCount]; @@ -108,7 +108,7 @@ public class BatteryConsumerData { mBatteryConsumerInfo.isSystemBatteryConsumer); } - for (int component = 0; component < BatteryConsumer.TIME_COMPONENT_COUNT; component++) { + for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) { final String metricTitle = getTimeMetricTitle(component); addEntry(metricTitle, EntryType.DURATION, requestedBatteryConsumer.getUsageDurationMillis(component), @@ -141,7 +141,7 @@ public class BatteryConsumerData { static String getTimeMetricTitle(int componentId) { final String componentName = DebugUtils.constantToString(BatteryConsumer.class, - "TIME_COMPONENT_", componentId); + "POWER_COMPONENT_", componentId); return componentName.charAt(0) + componentName.substring(1).toLowerCase().replace('_', ' ') + " time"; } @@ -159,7 +159,7 @@ public class BatteryConsumerData { private void computeTotalDuration(BatteryUsageStats batteryUsageStats, long[] durationByComponentMs) { for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) { - for (int component = 0; component < BatteryConsumer.TIME_COMPONENT_COUNT; + for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) { durationByComponentMs[component] += consumer.getUsageDurationMillis(component); } diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 2e2e6bd07539..6f17ea994699 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -185,15 +185,6 @@ public class ActivityThreadTest { } @Test - public void testHandleActivity_assetsChanged() { - relaunchActivityAndAssertPreserveWindow(activity -> { - // Relaunches all activities. - activity.getActivityThread().handleApplicationInfoChanged( - activity.getApplicationInfo()); - }); - } - - @Test public void testRecreateActivity() { relaunchActivityAndAssertPreserveWindow(Activity::recreate); } diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java index b2b9ab31282c..c9a18dafe11d 100644 --- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java +++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java @@ -30,6 +30,7 @@ import static android.app.admin.PasswordMetrics.validatePasswordMetrics; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -284,33 +285,42 @@ public class PasswordMetricsTest { PasswordMetrics none = new PasswordMetrics(CREDENTIAL_TYPE_NONE); PasswordMetrics pattern = new PasswordMetrics(CREDENTIAL_TYPE_PATTERN); PasswordMetrics password = new PasswordMetrics(CREDENTIAL_TYPE_PASSWORD); + PasswordMetrics pin = new PasswordMetrics(CREDENTIAL_TYPE_PIN); // To pass minimal length check. password.length = 4; + pin.length = 4; // No errors expected, credential is of stronger or equal type. assertValidationErrors( - validatePasswordMetrics(none, PASSWORD_COMPLEXITY_NONE, false, none)); + validatePasswordMetrics(none, PASSWORD_COMPLEXITY_NONE, none)); assertValidationErrors( - validatePasswordMetrics(none, PASSWORD_COMPLEXITY_NONE, false, pattern)); + validatePasswordMetrics(none, PASSWORD_COMPLEXITY_NONE, pattern)); assertValidationErrors( - validatePasswordMetrics(none, PASSWORD_COMPLEXITY_NONE, false, password)); + validatePasswordMetrics(none, PASSWORD_COMPLEXITY_NONE, password)); assertValidationErrors( - validatePasswordMetrics(pattern, PASSWORD_COMPLEXITY_NONE, false, pattern)); + validatePasswordMetrics(none, PASSWORD_COMPLEXITY_NONE, pin)); assertValidationErrors( - validatePasswordMetrics(pattern, PASSWORD_COMPLEXITY_NONE, false, password)); + validatePasswordMetrics(pattern, PASSWORD_COMPLEXITY_NONE, pattern)); assertValidationErrors( - validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, false, password)); + validatePasswordMetrics(pattern, PASSWORD_COMPLEXITY_NONE, password)); + assertValidationErrors( + validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, password)); + assertValidationErrors( + validatePasswordMetrics(pin, PASSWORD_COMPLEXITY_NONE, pin)); // Now actual credential type is weaker than required: assertValidationErrors( - validatePasswordMetrics(pattern, PASSWORD_COMPLEXITY_NONE, false, none), + validatePasswordMetrics(pattern, PASSWORD_COMPLEXITY_NONE, none), + PasswordValidationError.WEAK_CREDENTIAL_TYPE, 0); + assertValidationErrors( + validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, none), PasswordValidationError.WEAK_CREDENTIAL_TYPE, 0); assertValidationErrors( - validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, false, none), + validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, pattern), PasswordValidationError.WEAK_CREDENTIAL_TYPE, 0); assertValidationErrors( - validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, false, pattern), + validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, pin), PasswordValidationError.WEAK_CREDENTIAL_TYPE, 0); } diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java index 36da927116b7..3e2c4e98e21a 100644 --- a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java +++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java @@ -234,6 +234,7 @@ public class PeopleSpaceTileTest { .setIsImportantConversation(true) .setStatuses(statusList).setNotificationKey("key") .setNotificationContent("content") + .setNotificationSender("sender") .setNotificationDataUri(Uri.parse("data")) .setMessagesCount(2) .setIntent(new Intent()) @@ -256,6 +257,7 @@ public class PeopleSpaceTileTest { assertThat(readTile.getStatuses()).isEqualTo(tile.getStatuses()); assertThat(readTile.getNotificationKey()).isEqualTo(tile.getNotificationKey()); assertThat(readTile.getNotificationContent()).isEqualTo(tile.getNotificationContent()); + assertThat(readTile.getNotificationSender()).isEqualTo(tile.getNotificationSender()); assertThat(readTile.getNotificationDataUri()).isEqualTo(tile.getNotificationDataUri()); assertThat(readTile.getMessagesCount()).isEqualTo(tile.getMessagesCount()); assertThat(readTile.getIntent().toString()).isEqualTo(tile.getIntent().toString()); @@ -282,6 +284,16 @@ public class PeopleSpaceTileTest { } @Test + public void testNotificationSender() { + PeopleSpaceTile tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setNotificationSender("test") + .build(); + + assertThat(tile.getNotificationSender()).isEqualTo("test"); + } + + @Test public void testNotificationDataUri() { PeopleSpaceTile tile = new PeopleSpaceTile.Builder(new ShortcutInfo.Builder(mContext, "123").build(), diff --git a/core/tests/coretests/src/android/app/time/TimeZoneConfigurationTest.java b/core/tests/coretests/src/android/app/time/TimeZoneConfigurationTest.java index 3948eb86c4f6..17d249263034 100644 --- a/core/tests/coretests/src/android/app/time/TimeZoneConfigurationTest.java +++ b/core/tests/coretests/src/android/app/time/TimeZoneConfigurationTest.java @@ -16,11 +16,8 @@ package android.app.time; -import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import androidx.test.filters.SmallTest; @@ -29,25 +26,12 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +/** Also see {@link android.app.time.cts.TimeZoneConfigurationTest}, which covers the SDK APIs. */ @RunWith(AndroidJUnit4.class) @SmallTest public class TimeZoneConfigurationTest { @Test - public void testBuilder_copyConstructor() { - TimeZoneConfiguration.Builder builder1 = - new TimeZoneConfiguration.Builder() - .setAutoDetectionEnabled(true) - .setGeoDetectionEnabled(true); - TimeZoneConfiguration configuration1 = builder1.build(); - - TimeZoneConfiguration configuration2 = - new TimeZoneConfiguration.Builder(configuration1).build(); - - assertEquals(configuration1, configuration2); - } - - @Test public void testIntrospectionMethods() { TimeZoneConfiguration empty = new TimeZoneConfiguration.Builder().build(); assertFalse(empty.isComplete()); @@ -90,84 +74,4 @@ public class TimeZoneConfigurationTest { assertEquals(configuration2, merged1And2); } } - - @Test - public void testEquals() { - TimeZoneConfiguration.Builder builder1 = - new TimeZoneConfiguration.Builder(); - { - TimeZoneConfiguration one = builder1.build(); - assertEquals(one, one); - } - - TimeZoneConfiguration.Builder builder2 = - new TimeZoneConfiguration.Builder(); - { - TimeZoneConfiguration one = builder1.build(); - TimeZoneConfiguration two = builder2.build(); - assertEquals(one, two); - assertEquals(two, one); - } - - builder1.setAutoDetectionEnabled(true); - { - TimeZoneConfiguration one = builder1.build(); - TimeZoneConfiguration two = builder2.build(); - assertNotEquals(one, two); - } - - builder2.setAutoDetectionEnabled(false); - { - TimeZoneConfiguration one = builder1.build(); - TimeZoneConfiguration two = builder2.build(); - assertNotEquals(one, two); - } - - builder1.setAutoDetectionEnabled(false); - { - TimeZoneConfiguration one = builder1.build(); - TimeZoneConfiguration two = builder2.build(); - assertEquals(one, two); - } - - builder1.setGeoDetectionEnabled(true); - { - TimeZoneConfiguration one = builder1.build(); - TimeZoneConfiguration two = builder2.build(); - assertNotEquals(one, two); - } - - builder2.setGeoDetectionEnabled(false); - { - TimeZoneConfiguration one = builder1.build(); - TimeZoneConfiguration two = builder2.build(); - assertNotEquals(one, two); - } - - builder1.setGeoDetectionEnabled(false); - { - TimeZoneConfiguration one = builder1.build(); - TimeZoneConfiguration two = builder2.build(); - assertEquals(one, two); - } - } - - @Test - public void testParcelable() { - TimeZoneConfiguration.Builder builder = - new TimeZoneConfiguration.Builder(); - assertRoundTripParcelable(builder.build()); - - builder.setAutoDetectionEnabled(true); - assertRoundTripParcelable(builder.build()); - - builder.setAutoDetectionEnabled(false); - assertRoundTripParcelable(builder.build()); - - builder.setGeoDetectionEnabled(false); - assertRoundTripParcelable(builder.build()); - - builder.setGeoDetectionEnabled(true); - assertRoundTripParcelable(builder.build()); - } } diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java index c63ec45ed5a9..236c3daac272 100644 --- a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java @@ -65,7 +65,7 @@ public class AmbientDisplayPowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer( SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(90 * MINUTE_IN_MS); // 100,000,00 uC / 1000 (micro-/milli-) / 360 (seconds/hour) = 27.777778 mAh assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -91,7 +91,7 @@ public class AmbientDisplayPowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer( SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(90 * MINUTE_IN_MS); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(15.0); diff --git a/core/tests/coretests/src/com/android/internal/os/AudioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AudioPowerCalculatorTest.java index ed4638c1e7e0..c694d67cd97b 100644 --- a/core/tests/coretests/src/com/android/internal/os/AudioPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/AudioPowerCalculatorTest.java @@ -52,7 +52,7 @@ public class AudioPowerCalculatorTest { mStatsRule.apply(calculator); UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO)) .isEqualTo(1000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO)) .isWithin(PRECISION).of(0.1); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java index e0739be1b295..41fe372d5d9c 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java @@ -171,7 +171,7 @@ public class BatteryUsageStatsRule implements TestRule { final boolean includePowerModels = (query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( - customPowerComponentNames, 0, includePowerModels); + customPowerComponentNames, includePowerModels); SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats(); for (int i = 0; i < uidStats.size(); i++) { builder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i)); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java index b25359970c5a..55302bcf3b8d 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java @@ -67,7 +67,7 @@ public class BatteryUsageStatsTest { final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000); final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[]{"FOO"}, 1) + new BatteryUsageStats.Builder(new String[]{"FOO"}) .setDischargePercentage(20) .setDischargedPowerRange(1000, 2000) .setStatsStartTimestamp(1000); @@ -83,11 +83,9 @@ public class BatteryUsageStatsTest { .setConsumedPowerForCustomComponent( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500) .setUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_CPU, 600) - .setUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700) + BatteryConsumer.POWER_COMPONENT_CPU, 600) .setUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 800); + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 800); builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_CAMERA) .setConsumedPower( @@ -95,9 +93,9 @@ public class BatteryUsageStatsTest { .setConsumedPowerForCustomComponent( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200) .setUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_CPU, 10300) + BatteryConsumer.POWER_COMPONENT_CPU, 10300) .setUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400) + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400) .setPowerConsumedByApps(20000); return builder.build(); @@ -129,11 +127,9 @@ public class BatteryUsageStatsTest { assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(500); assertThat(uidBatteryConsumer.getUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(600); - assertThat(uidBatteryConsumer.getUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND)).isEqualTo(700); + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(600); assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(800); + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(800); assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1200); assertThat(uidBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1); assertThat(uidBatteryConsumer.getCustomPowerComponentName( @@ -152,9 +148,9 @@ public class BatteryUsageStatsTest { assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10200); assertThat(systemBatteryConsumer.getUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(10300); + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10300); assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(10400); + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10400); assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(20300); assertThat(systemBatteryConsumer.getPowerConsumedByApps()).isEqualTo(20000); assertThat(systemBatteryConsumer.getUsageDurationMillis()) diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java index 71cdb5fe17f0..8723195ab6fe 100644 --- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java @@ -147,7 +147,7 @@ public class BluetoothPowerCalculatorTest { .isEqualTo(powerModel); long usageDurationMillis = batteryConsumer.getUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_BLUETOOTH); + BatteryConsumer.POWER_COMPONENT_BLUETOOTH); assertThat(usageDurationMillis).isEqualTo(durationMs); } diff --git a/core/tests/coretests/src/com/android/internal/os/CameraPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CameraPowerCalculatorTest.java index a21dd58b0757..61eb173837e8 100644 --- a/core/tests/coretests/src/com/android/internal/os/CameraPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/CameraPowerCalculatorTest.java @@ -52,7 +52,7 @@ public class CameraPowerCalculatorTest { mStatsRule.apply(calculator); UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CAMERA)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isEqualTo(1000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.1); diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java index 63af21dcdd93..1a99fb0fd40f 100644 --- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java @@ -144,7 +144,7 @@ public class CpuPowerCalculatorTest { mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator); UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1); - assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU)) + assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU)) .isEqualTo(3333); assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(1.092233); @@ -153,7 +153,7 @@ public class CpuPowerCalculatorTest { assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar"); UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); - assertThat(uidConsumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU)) + assertThat(uidConsumer2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU)) .isEqualTo(7777); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(2.672322); @@ -208,7 +208,7 @@ public class CpuPowerCalculatorTest { mStatsRule.apply(calculator); UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1); - assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU)) + assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU)) .isEqualTo(3333); assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(3.18877); @@ -217,7 +217,7 @@ public class CpuPowerCalculatorTest { assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar"); UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); - assertThat(uidConsumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU)) + assertThat(uidConsumer2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU)) .isEqualTo(7777); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(7.44072); diff --git a/core/tests/coretests/src/com/android/internal/os/FlashlightPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/FlashlightPowerCalculatorTest.java index b7bbedd9cdb7..98d5aacd7f8e 100644 --- a/core/tests/coretests/src/com/android/internal/os/FlashlightPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/FlashlightPowerCalculatorTest.java @@ -52,7 +52,7 @@ public class FlashlightPowerCalculatorTest { mStatsRule.apply(calculator); UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_FLASHLIGHT)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) .isEqualTo(1000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) .isWithin(PRECISION).of(0.1); diff --git a/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java index aa066c36cd4b..7ea799ff5173 100644 --- a/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java @@ -56,7 +56,7 @@ public class GnssPowerCalculatorTest { mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator); UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_GNSS)) .isEqualTo(1000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(0.1); @@ -83,7 +83,7 @@ public class GnssPowerCalculatorTest { mStatsRule.apply(calculator); UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_GNSS)) .isEqualTo(1000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(2.77777); @@ -91,7 +91,7 @@ public class GnssPowerCalculatorTest { .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); UidBatteryConsumer consumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); - assertThat(consumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS)) + assertThat(consumer2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_GNSS)) .isEqualTo(2000); assertThat(consumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(5.55555); diff --git a/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java index a9800b7e6b75..2331eebf6dd1 100644 --- a/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java @@ -48,7 +48,7 @@ public class IdlePowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_IDLE); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_IDLE)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_IDLE)) .isEqualTo(3000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_IDLE)) .isWithin(PRECISION).of(0.7); diff --git a/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java index 71dbcdbf7f46..94e760a4e2e2 100644 --- a/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java @@ -55,7 +55,7 @@ public class MemoryPowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MEMORY); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MEMORY)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MEMORY)) .isEqualTo(3000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MEMORY)) .isWithin(PRECISION).of(0.7); diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java index 7d829e44d9ec..93c71068457e 100644 --- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java @@ -84,7 +84,7 @@ public class ScreenPowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(80 * MINUTE_IN_MS); // 600000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 166.66666 mAh @@ -98,7 +98,7 @@ public class ScreenPowerCalculatorTest { .isWithin(PRECISION).of(166.66666); UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1); - assertThat(uid1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(20 * MINUTE_IN_MS); // Uid1 took all of the foreground time during the first Display update. @@ -110,7 +110,7 @@ public class ScreenPowerCalculatorTest { .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); - assertThat(uid2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(60 * MINUTE_IN_MS); // Uid2 ran for 40 minutes out of the total 45 min of foreground time during the second @@ -153,7 +153,7 @@ public class ScreenPowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(80 * MINUTE_IN_MS); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(92.0); @@ -165,7 +165,7 @@ public class ScreenPowerCalculatorTest { .isWithin(PRECISION).of(92.0); UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1); - assertThat(uid1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(20 * MINUTE_IN_MS); // Uid1 took 20 out of the total of 80 min of foreground activity @@ -176,7 +176,7 @@ public class ScreenPowerCalculatorTest { .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); - assertThat(uid2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) + assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(60 * MINUTE_IN_MS); // Uid2 took 60 out of the total of 80 min of foreground activity diff --git a/core/tests/coretests/src/com/android/internal/os/SensorPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SensorPowerCalculatorTest.java index b50435bd2928..74235b28d43b 100644 --- a/core/tests/coretests/src/com/android/internal/os/SensorPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SensorPowerCalculatorTest.java @@ -70,7 +70,7 @@ public class SensorPowerCalculatorTest { mStatsRule.apply(calculator); UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SENSORS)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SENSORS)) .isEqualTo(3000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SENSORS)) .isWithin(PRECISION).of(0.5); diff --git a/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java index 6fa1d3bae0f7..aae69d7b5bc9 100644 --- a/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java @@ -58,16 +58,16 @@ public class UserPowerCalculatorTest { assertThat(mStatsRule.getUserBatteryConsumer(USER1)).isNull(); assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1)) - .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(3000); + .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO)).isEqualTo(3000); assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1)) - .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(7000); + .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO)).isEqualTo(7000); assertThat(mStatsRule.getUserBatteryConsumer(USER2)).isNull(); assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2)) - .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(5555); + .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO)).isEqualTo(5555); assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2)) - .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(9999); + .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO)).isEqualTo(9999); } @Test @@ -82,19 +82,19 @@ public class UserPowerCalculatorTest { assertThat(mStatsRule.getUserBatteryConsumer(USER1)).isNull(); assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1)) - .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(3000); + .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO)).isEqualTo(3000); assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1)) - .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(7000); + .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO)).isEqualTo(7000); assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID2))).isNull(); assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID3)) - .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(7070); + .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO)).isEqualTo(7070); assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID3)) - .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(11110); + .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO)).isEqualTo(11110); UserBatteryConsumer user2 = mStatsRule.getUserBatteryConsumer(USER2); - assertThat(user2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)) + assertThat(user2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO)) .isEqualTo(15308); - assertThat(user2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)) + assertThat(user2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO)) .isEqualTo(24196); assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID1))).isNull(); @@ -130,7 +130,7 @@ public class UserPowerCalculatorTest { protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { long durationMs = u.getAudioTurnedOnTimer().getTotalTimeLocked(rawRealtimeUs, 0); - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO, durationMs / 1000); + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO, durationMs / 1000); } } @@ -139,7 +139,7 @@ public class UserPowerCalculatorTest { protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { long durationMs = u.getVideoTurnedOnTimer().getTotalTimeLocked(rawRealtimeUs, 0); - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO, durationMs / 1000); + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO, durationMs / 1000); } } } diff --git a/core/tests/coretests/src/com/android/internal/os/VideoPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/VideoPowerCalculatorTest.java index 39eac49400ec..fa0dbc76d5c6 100644 --- a/core/tests/coretests/src/com/android/internal/os/VideoPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/VideoPowerCalculatorTest.java @@ -52,7 +52,7 @@ public class VideoPowerCalculatorTest { mStatsRule.apply(calculator); UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO)) .isEqualTo(1000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_VIDEO)) .isWithin(PRECISION).of(0.1); diff --git a/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java index 4f71b438c6fa..9d3ed55a0729 100644 --- a/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java @@ -62,13 +62,13 @@ public class WakelockPowerCalculatorTest { mStatsRule.apply(calculator); UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK)) .isEqualTo(1000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK)) .isWithin(PRECISION).of(0.1); UidBatteryConsumer osConsumer = mStatsRule.getUidBatteryConsumer(Process.ROOT_UID); - assertThat(osConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK)) + assertThat(osConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK)) .isEqualTo(5000); assertThat(osConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK)) .isWithin(PRECISION).of(0.5); diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java index 9349bce2c383..4a7cf1e21abe 100644 --- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java @@ -87,7 +87,7 @@ public class WifiPowerCalculatorTest { mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI)) .isEqualTo(1423); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.2214666); @@ -96,7 +96,7 @@ public class WifiPowerCalculatorTest { SystemBatteryConsumer systemConsumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); - assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI)) .isEqualTo(5577); assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(1.11153); @@ -117,7 +117,7 @@ public class WifiPowerCalculatorTest { mStatsRule.apply(calculator); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI)) .isEqualTo(1423); /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) @@ -127,7 +127,7 @@ public class WifiPowerCalculatorTest { SystemBatteryConsumer systemConsumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); - assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI)) .isEqualTo(5577); /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */ assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) @@ -162,7 +162,7 @@ public class WifiPowerCalculatorTest { mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI)) .isEqualTo(1000); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.8231573); @@ -171,7 +171,7 @@ public class WifiPowerCalculatorTest { SystemBatteryConsumer systemConsumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); - assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI)) .isEqualTo(2222); assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(2.575000); @@ -193,7 +193,7 @@ public class WifiPowerCalculatorTest { mStatsRule.apply(calculator); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI)) .isEqualTo(1000); /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) @@ -203,7 +203,7 @@ public class WifiPowerCalculatorTest { SystemBatteryConsumer systemConsumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); - assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI)) .isEqualTo(2222); /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */ assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk index cba9cbd79f7b..d9e61512115d 100644 --- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk +++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk @@ -24,6 +24,9 @@ LOCAL_STATIC_JAVA_LIBRARIES := android-common mockwebserver junit LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_PACKAGE_NAME := DownloadManagerTestApp +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_PRIVATE_PLATFORM_APIS := true ifneq ($(TARGET_BUILD_VARIANT),user) diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk index cc2d8d256e6a..2d8556f2a3c7 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk @@ -26,6 +26,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SDK_VERSION := 8 LOCAL_PACKAGE_NAME := MultiDexLegacyAndException +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_DEX_PREOPT := false diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk index fe7c944ebd30..d7af2d9c3cfe 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk @@ -26,6 +26,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SDK_VERSION := 8 LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_DEX_PREOPT := false @@ -59,6 +62,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SDK_VERSION := 8 LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp2 +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_DEX_PREOPT := false diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.mk index 69ec5ceb1daf..236c7403aa8c 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.mk @@ -26,6 +26,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SDK_VERSION := 8 LOCAL_PACKAGE_NAME := MultiDexLegacyTestAppTests +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_DEX_PREOPT := false diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk index 2dc30ea7c17b..6f6ccfe8dee7 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk @@ -26,6 +26,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SDK_VERSION := current LOCAL_PACKAGE_NAME := MultiDexLegacyTestAppTests2 +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_DEX_PREOPT := false @@ -49,6 +52,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SDK_VERSION := 8 LOCAL_PACKAGE_NAME := MultiDexLegacyTestAppTests2-multidex +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_DEX_PREOPT := false diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk index a6f87ea2e5df..33a46ea980c1 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk @@ -23,6 +23,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SDK_VERSION := 18 LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp_corrupted +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk index 3636c73ffc9c..efc06886bee4 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk @@ -23,6 +23,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SDK_VERSION := 9 LOCAL_PACKAGE_NAME := MultiDexLegacyTestServices +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk index afbcd4675a78..3920fd64b03e 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk @@ -21,6 +21,9 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := MultiDexLegacyTestServicesTests2 +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_JAVA_LIBRARIES := android-support-multidex LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules @@ -30,4 +33,3 @@ LOCAL_SDK_VERSION := 9 LOCAL_DEX_PREOPT := false include $(BUILD_PACKAGE) - diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk index 67f1fa574c07..2323ad98ac3d 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk @@ -23,6 +23,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SDK_VERSION := 9 LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v1 +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk index 33871e527820..79a59063f702 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk @@ -23,6 +23,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SDK_VERSION := 9 LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v2 +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk index 1b267ee93cce..521bad058693 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk @@ -23,6 +23,9 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SDK_VERSION := 9 LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v3 +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex @@ -43,4 +46,3 @@ $(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DE echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@ $(built_dex_intermediate): $(mainDexList) - diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk index f3c0abd8293f..b453cde925e4 100644 --- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk +++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk @@ -19,6 +19,9 @@ my_package_prefix := com.android.server.om.hosttest.signature_overlay include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_NonPlatformSignatureOverlay +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_bad @@ -27,6 +30,9 @@ include $(BUILD_PACKAGE) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureStaticOverlay +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_CERTIFICATE := platform @@ -37,6 +43,9 @@ include $(BUILD_PACKAGE) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureOverlay +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_CERTIFICATE := platform diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk index 878f05d57662..77fc887e9493 100644 --- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk +++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk @@ -18,6 +18,9 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under,src) LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules @@ -30,6 +33,9 @@ my_package_prefix := com.android.server.om.hosttest.framework_overlay include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV1 +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_CERTIFICATE := platform @@ -42,6 +48,9 @@ include $(BUILD_PACKAGE) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV2 +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_CERTIFICATE := platform @@ -56,6 +65,9 @@ my_package_prefix := com.android.server.om.hosttest.app_overlay include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV1 +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1 @@ -67,6 +79,9 @@ include $(BUILD_PACKAGE) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV2 +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2 diff --git a/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java b/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java index bdaf63021503..4cad535a3a51 100644 --- a/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java +++ b/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java @@ -19,7 +19,6 @@ package android.uwb; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -114,30 +113,6 @@ public class AdapterStateListenerTest { } @Test - public void testRegister_FirstRegisterFails() throws RemoteException { - AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); - AdapterStateCallback callback1 = mock(AdapterStateCallback.class); - AdapterStateCallback callback2 = mock(AdapterStateCallback.class); - - // Throw a remote exception whenever first registering - doThrow(mThrowRemoteException).when(mUwbAdapter).registerAdapterStateCallbacks(any()); - - adapterStateListener.register(getExecutor(), callback1); - verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any()); - - // No longer throw an exception, instead succeed - doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any()); - - // Register a different callback - adapterStateListener.register(getExecutor(), callback2); - verify(mUwbAdapter, times(2)).registerAdapterStateCallbacks(any()); - - // Ensure first callback was invoked again - verifyCallbackStateChangedInvoked(callback1, 2); - verifyCallbackStateChangedInvoked(callback2, 1); - } - - @Test public void testRegister_RegisterSameCallbackTwice() throws RemoteException { AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); AdapterStateCallback callback = mock(AdapterStateCallback.class); @@ -162,13 +137,6 @@ public class AdapterStateListenerTest { runViaExecutor(); } - @Test - public void testCallback_RunViaExecutor_Failure() throws RemoteException { - // Verify that the callbacks are invoked on the executor when there is a remote exception - doThrow(mThrowRemoteException).when(mUwbAdapter).registerAdapterStateCallbacks(any()); - runViaExecutor(); - } - private void runViaExecutor() { AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter); AdapterStateCallback callback = mock(AdapterStateCallback.class); diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 7ed791d6ca71..9b9511b15082 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -367,12 +367,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" }, - "-1729340764": { - "message": "setFinishTaskBounds(%d): bounds=%s", - "level": "DEBUG", - "group": "WM_DEBUG_RECENTS_ANIMATIONS", - "at": "com\/android\/server\/wm\/RecentsAnimationController.java" - }, "-1715268616": { "message": "Last window, removing starting window %s", "level": "VERBOSE", @@ -1705,6 +1699,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-163974242": { + "message": "setFinishTaskTransaction(%d): transaction=%s", + "level": "DEBUG", + "group": "WM_DEBUG_RECENTS_ANIMATIONS", + "at": "com\/android\/server\/wm\/RecentsAnimationController.java" + }, "-143556958": { "message": "resumeNextFocusableActivityWhenRootTaskIsEmpty: %s, go home", "level": "DEBUG", @@ -1897,12 +1897,6 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimationController.java" }, - "86989930": { - "message": "setTaskWindowingMode: moving task=%d to windowingMode=%d toTop=%b", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, "90764070": { "message": "Could not report token removal to the window token client.", "level": "WARN", diff --git a/data/keyboards/Vendor_054c_Product_0268.kl b/data/keyboards/Vendor_054c_Product_0268.kl index b463dd84e75b..08d1c34f9eba 100644 --- a/data/keyboards/Vendor_054c_Product_0268.kl +++ b/data/keyboards/Vendor_054c_Product_0268.kl @@ -77,3 +77,11 @@ key 0x120 BUTTON_SELECT key 0x123 BUTTON_START # PS key key 0x2d0 BUTTON_MODE + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl index 3d93f0fec800..d281b4bb531e 100644 --- a/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl +++ b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl @@ -55,3 +55,11 @@ key 0x13a BUTTON_SELECT key 0x13b BUTTON_START # PS key key 0x13c BUTTON_MODE + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl index 3d93f0fec800..d281b4bb531e 100644 --- a/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl +++ b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl @@ -55,3 +55,11 @@ key 0x13a BUTTON_SELECT key 0x13b BUTTON_START # PS key key 0x13c BUTTON_MODE + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl index 5fe35f7faefa..3eafea0f6c31 100644 --- a/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl +++ b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl @@ -55,3 +55,11 @@ key 0x13a BUTTON_SELECT key 0x13b BUTTON_START # PS key key 0x13c BUTTON_MODE + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm index 9c55a2fb3bf1..53308e312aaa 100644 --- a/data/keyboards/Virtual.kcm +++ b/data/keyboards/Virtual.kcm @@ -25,12 +25,14 @@ key A { label: 'A' base: 'a' shift, capslock: 'A' + shift+capslock: 'a' } key B { label: 'B' base: 'b' shift, capslock: 'B' + shift+capslock: 'b' } key C { @@ -39,12 +41,14 @@ key C { shift, capslock: 'C' alt: '\u00e7' shift+alt: '\u00c7' + shift+capslock: 'c' } key D { label: 'D' base: 'd' shift, capslock: 'D' + shift+capslock: 'd' } key E { @@ -52,24 +56,28 @@ key E { base: 'e' shift, capslock: 'E' alt: '\u0301' + shift+capslock: 'e' } key F { label: 'F' base: 'f' shift, capslock: 'F' + shift+capslock: 'f' } key G { label: 'G' base: 'g' shift, capslock: 'G' + shift+capslock: 'g' } key H { label: 'H' base: 'h' shift, capslock: 'H' + shift+capslock: 'h' } key I { @@ -77,30 +85,35 @@ key I { base: 'i' shift, capslock: 'I' alt: '\u0302' + shift+capslock: 'i' } key J { label: 'J' base: 'j' shift, capslock: 'J' + shift+capslock: 'j' } key K { label: 'K' base: 'k' shift, capslock: 'K' + shift+capslock: 'k' } key L { label: 'L' base: 'l' shift, capslock: 'L' + shift+capslock: 'l' } key M { label: 'M' base: 'm' shift, capslock: 'M' + shift+capslock: 'm' } key N { @@ -108,30 +121,35 @@ key N { base: 'n' shift, capslock: 'N' alt: '\u0303' + shift+capslock: 'n' } key O { label: 'O' base: 'o' shift, capslock: 'O' + shift+capslock: 'o' } key P { label: 'P' base: 'p' shift, capslock: 'P' + shift+capslock: 'p' } key Q { label: 'Q' base: 'q' shift, capslock: 'Q' + shift+capslock: 'q' } key R { label: 'R' base: 'r' shift, capslock: 'R' + shift+capslock: 'r' } key S { @@ -139,12 +157,14 @@ key S { base: 's' shift, capslock: 'S' alt: '\u00df' + shift+capslock: 's' } key T { label: 'T' base: 't' shift, capslock: 'T' + shift+capslock: 't' } key U { @@ -152,36 +172,42 @@ key U { base: 'u' shift, capslock: 'U' alt: '\u0308' + shift+capslock: 'u' } key V { label: 'V' base: 'v' shift, capslock: 'V' + shift+capslock: 'v' } key W { label: 'W' base: 'w' shift, capslock: 'W' + shift+capslock: 'w' } key X { label: 'X' base: 'x' shift, capslock: 'X' + shift+capslock: 'x' } key Y { label: 'Y' base: 'y' shift, capslock: 'Y' + shift+capslock: 'y' } key Z { label: 'Z' base: 'z' shift, capslock: 'Z' + shift+capslock: 'z' } key 0 { diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index 13e2692ff800..7f5b7520e76d 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -225,7 +225,14 @@ public class FontListParser { } } String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); - String updatedName = findUpdatedFontFile(sanitizedName, updatableFontMap); + + if (postScriptName == null) { + // If post script name was not provided, assume the file name is same to PostScript + // name. + postScriptName = sanitizedName.substring(0, sanitizedName.length() - 4); + } + + String updatedName = findUpdatedFontFile(postScriptName, updatableFontMap); String filePath; String originalPath; if (updatedName != null) { @@ -246,12 +253,7 @@ public class FontListParser { File file = new File(filePath); - if (postScriptName == null) { - // If post script name was not provided, assume the file name is same to PostScript - // name. - String name = file.getName(); - postScriptName = name.substring(0, name.length() - 4); - } + return new FontConfig.Font(file, originalPath == null ? null : new File(originalPath), @@ -265,10 +267,10 @@ public class FontListParser { fallbackFor); } - private static String findUpdatedFontFile(String name, + private static String findUpdatedFontFile(String psName, @Nullable Map<String, File> updatableFontMap) { if (updatableFontMap != null) { - File updatedFile = updatableFontMap.get(name); + File updatedFile = updatableFontMap.get(psName); if (updatedFile != null) { return updatedFile.getAbsolutePath(); } diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java index e7c10819f679..2b4d5b45a009 100644 --- a/graphics/java/android/graphics/drawable/RippleShader.java +++ b/graphics/java/android/graphics/drawable/RippleShader.java @@ -115,7 +115,7 @@ final class RippleShader extends RuntimeShader { + " float fade = min(fadeIn, 1. - fadeOutRipple);\n" + " vec4 circle = in_color * (softCircle(p, center, in_maxRadius " + " * scaleIn, 0.2) * fade);\n" - + " float mask = in_hasMask == 1. ? sample(in_shader).a > 0. ? 1. : 0. : 1.;\n" + + " float mask = in_hasMask == 1. ? sample(in_shader, p).a > 0. ? 1. : 0. : 1.;\n" + " return mix(circle, in_sparkleColor, sparkle) * mask;\n" + "}"; private static final String SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN; diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index 9298d9fcb9a7..4065bd110c7e 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -349,15 +349,19 @@ public class VectorDrawable extends Drawable { private final Rect mTmpBounds = new Rect(); public VectorDrawable() { - this(new VectorDrawableState(null), null); + this(null, null); } /** * The one constructor to rule them all. This is called by all public * constructors to set the state and initialize local properties. */ - private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) { - mVectorState = state; + private VectorDrawable(@Nullable VectorDrawableState state, @Nullable Resources res) { + // As the mutable, not-thread-safe native instance is stored in VectorDrawableState, we + // need to always do a defensive copy even if mutate() isn't called. Otherwise + // draw() being called on 2 different VectorDrawable instances could still hit the same + // underlying native object. + mVectorState = new VectorDrawableState(state); updateLocalState(res); } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java index 9d8a5effc2d7..e808c5cc51bd 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java @@ -579,7 +579,11 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor protected final byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException { if (mCipher != null) { - return mCipher.doFinal(input, inputOffset, inputLen); + if (input == null && inputLen == 0) { + return mCipher.doFinal(); + } else { + return mCipher.doFinal(input, inputOffset, inputLen); + } } if (mCachedException != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index 625f4b85c946..04ec3917428e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -21,6 +21,10 @@ import static android.os.UserHandle.myUserId; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE; import android.annotation.BinderThread; import android.content.ComponentName; @@ -62,8 +66,7 @@ import java.io.PrintWriter; /** * Manages and manipulates the one handed states, transitions, and gesture for phones. */ -public class OneHandedController implements RemoteCallable<OneHandedController>, - OneHandedTransitionCallback { +public class OneHandedController implements RemoteCallable<OneHandedController> { private static final String TAG = "OneHandedController"; private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = @@ -75,7 +78,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, private volatile boolean mIsOneHandedEnabled; private volatile boolean mIsSwipeToNotificationEnabled; - private volatile boolean mIsTransitioning; private boolean mTaskChangeToExit; private boolean mLockedDisabled; private int mUserId; @@ -89,6 +91,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, private final OneHandedAccessibilityUtil mOneHandedAccessibilityUtil; private final OneHandedTimeoutHandler mTimeoutHandler; private final OneHandedTouchHandler mTouchHandler; + private final OneHandedState mState; private final OneHandedTutorialHandler mTutorialHandler; private final OneHandedUiEventLogger mOneHandedUiEventLogger; private final TaskStackListenerImpl mTaskStackListener; @@ -162,6 +165,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, } }; + private final OneHandedTransitionCallback mTransitionCallBack = + new OneHandedTransitionCallback() { + @Override + public void onStartFinished(Rect bounds) { + mState.setState(STATE_ACTIVE); + } + + @Override + public void onStopFinished(Rect bounds) { + mState.setState(STATE_NONE); + } + }; + private final TaskStackListenerCallback mTaskStackListenerCallback = new TaskStackListenerCallback() { @Override @@ -200,8 +216,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil(); OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context); OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor); + OneHandedState transitionState = new OneHandedState(); OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context, - windowManager, mainExecutor); + displayLayout, windowManager, mainExecutor); OneHandedAnimationController animationController = new OneHandedAnimationController(context); OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler, @@ -218,7 +235,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, ServiceManager.getService(Context.OVERLAY_SERVICE)); return new OneHandedController(context, displayController, oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler, - gestureHandler, settingsUtil, accessibilityUtil, timeoutHandler, + gestureHandler, settingsUtil, accessibilityUtil, timeoutHandler, transitionState, oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor, mainHandler); } @@ -234,6 +251,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, OneHandedSettingsUtil settingsUtil, OneHandedAccessibilityUtil oneHandedAccessibilityUtil, OneHandedTimeoutHandler timeoutHandler, + OneHandedState state, OneHandedUiEventLogger uiEventsLogger, IOverlayManager overlayManager, TaskStackListenerImpl taskStackListener, @@ -246,6 +264,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, mDisplayAreaOrganizer = displayAreaOrganizer; mDisplayController = displayController; mTouchHandler = touchHandler; + mState = state; mTutorialHandler = tutorialHandler; mGestureHandler = gestureHandler; mOverlayManager = overlayManager; @@ -330,26 +349,27 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, @VisibleForTesting void startOneHanded() { - if (isLockedDisabled() || mIsTransitioning) { + if (isLockedDisabled()) { Slog.d(TAG, "Temporary lock disabled"); return; } + if (mState.isTransitioning() || mState.isInOneHanded()) { + return; + } final int currentRotation = mDisplayAreaOrganizer.getDisplayLayout().rotation(); if (currentRotation != Surface.ROTATION_0 && currentRotation != Surface.ROTATION_180) { Slog.w(TAG, "One handed mode only support portrait mode"); return; } - if (!mDisplayAreaOrganizer.isInOneHanded()) { - mIsTransitioning = true; - final int yOffSet = Math.round( - mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction); - mOneHandedAccessibilityUtil.announcementForScreenReader( - mOneHandedAccessibilityUtil.getOneHandedStartDescription()); - mDisplayAreaOrganizer.scheduleOffset(0, yOffSet); - mTimeoutHandler.resetTimer(); - mOneHandedUiEventLogger.writeEvent( - OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN); - } + mState.setState(STATE_ENTERING); + final int yOffSet = Math.round( + mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction); + mOneHandedAccessibilityUtil.announcementForScreenReader( + mOneHandedAccessibilityUtil.getOneHandedStartDescription()); + mDisplayAreaOrganizer.scheduleOffset(0, yOffSet); + mTimeoutHandler.resetTimer(); + mOneHandedUiEventLogger.writeEvent( + OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN); } @VisibleForTesting @@ -358,14 +378,15 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, } private void stopOneHanded(int uiEvent) { - if (mDisplayAreaOrganizer.isInOneHanded() && !mIsTransitioning) { - mIsTransitioning = true; - mOneHandedAccessibilityUtil.announcementForScreenReader( - mOneHandedAccessibilityUtil.getOneHandedStopDescription()); - mDisplayAreaOrganizer.scheduleOffset(0, 0); - mTimeoutHandler.removeTimer(); - mOneHandedUiEventLogger.writeEvent(uiEvent); + if (mState.isTransitioning() || mState.getState() == STATE_NONE) { + return; } + mState.setState(STATE_EXITING); + mOneHandedAccessibilityUtil.announcementForScreenReader( + mOneHandedAccessibilityUtil.getOneHandedStopDescription()); + mDisplayAreaOrganizer.scheduleOffset(0, 0); + mTimeoutHandler.removeTimer(); + mOneHandedUiEventLogger.writeEvent(uiEvent); } private void setThreeButtonModeEnabled(boolean enabled) { @@ -388,7 +409,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler); mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler); mDisplayAreaOrganizer.registerTransitionCallback(mBackgroundPanelOrganizer); - mDisplayAreaOrganizer.registerTransitionCallback(this); + mDisplayAreaOrganizer.registerTransitionCallback(mTransitionCallBack); if (mTaskChangeToExit) { mTaskStackListener.addListener(mTaskStackListenerCallback); } @@ -432,6 +453,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, final DisplayLayout newDisplayLayout = mDisplayController.getDisplayLayout(displayId); mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout); mGestureHandler.onDisplayChanged(newDisplayLayout); + mTutorialHandler.onDisplayChanged(newDisplayLayout); } private ContentObserver getObserver(Runnable onChangeRunnable) { @@ -523,8 +545,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, } private void updateOneHandedEnabled() { - if (mDisplayAreaOrganizer.isInOneHanded()) { - stopOneHanded(); + if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) { + mMainExecutor.execute(() -> stopOneHanded()); } mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled); @@ -615,8 +637,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, pw.println(mLockedDisabled); pw.print(innerPrefix + "mUserId="); pw.println(mUserId); - pw.print(innerPrefix + "mIsTransitioning="); - pw.println(mIsTransitioning); if (mBackgroundPanelOrganizer != null) { mBackgroundPanelOrganizer.dump(pw); @@ -638,6 +658,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, mTimeoutHandler.dump(pw); } + if (mState != null) { + mState.dump(pw); + } + if (mTutorialHandler != null) { mTutorialHandler.dump(pw); } @@ -663,26 +687,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, } /** - * TODO(b/185558765) To implement a state machine for One-Handed transition state machine. - * ONE_HANDDE_STATE_TRANSITION { - * STATE_DEFAULT, - * STATE_TRANSITIONING, - * STATE_ENTER_ONE_HANED, - * STATE_EXIT_ONE_HANDED - * } - * and we need to align the state to launcher3 quick steps through SysuiProxy. - */ - @Override - public void onStartFinished(Rect bounds) { - mIsTransitioning = false; - } - - @Override - public void onStopFinished(Rect bounds) { - mIsTransitioning = false; - } - - /** * The interface for calls from outside the Shell, within the host process. */ @ExternalThread diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index 390985c09dcc..4b4d934bef43 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -65,7 +65,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { private final Rect mDefaultDisplayBounds = new Rect(); private final OneHandedSettingsUtil mOneHandedSettingsUtil; - private boolean mIsInOneHanded; private int mEnterExitAnimationDurationMs; private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap(); @@ -268,9 +267,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @VisibleForTesting void finishOffset(int offset, @OneHandedAnimationController.TransitionDirection int direction) { - // Only finishOffset() can update mIsInOneHanded to ensure the state is handle in sequence, - // the flag *MUST* be updated before dispatch mTransitionCallbacks - mIsInOneHanded = (offset > 0 || direction == TRANSITION_DIRECTION_TRIGGER); mLastVisualDisplayBounds.offsetTo(0, direction == TRANSITION_DIRECTION_TRIGGER ? offset : 0); for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) { @@ -285,15 +281,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { } /** - * The latest state of one handed mode - * - * @return true Currently is in one handed mode, otherwise is not in one handed mode - */ - public boolean isInOneHanded() { - return mIsInOneHanded; - } - - /** * The latest visual bounds of displayArea translated * * @return Rect latest finish_offset @@ -337,8 +324,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { void dump(@NonNull PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG + "states: "); - pw.print(innerPrefix + "mIsInOneHanded="); - pw.println(mIsInOneHanded); pw.print(innerPrefix + "mDisplayLayout.rotation()="); pw.println(mDisplayLayout.rotation()); pw.print(innerPrefix + "mDisplayAreaTokenMap="); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedState.java new file mode 100644 index 000000000000..cc874432be87 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedState.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 com.android.wm.shell.onehanded; + +import android.annotation.IntDef; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + Represents current OHM state by following steps, a generic CUJ is + STATE_NONE -> STATE_ENTERING -> STATE_ACTIVE -> STATE_EXITING -> STATE_NONE + */ +public class OneHandedState { + /** DEFAULT STATE after OHM feature initialized. */ + public static final int STATE_NONE = 0x00000000; + /** The state flag set when user trigger OHM. */ + public static final int STATE_ENTERING = 0x00000001; + /** The state flag set when transitioning */ + public static final int STATE_ACTIVE = 0x00000002; + /** The state flag set when user stop OHM feature. */ + public static final int STATE_EXITING = 0x00000004; + + @IntDef(prefix = { "STATE_" }, value = { + STATE_NONE, + STATE_ENTERING, + STATE_ACTIVE, + STATE_EXITING + }) + @Retention(RetentionPolicy.SOURCE) + @interface State {} + + public OneHandedState() { + sCurrentState = STATE_NONE; + } + + @State + private static int sCurrentState = STATE_NONE; + + private static final String TAG = OneHandedState.class.getSimpleName(); + + /** + * Gets current transition state of One handed mode. + * + * @return The bitwise flags representing current states. + */ + public @State int getState() { + return sCurrentState; + } + + /** + * Is the One handed mode is in transitioning state. + * @return true if One handed mode is in transitioning states. + */ + public boolean isTransitioning() { + return sCurrentState == STATE_ENTERING || sCurrentState == STATE_EXITING; + } + + /** + * Is the One handed mode active state. + * @return true if One handed mode is active state. + */ + public boolean isInOneHanded() { + return sCurrentState == STATE_ACTIVE; + } + + /** + * Sets new state for One handed mode feature. + * @param newState The bitwise value to represent current transition states. + */ + public void setState(@State int newState) { + sCurrentState = newState; + } + + /** Dumps internal state. */ + public void dump(PrintWriter pw) { + final String innerPrefix = " "; + pw.println(TAG + "states: "); + pw.println(innerPrefix + "sCurrentState=" + sCurrentState); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java index b445917fb90c..7a3f34d0e5a5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java @@ -33,6 +33,7 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import com.android.wm.shell.R; +import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import java.io.PrintWriter; @@ -50,9 +51,10 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { private static final int MAX_TUTORIAL_SHOW_COUNT = 2; private final WindowManager mWindowManager; private final String mPackageName; - private final Rect mDisplaySize; + private final float mTutorialHeightRatio; private Context mContext; + private Rect mDisplayBounds; private View mTutorialView; private ContentResolver mContentResolver; private boolean mCanShowTutorial; @@ -94,23 +96,22 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { } }; - public OneHandedTutorialHandler(Context context, WindowManager windowManager, - ShellExecutor mainExecutor) { + public OneHandedTutorialHandler(Context context, DisplayLayout displayLayout, + WindowManager windowManager, ShellExecutor mainExecutor) { mContext = context; mWindowManager = windowManager; - mDisplaySize = windowManager.getCurrentWindowMetrics().getBounds(); mPackageName = context.getPackageName(); mContentResolver = context.getContentResolver(); - mCanShowTutorial = (Settings.Secure.getInt(mContentResolver, - Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT) - ? false : true; - mIsOneHandedMode = false; final float offsetPercentageConfig = context.getResources().getFraction( R.fraction.config_one_handed_offset, 1, 1); final int sysPropPercentageConfig = SystemProperties.getInt( ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f)); - mTutorialAreaHeight = Math.round( - mDisplaySize.height() * (sysPropPercentageConfig / 100.0f)); + mTutorialHeightRatio = sysPropPercentageConfig / 100.0f; + onDisplayChanged(displayLayout); + mCanShowTutorial = (Settings.Secure.getInt(mContentResolver, + Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT) + ? false : true; + mIsOneHandedMode = false; mainExecutor.execute(() -> { recreateTutorialView(mContext); @@ -131,6 +132,20 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET; } + /** + * Called when onDisplayAdded() or onDisplayRemoved() callback + * @param displayLayout The latest {@link DisplayLayout} representing current displayId + */ + public void onDisplayChanged(DisplayLayout displayLayout) { + // Ensure the mDisplayBounds is portrait, due to OHM only support on portrait + if (displayLayout.height() > displayLayout.width()) { + mDisplayBounds = new Rect(0, 0, displayLayout.width(), displayLayout.height()); + } else { + mDisplayBounds = new Rect(0, 0, displayLayout.height(), displayLayout.width()); + } + mTutorialAreaHeight = Math.round(mDisplayBounds.height() * mTutorialHeightRatio); + } + private void recreateTutorialView(Context context) { mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null); @@ -190,7 +205,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { */ private WindowManager.LayoutParams getTutorialTargetLayoutParams() { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - mDisplaySize.width(), mTutorialAreaHeight, 0, 0, + mDisplayBounds.width(), mTutorialAreaHeight, 0, 0, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, @@ -207,8 +222,8 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { pw.println(TAG + " states: "); pw.print(innerPrefix + "mTriggerState="); pw.println(mTriggerState); - pw.print(innerPrefix + "mDisplaySize="); - pw.println(mDisplaySize); + pw.print(innerPrefix + "mDisplayBounds="); + pw.println(mDisplayBounds); pw.print(innerPrefix + "mTutorialAreaHeight="); pw.println(mTutorialAreaHeight); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 8ac9a7a479db..ca05ff47d507 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -427,36 +427,44 @@ public class PipAnimationController { Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, float startingAngle, @Surface.Rotation int rotationDelta) { + final boolean isOutPipDirection = isOutPipDirection(direction); + // Just for simplicity we'll interpolate between the source rect hint insets and empty // insets to calculate the window crop final Rect initialSourceValue; - if (isOutPipDirection(direction)) { + if (isOutPipDirection) { initialSourceValue = new Rect(endValue); } else { initialSourceValue = new Rect(baseValue); } - final Rect sourceHintRectInsets; - if (sourceHintRect == null) { - sourceHintRectInsets = null; - } else { - sourceHintRectInsets = new Rect(sourceHintRect.left - initialSourceValue.left, - sourceHintRect.top - initialSourceValue.top, - initialSourceValue.right - sourceHintRect.right, - initialSourceValue.bottom - sourceHintRect.bottom); - } - final Rect sourceInsets = new Rect(0, 0, 0, 0); - final Rect rotatedEndRect; + final Rect lastEndRect; + final Rect initialContainerRect; if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) { + lastEndRect = new Rect(endValue); + rotatedEndRect = new Rect(endValue); // Rotate the end bounds according to the rotation delta because the display will // be rotated to the same orientation. - rotatedEndRect = new Rect(endValue); - rotateBounds(rotatedEndRect, endValue, rotationDelta); + rotateBounds(rotatedEndRect, initialSourceValue, rotationDelta); + // Use the rect that has the same orientation as the hint rect. + initialContainerRect = isOutPipDirection ? rotatedEndRect : initialSourceValue; } else { - rotatedEndRect = null; + rotatedEndRect = lastEndRect = null; + initialContainerRect = initialSourceValue; } + final Rect sourceHintRectInsets; + if (sourceHintRect == null) { + sourceHintRectInsets = null; + } else { + sourceHintRectInsets = new Rect(sourceHintRect.left - initialContainerRect.left, + sourceHintRect.top - initialContainerRect.top, + initialContainerRect.right - sourceHintRect.right, + initialContainerRect.bottom - sourceHintRect.bottom); + } + final Rect zeroInsets = new Rect(0, 0, 0, 0); + // construct new Rect instances in case they are recycled return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS, endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue), @@ -472,8 +480,8 @@ public class PipAnimationController { final Rect end = getEndValue(); if (rotatedEndRect != null) { // Animate the bounds in a different orientation. It only happens when - // leaving PiP to fullscreen. - applyRotation(tx, leash, fraction, start, end, rotatedEndRect); + // switching between PiP and fullscreen. + applyRotation(tx, leash, fraction, start, end); return; } Rect bounds = mRectEvaluator.evaluate(fraction, start, end); @@ -481,20 +489,13 @@ public class PipAnimationController { setCurrentValue(bounds); if (inScaleTransition() || sourceHintRect == null) { - if (isOutPipDirection(direction)) { + if (isOutPipDirection) { getSurfaceTransactionHelper().scale(tx, leash, end, bounds); } else { getSurfaceTransactionHelper().scale(tx, leash, base, bounds, angle); } } else { - final Rect insets; - if (isOutPipDirection(direction)) { - insets = mInsetsEvaluator.evaluate(fraction, sourceHintRectInsets, - sourceInsets); - } else { - insets = mInsetsEvaluator.evaluate(fraction, sourceInsets, - sourceHintRectInsets); - } + final Rect insets = computeInsets(fraction); getSurfaceTransactionHelper().scaleAndCrop(tx, leash, initialSourceValue, bounds, insets); } @@ -502,9 +503,17 @@ public class PipAnimationController { } private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash, - float fraction, Rect start, Rect end, Rect rotatedEndRect) { + float fraction, Rect start, Rect end) { + if (!end.equals(lastEndRect)) { + // If the end bounds are changed during animating (e.g. shelf height), the + // rotated end bounds also need to be updated. + rotatedEndRect.set(endValue); + rotateBounds(rotatedEndRect, initialSourceValue, rotationDelta); + lastEndRect.set(end); + } final Rect bounds = mRectEvaluator.evaluate(fraction, start, rotatedEndRect); setCurrentValue(bounds); + final Rect insets = computeInsets(fraction); final float degree, x, y; if (rotationDelta == ROTATION_90) { degree = 90 * fraction; @@ -515,11 +524,21 @@ public class PipAnimationController { x = fraction * (end.left - start.left) + start.left; y = fraction * (end.bottom - start.top) + start.top; } - getSurfaceTransactionHelper().rotateAndScaleWithCrop(tx, leash, bounds, - rotatedEndRect, degree, x, y); + getSurfaceTransactionHelper().rotateAndScaleWithCrop(tx, leash, + initialContainerRect, bounds, insets, degree, x, y, isOutPipDirection, + rotationDelta == ROTATION_270 /* clockwise */); tx.apply(); } + private Rect computeInsets(float fraction) { + if (sourceHintRectInsets == null) { + return zeroInsets; + } + final Rect startRect = isOutPipDirection ? sourceHintRectInsets : zeroInsets; + final Rect endRect = isOutPipDirection ? zeroInsets : sourceHintRectInsets; + return mInsetsEvaluator.evaluate(fraction, startRect, endRect); + } + @Override void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) { getSurfaceTransactionHelper() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java index 3dd97f565179..2b795390adda 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java @@ -137,23 +137,41 @@ public class PipSurfaceTransactionHelper { * @return same {@link PipSurfaceTransactionHelper} instance for method chaining */ public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx, - SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, float degrees, - float positionX, float positionY) { + SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, Rect insets, + float degrees, float positionX, float positionY, boolean isExpanding, + boolean clockwise) { mTmpDestinationRect.set(sourceBounds); - final int dw = destinationBounds.width(); - final int dh = destinationBounds.height(); + mTmpDestinationRect.inset(insets); + final int srcW = mTmpDestinationRect.width(); + final int srcH = mTmpDestinationRect.height(); + final int destW = destinationBounds.width(); + final int destH = destinationBounds.height(); // Scale by the short side so there won't be empty area if the aspect ratio of source and // destination are different. - final float scale = dw <= dh - ? (float) sourceBounds.width() / dw - : (float) sourceBounds.height() / dh; + final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH; + final Rect crop = mTmpDestinationRect; + crop.set(0, 0, destW, destH); // Inverse scale for crop to fit in screen coordinates. - mTmpDestinationRect.scale(1 / scale); - mTmpTransform.setRotate(degrees); - mTmpTransform.postScale(scale, scale); + crop.scale(1 / scale); + crop.offset(insets.left, insets.top); + if (isExpanding) { + // Expand bounds (shrink insets) in source orientation. + positionX -= insets.left * scale; + positionY -= insets.top * scale; + } else { + // Shrink bounds (expand insets) in destination orientation. + if (clockwise) { + positionX -= insets.top * scale; + positionY -= insets.left * scale; + } else { + positionX += insets.top * scale; + positionY += insets.left * scale; + } + } + mTmpTransform.setScale(scale, scale); + mTmpTransform.postRotate(degrees); mTmpTransform.postTranslate(positionX, positionY); - tx.setMatrix(leash, mTmpTransform, mTmpFloat9) - .setWindowCrop(leash, mTmpDestinationRect.width(), mTmpDestinationRect.height()); + tx.setMatrix(leash, mTmpTransform, mTmpFloat9).setWindowCrop(leash, crop); return this; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index e66be66c8ef4..4ce6c9e35e9e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -20,6 +20,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.util.RotationUtils.deltaRotation; +import static android.util.RotationUtils.rotateBounds; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP; import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; @@ -50,8 +52,10 @@ import android.app.TaskInfo; import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.graphics.Rect; import android.os.RemoteException; +import android.os.SystemClock; import android.util.Log; import android.util.Rational; import android.view.Display; @@ -94,6 +98,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, DisplayController.OnDisplaysChangedListener { private static final String TAG = PipTaskOrganizer.class.getSimpleName(); private static final boolean DEBUG = false; + /** + * The alpha type is set for swiping to home. But the swiped task may not enter PiP. And if + * another task enters PiP by non-swipe ways, e.g. call API in foreground or switch to 3-button + * navigation, then the alpha type is unexpected. + */ + private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000; // Not a complete set of states but serves what we want right now. private enum State { @@ -127,6 +137,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } + private final Context mContext; private final SyncTransactionQueue mSyncTransactionQueue; private final PipBoundsState mPipBoundsState; private final PipBoundsAlgorithm mPipBoundsAlgorithm; @@ -160,8 +171,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx, PipAnimationController.PipTransitionAnimator animator) { final int direction = animator.getTransitionDirection(); - finishResize(tx, animator.getDestinationBounds(), direction, - animator.getAnimationType()); + final int animationType = animator.getAnimationType(); + final Rect destinationBounds = animator.getDestinationBounds(); + if (mWaitForFixedRotation && animationType == ANIM_TYPE_BOUNDS + && direction == TRANSITION_DIRECTION_TO_PIP) { + // Notify the display to continue the deferred orientation change. + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.scheduleFinishEnterPip(mToken, destinationBounds); + mTaskOrganizer.applyTransaction(wct); + // The final task bounds will be applied by onFixedRotationFinished so that all + // coordinates are in new rotation. + mDeferredAnimEndTransaction = tx; + return; + } + finishResize(tx, destinationBounds, direction, animationType); sendOnPipTransitionFinished(direction); if (direction == TRANSITION_DIRECTION_TO_PIP) { // TODO (b//169221267): Add jank listener for transactions without buffer updates. @@ -186,10 +209,18 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private SurfaceControl mLeash; private State mState = State.UNDEFINED; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; + private long mLastOneShotAlphaAnimationTime; private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; private PictureInPictureParams mPictureInPictureParams; private IntConsumer mOnDisplayIdChangeCallback; + /** + * The end transaction of PiP animation for switching between PiP and fullscreen with + * orientation change. The transaction should be applied after the display is rotated. + */ + private SurfaceControl.Transaction mDeferredAnimEndTransaction; + /** Whether the existing PiP is hidden by alpha. */ + private boolean mHasFadeOut; /** * If set to {@code true}, the entering animation will be skipped and we will wait for @@ -203,6 +234,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ private @Surface.Rotation int mNextRotation; + private @Surface.Rotation int mCurrentRotation; + /** * If set to {@code true}, no entering PiP transition would be kicked off and most likely * it's due to the fact that Launcher is handling the transition directly when swiping @@ -224,6 +257,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @NonNull PipUiEventLogger pipUiEventLogger, @NonNull ShellTaskOrganizer shellTaskOrganizer, @ShellMainThread ShellExecutor mainExecutor) { + mContext = context; mSyncTransactionQueue = syncTransactionQueue; mPipBoundsState = pipBoundsState; mPipBoundsAlgorithm = boundsHandler; @@ -261,10 +295,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return mState.isInPip(); } - public boolean isDeferringEnterPipAnimation() { - return mState.isInPip() && mWaitForFixedRotation; - } - /** * Returns whether the entry animation is waiting to be started. */ @@ -286,6 +316,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) { mOneShotAnimationType = animationType; + if (animationType == ANIM_TYPE_ALPHA) { + mLastOneShotAlphaAnimationTime = SystemClock.uptimeMillis(); + } } /** @@ -297,9 +330,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mInSwipePipToHomeTransition = true; sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP); setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo); - // disable the conflicting transaction from fixed rotation, see also - // onFixedRotationStarted and onFixedRotationFinished - mWaitForFixedRotation = false; return mPipBoundsAlgorithm.getEntryDestinationBounds(); } @@ -355,6 +385,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, : WINDOWING_MODE_FULLSCREEN); wct.setBounds(mToken, destinationBounds); wct.setBoundsChangeTransaction(mToken, tx); + // Set the exiting state first so if there is fixed rotation later, the running animation + // won't be interrupted by alpha animation for existing PiP. + mState = State.EXITING_PIP; mSyncTransactionQueue.queue(wct); mSyncTransactionQueue.runInSync(t -> { // Make sure to grab the latest source hint rect as it could have been @@ -362,9 +395,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( mPictureInPictureParams, destinationBounds); final PipAnimationController.PipTransitionAnimator<?> animator = - scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds, - 0 /* startingAngle */, sourceHintRect, direction, - animationDurationMs, null /* updateBoundsCallback */); + animateResizePip(mPipBoundsState.getBounds(), destinationBounds, sourceHintRect, + direction, animationDurationMs, 0 /* startingAngle */); if (animator != null) { // Even though the animation was started above, re-apply the transaction for the // first frame using the SurfaceControl.Transaction supplied by the @@ -374,7 +406,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // hint during expansion that causes a visible jank/flash. See b/184166183. animator.applySurfaceControlTransaction(mLeash, t, FRACTION_START); } - mState = State.EXITING_PIP; }); } @@ -447,29 +478,22 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } if (mInSwipePipToHomeTransition) { - final Rect destinationBounds = mPipBoundsState.getBounds(); - final SurfaceControl.Transaction tx = - mSurfaceControlTransactionFactory.getTransaction(); - mSurfaceTransactionHelper.resetScale(tx, mLeash, destinationBounds); - mSurfaceTransactionHelper.crop(tx, mLeash, destinationBounds); - // animation is finished in the Launcher and here we directly apply the final touch. - applyEnterPipSyncTransaction(destinationBounds, () -> { - // ensure menu's settled in its final bounds first - finishResizeForMenu(destinationBounds); - sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); - }, tx); - mInSwipePipToHomeTransition = false; + if (!mWaitForFixedRotation) { + onEndOfSwipePipToHomeTransition(); + } else { + Log.d(TAG, "Defer onTaskAppeared-SwipePipToHome until end of fixed rotation."); + } return; } + if (mOneShotAnimationType == ANIM_TYPE_ALPHA + && SystemClock.uptimeMillis() - mLastOneShotAlphaAnimationTime + > ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS) { + Log.d(TAG, "Alpha animation is expired. Use bounds animation."); + mOneShotAnimationType = ANIM_TYPE_BOUNDS; + } if (mWaitForFixedRotation) { - if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing"); - // if deferred, hide the surface till fixed rotation is completed - final SurfaceControl.Transaction tx = - mSurfaceControlTransactionFactory.getTransaction(); - tx.setAlpha(mLeash, 0f); - tx.show(mLeash); - tx.apply(); + onTaskAppearedWithFixedRotation(); return; } @@ -500,6 +524,27 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } + private void onTaskAppearedWithFixedRotation() { + if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + Log.d(TAG, "Defer entering PiP alpha animation, fixed rotation is ongoing"); + // If deferred, hide the surface till fixed rotation is completed. + final SurfaceControl.Transaction tx = + mSurfaceControlTransactionFactory.getTransaction(); + tx.setAlpha(mLeash, 0f); + tx.show(mLeash); + tx.apply(); + mOneShotAnimationType = ANIM_TYPE_BOUNDS; + return; + } + final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); + final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( + mPictureInPictureParams, currentBounds); + final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); + animateResizePip(currentBounds, destinationBounds, sourceHintRect, + TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, 0 /* startingAngle */); + mState = State.ENTERING_PIP; + } + /** * Called when the display rotation handling is skipped (e.g. when rotation happens while in * the middle of an entry transition). @@ -536,6 +581,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, }, null /* boundsChangeTransaction */); } + private void onEndOfSwipePipToHomeTransition() { + final Rect destinationBounds = mPipBoundsState.getBounds(); + final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); + mSurfaceTransactionHelper.resetScale(tx, mLeash, destinationBounds); + mSurfaceTransactionHelper.crop(tx, mLeash, destinationBounds); + // The animation is finished in the Launcher and here we directly apply the final touch. + applyEnterPipSyncTransaction(destinationBounds, () -> { + // Ensure menu's settled in its final bounds first. + finishResizeForMenu(destinationBounds); + sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); + }, tx); + mInSwipePipToHomeTransition = false; + } + private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable, @Nullable SurfaceControl.Transaction boundsChangeTransaction) { // PiP menu is attached late in the process here to avoid any artifacts on the leash @@ -547,7 +606,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (boundsChangeTransaction != null) { wct.setBoundsChangeTransaction(mToken, boundsChangeTransaction); } - wct.scheduleFinishEnterPip(mToken, destinationBounds); mSyncTransactionQueue.queue(wct); if (runnable != null) { mSyncTransactionQueue.runInSync(t -> runnable.run()); @@ -600,7 +658,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Log.wtf(TAG, "Unrecognized token: " + token); return; } - mWaitForFixedRotation = false; + clearWaitForFixedRotation(); mInSwipePipToHomeTransition = false; mPictureInPictureParams = null; mState = State.UNDEFINED; @@ -617,8 +675,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken"); - if (mState != State.ENTERED_PIP) { + if (mState != State.ENTERED_PIP && mState != State.EXITING_PIP) { Log.d(TAG, "Defer onTaskInfoChange in current state: " + mState); + // Defer applying PiP parameters if the task is entering PiP to avoid disturbing + // the animation. mDeferredTaskInfo = info; return; } @@ -648,16 +708,60 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public void onFixedRotationStarted(int displayId, int newRotation) { mNextRotation = newRotation; mWaitForFixedRotation = true; + + if (mState.isInPip()) { + // Fade out the existing PiP to avoid jump cut during seamless rotation. + fadeExistingPip(false /* show */); + } } @Override public void onFixedRotationFinished(int displayId) { - if (mWaitForFixedRotation && mState.isInPip()) { - final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); - // schedule a regular animation to ensure all the callbacks are still being sent - enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */); + if (!mWaitForFixedRotation) { + return; } + if (mState == State.TASK_APPEARED) { + if (mInSwipePipToHomeTransition) { + onEndOfSwipePipToHomeTransition(); + } else { + // Schedule a regular animation to ensure all the callbacks are still being sent. + enterPipWithAlphaAnimation(mPipBoundsAlgorithm.getEntryDestinationBounds(), + mEnterAnimationDuration); + } + } else if (mState == State.ENTERED_PIP && mHasFadeOut) { + fadeExistingPip(true /* show */); + } else if (mState == State.ENTERING_PIP && mDeferredAnimEndTransaction != null) { + final PipAnimationController.PipTransitionAnimator<?> animator = + mPipAnimationController.getCurrentAnimator(); + final Rect destinationBounds = animator.getDestinationBounds(); + mPipBoundsState.setBounds(destinationBounds); + applyEnterPipSyncTransaction(destinationBounds, () -> { + finishResizeForMenu(destinationBounds); + sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); + }, mDeferredAnimEndTransaction); + } + clearWaitForFixedRotation(); + } + + private void fadeExistingPip(boolean show) { + final float alphaStart = show ? 0 : 1; + final float alphaEnd = show ? 1 : 0; + mPipAnimationController + .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), alphaStart, alphaEnd) + .setTransitionDirection(TRANSITION_DIRECTION_SAME) + .setDuration(show ? mEnterAnimationDuration : mExitAnimationDuration) + .start(); + mHasFadeOut = !show; + } + + private void clearWaitForFixedRotation() { mWaitForFixedRotation = false; + mDeferredAnimEndTransaction = null; + } + + @Override + public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + mCurrentRotation = newConfig.windowConfiguration.getRotation(); } /** @@ -686,7 +790,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipAnimationController.getCurrentAnimator(); if (animator == null || !animator.isRunning() || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) { - if (mState.isInPip() && fromRotation && !mWaitForFixedRotation) { + final boolean rotatingPip = mState.isInPip() && fromRotation; + if (rotatingPip && mWaitForFixedRotation && mHasFadeOut) { + // The position will be used by fade-in animation when the fixed rotation is done. + mPipBoundsState.setBounds(destinationBoundsOut); + } else if (rotatingPip) { // Update bounds state to final destination first. It's important to do this // before finishing & cancelling the transition animation so that the MotionHelper // bounds are synchronized to the destination bounds when the animation ends. @@ -737,7 +845,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final Rect newDestinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); if (newDestinationBounds.equals(currentDestinationBounds)) return; if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) { - animator.updateEndValue(newDestinationBounds); + if (mWaitForFixedRotation) { + // The new destination bounds are in next rotation (DisplayLayout has been rotated + // in computeRotatedBounds). The animation runs in previous rotation so the end + // bounds need to be transformed. + final Rect displayBounds = mPipBoundsState.getDisplayBounds(); + final Rect rotatedEndBounds = new Rect(newDestinationBounds); + rotateBounds(rotatedEndBounds, displayBounds, mNextRotation, mCurrentRotation); + animator.updateEndValue(rotatedEndBounds); + } else { + animator.updateEndValue(newDestinationBounds); + } } animator.setDestinationBounds(newDestinationBounds); destinationBoundsOut.set(newDestinationBounds); @@ -1050,7 +1168,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // activity windowing mode set by WM, and set the task bounds to the final bounds taskBounds = destinationBounds; wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); - wct.scheduleFinishEnterPip(mToken, destinationBounds); } else if (isOutPipDirection(direction)) { // If we are animating to fullscreen or split screen, then we need to reset the // override bounds on the task to ensure that the task "matches" the parent's bounds. @@ -1096,8 +1213,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return null; } final int rotationDelta = mWaitForFixedRotation - ? ((mNextRotation - mPipBoundsState.getDisplayLayout().rotation()) + 4) % 4 + ? deltaRotation(mCurrentRotation, mNextRotation) : Surface.ROTATION_0; + if (rotationDelta != Surface.ROTATION_0) { + sourceHintRect = computeRotatedBounds(rotationDelta, direction, destinationBounds, + sourceHintRect); + } Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE ? mPipBoundsState.getBounds() : currentBounds; final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController @@ -1107,9 +1228,35 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, .setPipAnimationCallback(mPipAnimationCallback) .setDuration(durationMs) .start(); + if (rotationDelta != Surface.ROTATION_0 && direction == TRANSITION_DIRECTION_TO_PIP) { + // The destination bounds are used for the end rect of animation and the final bounds + // after animation finishes. So after the animation is started, the destination bounds + // can be updated to new rotation (computeRotatedBounds has changed the DisplayLayout + // without affecting the animation. + animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds()); + } return animator; } + /** Computes destination bounds in old rotation and returns source hint rect if available. */ + private @Nullable Rect computeRotatedBounds(int rotationDelta, int direction, + Rect outDestinationBounds, Rect sourceHintRect) { + if (direction == TRANSITION_DIRECTION_TO_PIP) { + mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), mNextRotation); + final Rect displayBounds = mPipBoundsState.getDisplayBounds(); + outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); + // Transform the destination bounds to current display coordinates. + rotateBounds(outDestinationBounds, displayBounds, mNextRotation, mCurrentRotation); + } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) { + final Rect rotatedDestinationBounds = new Rect(outDestinationBounds); + rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(), + rotationDelta); + return PipBoundsAlgorithm.getValidSourceHintRect(mPictureInPictureParams, + rotatedDestinationBounds); + } + return sourceHintRect; + } + /** * Sync with {@link LegacySplitScreenController} on destination bounds if PiP is going to split * screen. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index f505e60de61e..b881fea3ac79 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -114,13 +114,17 @@ public class PipController implements PipTransitionController.PipTransitionCallb */ private final DisplayChangeController.OnDisplayChangingListener mRotationController = ( int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> { - if (!mPipTaskOrganizer.isInPip() - || mPipBoundsState.getDisplayLayout().rotation() == toRotation - || mPipTaskOrganizer.isDeferringEnterPipAnimation() - || mPipTaskOrganizer.isEntryScheduled()) { - // Skip if the same rotation has been set or we aren't in PIP or haven't actually - // entered PIP yet. We still need to update the display layout in the bounds handler - // in this case. + if (mPipBoundsState.getDisplayLayout().rotation() == toRotation) { + // The same rotation may have been set by auto PiP-able or fixed rotation. So notify + // the change with fromRotation=false to apply the rotated destination bounds from + // PipTaskOrganizer#onMovementBoundsChanged. + updateMovementBounds(null, false /* fromRotation */, + false /* fromImeAdjustment */, false /* fromShelfAdjustment */, t); + return; + } + if (!mPipTaskOrganizer.isInPip() || mPipTaskOrganizer.isEntryScheduled()) { + // Update display layout and bounds handler if we aren't in PIP or haven't actually + // entered PIP yet. onDisplayRotationChangedNotInPip(mContext, toRotation); // do not forget to update the movement bounds as well. updateMovementBounds(mPipBoundsState.getNormalBounds(), true /* fromRotation */, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index c91a92ad3242..efaa2696cbeb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -337,6 +337,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, final WindowContainerTransaction wct = new WindowContainerTransaction(); // Make the stages adjacent to each other so they occlude what's behind them. wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); + wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); mTaskOrganizer.applyTransaction(wct); } } @@ -346,6 +347,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, final WindowContainerTransaction wct = new WindowContainerTransaction(); // Deactivate the main stage if it no longer has a root task. mMainStage.deactivate(wct); + wct.clearLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); mTaskOrganizer.applyTransaction(wct); } } @@ -449,6 +451,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, final WindowContainerTransaction wct = new WindowContainerTransaction(); // Make sure the main stage is active. mMainStage.activate(getMainStageBounds(), wct); + mSideStage.setBounds(getSideStageBounds(), wct); mTaskOrganizer.applyTransaction(wct); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java index e3362870cdf0..cb7afc77a65b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -15,18 +15,10 @@ */ package com.android.wm.shell.startingsurface; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_SAME_PACKAGE; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; @@ -68,27 +60,24 @@ import java.util.function.BiConsumer; */ public class StartingWindowController implements RemoteCallable<StartingWindowController> { private static final String TAG = StartingWindowController.class.getSimpleName(); + // TODO b/183150443 Keep this flag open for a while, several things might need to adjust. - static final boolean DEBUG_SPLASH_SCREEN = true; - static final boolean DEBUG_TASK_SNAPSHOT = false; + public static final boolean DEBUG_SPLASH_SCREEN = true; + public static final boolean DEBUG_TASK_SNAPSHOT = false; private final StartingSurfaceDrawer mStartingSurfaceDrawer; - private final StartingTypeChecker mStartingTypeChecker = new StartingTypeChecker(); + private final StartingWindowTypeAlgorithm mStartingWindowTypeAlgorithm; private BiConsumer<Integer, Integer> mTaskLaunchingCallback; private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl(); private final Context mContext; private final ShellExecutor mSplashScreenExecutor; - // For Car Launcher - public StartingWindowController(Context context, ShellExecutor splashScreenExecutor) { - this(context, splashScreenExecutor, new TransactionPool()); - } - public StartingWindowController(Context context, ShellExecutor splashScreenExecutor, - TransactionPool pool) { + StartingWindowTypeAlgorithm startingWindowTypeAlgorithm, TransactionPool pool) { mContext = context; mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, pool); + mStartingWindowTypeAlgorithm = startingWindowTypeAlgorithm; mSplashScreenExecutor = splashScreenExecutor; } @@ -109,90 +98,6 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo return mSplashScreenExecutor; } - private static class StartingTypeChecker { - - private @StartingWindowInfo.StartingWindowType int - estimateStartingWindowType(StartingWindowInfo windowInfo) { - final int parameter = windowInfo.startingWindowTypeParameter; - final boolean newTask = (parameter & TYPE_PARAMETER_NEW_TASK) != 0; - final boolean taskSwitch = (parameter & TYPE_PARAMETER_TASK_SWITCH) != 0; - final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0; - final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0; - final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0; - final boolean samePackage = (parameter & TYPE_PARAMETER_SAME_PACKAGE) != 0; - return estimateStartingWindowType(windowInfo, newTask, taskSwitch, - processRunning, allowTaskSnapshot, activityCreated, samePackage); - } - - // reference from ActivityRecord#getStartingWindowType - private int estimateStartingWindowType(StartingWindowInfo windowInfo, - boolean newTask, boolean taskSwitch, boolean processRunning, - boolean allowTaskSnapshot, boolean activityCreated, boolean samePackage) { - if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { - Slog.d(TAG, "preferredStartingWindowType newTask " + newTask - + " taskSwitch " + taskSwitch - + " processRunning " + processRunning - + " allowTaskSnapshot " + allowTaskSnapshot - + " activityCreated " + activityCreated - + " samePackage " + samePackage); - } - if (windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) { - if (!processRunning) { - return STARTING_WINDOW_TYPE_SPLASH_SCREEN; - } - if (newTask) { - if (samePackage) { - return STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; - } else { - return STARTING_WINDOW_TYPE_SPLASH_SCREEN; - } - } - if (taskSwitch && !activityCreated) { - return STARTING_WINDOW_TYPE_SPLASH_SCREEN; - } - } - if (taskSwitch && allowTaskSnapshot) { - final TaskSnapshot snapshot = windowInfo.mTaskSnapshot; - if (isSnapshotCompatible(windowInfo, snapshot)) { - return STARTING_WINDOW_TYPE_SNAPSHOT; - } - if (windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) { - return STARTING_WINDOW_TYPE_SPLASH_SCREEN; - } - } - return STARTING_WINDOW_TYPE_NONE; - } - - /** - * Returns {@code true} if the task snapshot is compatible with this activity (at least the - * rotation must be the same). - */ - private boolean isSnapshotCompatible(StartingWindowInfo windowInfo, TaskSnapshot snapshot) { - if (snapshot == null) { - if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { - Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId); - } - return false; - } - if (!snapshot.getTopActivityComponent().equals(windowInfo.taskInfo.topActivity)) { - if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { - Slog.d(TAG, "isSnapshotCompatible obsoleted snapshot " - + windowInfo.taskInfo.topActivity); - } - return false; - } - - final int taskRotation = windowInfo.taskInfo.configuration - .windowConfiguration.getRotation(); - final int snapshotRotation = snapshot.getRotation(); - if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { - Slog.d(TAG, "isSnapshotCompatible rotation " + taskRotation - + " snapshot " + snapshotRotation); - } - return taskRotation == snapshotRotation; - } - } - /* * Registers the starting window listener. * @@ -212,7 +117,8 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { mSplashScreenExecutor.execute(() -> { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow"); - final int suggestionType = mStartingTypeChecker.estimateStartingWindowType( + + final int suggestionType = mStartingWindowTypeAlgorithm.getSuggestedWindowType( windowInfo); final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo; if (mTaskLaunchingCallback != null && shouldSendToListener(suggestionType)) { @@ -228,8 +134,10 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo final TaskSnapshot snapshot = windowInfo.mTaskSnapshot; mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot); + } else /* suggestionType == STARTING_WINDOW_TYPE_NONE */ { + // Don't add a staring window. } - // If prefer don't show, then don't show! + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); }); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowTypeAlgorithm.java new file mode 100644 index 000000000000..de221ed4cea7 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowTypeAlgorithm.java @@ -0,0 +1,30 @@ +/* + * 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.wm.shell.startingsurface; + +import android.window.StartingWindowInfo; + +/** + * Used by {@link StartingWindowController} for determining the type of a new starting window. + */ +public interface StartingWindowTypeAlgorithm { + /** + * @return suggested type for the given window. + */ + @StartingWindowInfo.StartingWindowType + int getSuggestedWindowType(StartingWindowInfo windowInfo); +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java new file mode 100644 index 000000000000..9948e7d1f9c4 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java @@ -0,0 +1,123 @@ +/* + * 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.wm.shell.startingsurface.phone; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_SAME_PACKAGE; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; + +import static com.android.wm.shell.startingsurface.StartingWindowController.DEBUG_SPLASH_SCREEN; +import static com.android.wm.shell.startingsurface.StartingWindowController.DEBUG_TASK_SNAPSHOT; + +import android.util.Slog; +import android.window.StartingWindowInfo; +import android.window.TaskSnapshot; + +import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm; + +/** + * Algorithm for determining the type of a new starting window on handheld devices. + * At the moment also used on Android Auto. + */ +public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgorithm { + private static final String TAG = PhoneStartingWindowTypeAlgorithm.class.getSimpleName(); + + @Override + public int getSuggestedWindowType(StartingWindowInfo windowInfo) { + final int parameter = windowInfo.startingWindowTypeParameter; + final boolean newTask = (parameter & TYPE_PARAMETER_NEW_TASK) != 0; + final boolean taskSwitch = (parameter & TYPE_PARAMETER_TASK_SWITCH) != 0; + final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0; + final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0; + final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0; + final boolean samePackage = (parameter & TYPE_PARAMETER_SAME_PACKAGE) != 0; + final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME; + + if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { + Slog.d(TAG, "preferredStartingWindowType newTask " + newTask + + " taskSwitch " + taskSwitch + + " processRunning " + processRunning + + " allowTaskSnapshot " + allowTaskSnapshot + + " activityCreated " + activityCreated + + " samePackage " + samePackage + + " topIsHome " + topIsHome); + } + if (!topIsHome) { + if (!processRunning) { + return STARTING_WINDOW_TYPE_SPLASH_SCREEN; + } + if (newTask) { + if (samePackage) { + return STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; + } else { + return STARTING_WINDOW_TYPE_SPLASH_SCREEN; + } + } + if (taskSwitch && !activityCreated) { + return STARTING_WINDOW_TYPE_SPLASH_SCREEN; + } + } + if (taskSwitch && allowTaskSnapshot) { + if (isSnapshotCompatible(windowInfo)) { + return STARTING_WINDOW_TYPE_SNAPSHOT; + } + if (!topIsHome) { + return STARTING_WINDOW_TYPE_SPLASH_SCREEN; + } + } + return STARTING_WINDOW_TYPE_NONE; + } + + + /** + * Returns {@code true} if the task snapshot is compatible with this activity (at least the + * rotation must be the same). + */ + private boolean isSnapshotCompatible(StartingWindowInfo windowInfo) { + final TaskSnapshot snapshot = windowInfo.mTaskSnapshot; + if (snapshot == null) { + if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { + Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId); + } + return false; + } + if (!snapshot.getTopActivityComponent().equals(windowInfo.taskInfo.topActivity)) { + if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { + Slog.d(TAG, "isSnapshotCompatible obsoleted snapshot " + + windowInfo.taskInfo.topActivity); + } + return false; + } + + final int taskRotation = windowInfo.taskInfo.configuration + .windowConfiguration.getRotation(); + final int snapshotRotation = snapshot.getRotation(); + if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { + Slog.d(TAG, "isSnapshotCompatible rotation " + taskRotation + + " snapshot " + snapshotRotation); + } + return taskRotation == snapshotRotation; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/tv/TvStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/tv/TvStartingWindowTypeAlgorithm.java new file mode 100644 index 000000000000..6e7dec590308 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/tv/TvStartingWindowTypeAlgorithm.java @@ -0,0 +1,35 @@ +/* + * 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.wm.shell.startingsurface.tv; + +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; + +import android.window.StartingWindowInfo; + +import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm; + +/** + * Algorithm for determining the type of a new starting window on Android TV. + * For now we always show empty splash screens on Android TV. + */ +public class TvStartingWindowTypeAlgorithm implements StartingWindowTypeAlgorithm { + @Override + public int getSuggestedWindowType(StartingWindowInfo windowInfo) { + // For now we want to always show empty splash screens on TV. + return STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index b29b18bec032..c6fb5af7d4be 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -18,9 +18,14 @@ package com.android.wm.shell.transition; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_RELAUNCH; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION; +import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; @@ -64,6 +69,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { /** Keeps track of the currently-running animations associated with each transition. */ private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>(); + private final Rect mInsets = new Rect(0, 0, 0, 0); private float mTransitionAnimationScaleSetting = 1.0f; DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context, @@ -111,7 +117,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { // Don't animate anything that isn't independent. if (!TransitionInfo.isIndependent(change, info)) continue; - Animation a = loadAnimation(info.getType(), change); + Animation a = loadAnimation(info.getType(), info.getFlags(), change); if (a != null) { startAnimInternal(animations, a, change.getLeash(), onAnimFinish); } @@ -135,47 +141,69 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } @Nullable - private Animation loadAnimation(int type, TransitionInfo.Change change) { + private Animation loadAnimation(int type, int flags, TransitionInfo.Change change) { // TODO(b/178678389): It should handle more type animation here Animation a = null; final boolean isOpening = Transitions.isOpeningType(type); - final int mode = change.getMode(); - final int flags = change.getFlags(); + final int changeMode = change.getMode(); + final int changeFlags = change.getFlags(); - if (mode == TRANSIT_OPEN && isOpening) { - if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { + if (type == TRANSIT_RELAUNCH) { + a = mTransitionAnimation.createRelaunchAnimation( + change.getStartAbsBounds(), mInsets, change.getEndAbsBounds()); + } else if (type == TRANSIT_KEYGUARD_GOING_AWAY) { + a = mTransitionAnimation.loadKeyguardExitAnimation(flags, + (changeFlags & FLAG_SHOW_WALLPAPER) != 0); + } else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) { + a = mTransitionAnimation.loadKeyguardUnoccludeAnimation(); + } else if (changeMode == TRANSIT_OPEN && isOpening) { + if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { // This received a transferred starting window, so don't animate return null; } - if (change.getTaskInfo() != null) { + if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) { + a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */); + } else if (change.getTaskInfo() != null) { a = mTransitionAnimation.loadDefaultAnimationAttr( R.styleable.WindowAnimation_taskOpenEnterAnimation); } else { - a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0 + a = mTransitionAnimation.loadDefaultAnimationRes( + (changeFlags & FLAG_TRANSLUCENT) == 0 ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter); } - } else if (mode == TRANSIT_TO_FRONT && isOpening) { - if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { + } else if (changeMode == TRANSIT_TO_FRONT && isOpening) { + if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { // This received a transferred starting window, so don't animate return null; } - a = mTransitionAnimation.loadDefaultAnimationAttr( - R.styleable.WindowAnimation_taskToFrontEnterAnimation); - } else if (mode == TRANSIT_CLOSE && !isOpening) { - if (change.getTaskInfo() != null) { + if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) { + a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */); + } else { + a = mTransitionAnimation.loadDefaultAnimationAttr( + R.styleable.WindowAnimation_taskToFrontEnterAnimation); + } + } else if (changeMode == TRANSIT_CLOSE && !isOpening) { + if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) { + a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */); + } else if (change.getTaskInfo() != null) { a = mTransitionAnimation.loadDefaultAnimationAttr( R.styleable.WindowAnimation_taskCloseExitAnimation); } else { - a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0 + a = mTransitionAnimation.loadDefaultAnimationRes( + (changeFlags & FLAG_TRANSLUCENT) == 0 ? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit); } - } else if (mode == TRANSIT_TO_BACK && !isOpening) { - a = mTransitionAnimation.loadDefaultAnimationAttr( - R.styleable.WindowAnimation_taskToBackExitAnimation); - } else if (mode == TRANSIT_CHANGE) { + } else if (changeMode == TRANSIT_TO_BACK && !isOpening) { + if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) { + a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */); + } else { + a = mTransitionAnimation.loadDefaultAnimationAttr( + R.styleable.WindowAnimation_taskToBackExitAnimation); + } + } else if (changeMode == TRANSIT_CHANGE) { // In the absence of a specific adapter, we just want to keep everything stationary. a = new AlphaAnimation(1.f, 1.f); a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index 2609258e48e0..a7e1d0fdf90c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -47,6 +47,12 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } + @FlakyTest + @Test + override fun noUncoveredRegions() { + super.noUncoveredRegions() + } + @Presubmit @Test fun pipAppWindowAlwaysVisible() { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index 105bd828aa9e..c1282c9d7b12 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -16,6 +16,9 @@ package com.android.wm.shell.onehanded; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -61,6 +64,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { OneHandedAccessibilityUtil mOneHandedAccessibilityUtil; OneHandedController mSpiedOneHandedController; OneHandedTimeoutHandler mSpiedTimeoutHandler; + OneHandedState mSpiedTransitionState; @Mock DisplayController mMockDisplayController; @@ -99,9 +103,9 @@ public class OneHandedControllerTest extends OneHandedTestCase { mDisplay = mContext.getDisplay(); mDisplayLayout = new DisplayLayout(mContext, mDisplay); mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor)); + mSpiedTransitionState = spy(new OneHandedState()); when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash); when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn( @@ -129,6 +133,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { mMockSettingsUitl, mOneHandedAccessibilityUtil, mSpiedTimeoutHandler, + mSpiedTransitionState, mMockUiEventLogger, mMockOverlayManager, mMockTaskStackListener, @@ -139,18 +144,13 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testDefaultShouldNotInOneHanded() { - final OneHandedAnimationController animationController = new OneHandedAnimationController( - mContext); - OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer( - mContext, mDisplayLayout, mMockSettingsUitl, animationController, - mMockTutorialHandler, mMockBackgroundOrganizer, mMockShellMainExecutor); - - assertThat(displayAreaOrganizer.isInOneHanded()).isFalse(); + // Assert default transition state is STATE_NONE + assertThat(mSpiedTransitionState.getState()).isEqualTo(STATE_NONE); } @Test public void testStartOneHandedShouldTriggerScheduleOffset() { - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.startOneHanded(); @@ -160,7 +160,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testStartOneHandedShouldNotTriggerScheduleOffset() { mSpiedOneHandedController.setOneHandedEnabled(true); - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(true); + mSpiedTransitionState.setState(STATE_ENTERING); mSpiedOneHandedController.startOneHanded(); verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); @@ -168,7 +168,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testStopOneHanded() { - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); mSpiedOneHandedController.stopOneHanded(); verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); @@ -192,7 +192,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testStopOneHandedShouldRemoveTimer() { - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(true); + mSpiedTransitionState.setState(STATE_ENTERING); mSpiedOneHandedController.stopOneHanded(); verify(mSpiedTimeoutHandler, atLeastOnce()).removeTimer(); @@ -280,7 +280,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testKeyguardShowingLockOneHandedDisabled() { - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.setLockedDisabled(true /* locked */, false /* enabled */); mSpiedOneHandedController.startOneHanded(); @@ -290,7 +290,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testResetKeyguardShowingLockOneHandedDisabled() { - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */); mSpiedOneHandedController.startOneHanded(); @@ -302,7 +302,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { public void testRotation90CanNotStartOneHanded() { final DisplayLayout landscapeDisplayLayout = new DisplayLayout(mDisplayLayout); landscapeDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90); - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(landscapeDisplayLayout); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */); @@ -315,7 +315,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { public void testRotation180CanStartOneHanded() { final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout); testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180); - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */); @@ -328,7 +328,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { public void testRotation270CanNotStartOneHanded() { final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout); testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270); - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedTransitionState.setState(STATE_NONE); when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java new file mode 100644 index 000000000000..89aae652444f --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java @@ -0,0 +1,221 @@ +/* + * 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.wm.shell.onehanded; + +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.om.IOverlayManager; +import android.graphics.Rect; +import android.os.Handler; +import android.os.UserHandle; +import android.testing.AndroidTestingRunner; +import android.util.ArrayMap; +import android.view.Display; +import android.view.SurfaceControl; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.TaskStackListenerImpl; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class OneHandedStateTest extends OneHandedTestCase { + private int mCurrentUser = UserHandle.myUserId(); + + Display mDisplay; + DisplayLayout mDisplayLayout; + OneHandedAccessibilityUtil mOneHandedAccessibilityUtil; + OneHandedController mSpiedOneHandedController; + OneHandedTimeoutHandler mSpiedTimeoutHandler; + OneHandedState mSpiedState; + + @Mock + DisplayController mMockDisplayController; + @Mock + OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer; + @Mock + OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; + @Mock + OneHandedTouchHandler mMockTouchHandler; + @Mock + OneHandedTutorialHandler mMockTutorialHandler; + @Mock + OneHandedGestureHandler mMockGestureHandler; + @Mock + OneHandedSettingsUtil mMockSettingsUitl; + @Mock + OneHandedUiEventLogger mMockUiEventLogger; + @Mock + IOverlayManager mMockOverlayManager; + @Mock + TaskStackListenerImpl mMockTaskStackListener; + @Mock + ShellExecutor mMockShellMainExecutor; + @Mock + SurfaceControl mMockLeash; + @Mock + Handler mMockShellMainHandler; + + final boolean mDefaultEnabled = true; + final boolean mDefaultSwipeToNotificationEnabled = false; + final boolean mDefaultTapAppToExitEnabled = true; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mDisplay = mContext.getDisplay(); + mDisplayLayout = new DisplayLayout(mContext, mDisplay); + mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor)); + mSpiedState = spy(new OneHandedState()); + + when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); + when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); + when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash); + when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn( + mDefaultEnabled); + when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn( + OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); + when(mMockSettingsUitl.getSettingsTapsAppToExit(any(), anyInt())).thenReturn( + mDefaultTapAppToExitEnabled); + when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn( + mDefaultSwipeToNotificationEnabled); + + when(mMockDisplayAreaOrganizer.getLastDisplayBounds()).thenReturn( + new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height())); + when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(mDisplayLayout); + + mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext); + mSpiedOneHandedController = spy(new OneHandedController( + mContext, + mMockDisplayController, + mMockBackgroundOrganizer, + mMockDisplayAreaOrganizer, + mMockTouchHandler, + mMockTutorialHandler, + mMockGestureHandler, + mMockSettingsUitl, + mOneHandedAccessibilityUtil, + mSpiedTimeoutHandler, + mSpiedState, + mMockUiEventLogger, + mMockOverlayManager, + mMockTaskStackListener, + mMockShellMainExecutor, + mMockShellMainHandler) + ); + } + + @Test + public void testState_stateEntering_isTransitioning() { + mSpiedState.setState(STATE_ENTERING); + + assertThat(mSpiedState.isTransitioning()).isTrue(); + } + + @Test + public void testState_stateExiting_isTransitioning() { + mSpiedState.setState(STATE_EXITING); + + assertThat(mSpiedState.isTransitioning()).isTrue(); + } + + @Test + public void testInEnteringState_shouldSkipDupTrigger() { + when(mSpiedState.getState()).thenReturn(STATE_ENTERING); + when(mSpiedState.isTransitioning()).thenReturn(true); + when(mSpiedState.isInOneHanded()).thenReturn(false); + mSpiedOneHandedController.startOneHanded(); + + verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); + } + + @Test + public void testInActiveState_shouldSkipDupTrigger() { + when(mSpiedState.getState()).thenReturn(STATE_ACTIVE); + when(mSpiedState.isTransitioning()).thenReturn(false); + when(mSpiedState.isInOneHanded()).thenReturn(true); + mSpiedOneHandedController.startOneHanded(); + + verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); + } + + @Test + public void testInActiveState_canExit() { + when(mSpiedState.getState()).thenReturn(STATE_ACTIVE); + when(mSpiedState.isTransitioning()).thenReturn(false); + mSpiedOneHandedController.stopOneHanded(); + + verify(mSpiedState).setState(STATE_EXITING); + } + + @Test + public void testInEnteringState_shouldSkipExitAction() { + when(mSpiedState.getState()).thenReturn(STATE_ENTERING); + when(mSpiedState.isTransitioning()).thenReturn(true); + mSpiedOneHandedController.stopOneHanded(); + + verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); + } + + @Test + public void testInExitingState_shouldSkipStartAction() { + when(mSpiedState.getState()).thenReturn(STATE_EXITING); + when(mSpiedState.isTransitioning()).thenReturn(true); + mSpiedOneHandedController.startOneHanded(); + + verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); + } + + @Test + public void testInExitingState_shouldSkipStopAction() { + when(mSpiedState.getState()).thenReturn(STATE_EXITING); + when(mSpiedState.isTransitioning()).thenReturn(true); + mSpiedOneHandedController.stopOneHanded(); + + verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); + } + + @Test + public void testInActiveState_disableOHM_shouldStopOHM() { + when(mSpiedState.getState()).thenReturn(STATE_ACTIVE); + when(mSpiedState.isTransitioning()).thenReturn(false); + when(mSpiedState.isInOneHanded()).thenReturn(true); + mSpiedOneHandedController.setOneHandedEnabled(false); + + verify(mMockShellMainExecutor).execute(any()); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java index 06a66717ed31..b82a8ca4ffa0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java @@ -41,6 +41,7 @@ import org.mockito.MockitoAnnotations; public class OneHandedTutorialHandlerTest extends OneHandedTestCase { OneHandedTimeoutHandler mTimeoutHandler; OneHandedController mOneHandedController; + OneHandedState mSpiedTransitionState; @Mock OneHandedGestureHandler mMockGestureHandler; @@ -73,6 +74,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { public void setUp() { MockitoAnnotations.initMocks(this); mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor); + mSpiedTransitionState = new OneHandedState(); when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); mOneHandedController = new OneHandedController( @@ -86,6 +88,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { mMockSettingsUtil, mMockAccessibilityUtil, mTimeoutHandler, + mSpiedTransitionState, mMockUiEventLogger, mMockOverlayManager, mMockTaskStackListener, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java index 63b94139dd9c..882d38286130 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java @@ -18,6 +18,7 @@ package com.android.wm.shell.pip; import static android.util.RotationUtils.rotateBounds; import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP; @@ -133,17 +134,30 @@ public class PipAnimationControllerTest extends ShellTestCase { @Test public void pipTransitionAnimator_rotatedEndValue() { + final DummySurfaceControlTx tx = new DummySurfaceControlTx(); final Rect startBounds = new Rect(200, 700, 400, 800); final Rect endBounds = new Rect(0, 0, 500, 1000); - final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController + // Fullscreen to PiP. + PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, null, startBounds, endBounds, null, - TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_90); + TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_90); // Apply fraction 1 to compute the end value. - animator.applySurfaceControlTransaction(mLeash, new DummySurfaceControlTx(), 1); + animator.applySurfaceControlTransaction(mLeash, tx, 1); final Rect rotatedEndBounds = new Rect(endBounds); rotateBounds(rotatedEndBounds, endBounds, ROTATION_90); assertEquals("Expect 90 degree rotated bounds", rotatedEndBounds, animator.mCurrentValue); + + // PiP to fullscreen. + startBounds.set(0, 0, 1000, 500); + endBounds.set(200, 100, 400, 500); + animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, startBounds, startBounds, + endBounds, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_270); + animator.applySurfaceControlTransaction(mLeash, tx, 1); + rotatedEndBounds.set(endBounds); + rotateBounds(rotatedEndBounds, startBounds, ROTATION_270); + + assertEquals("Expect 270 degree rotated bounds", rotatedEndBounds, animator.mCurrentValue); } @Test diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp index c2642d3fdb55..6eb6e1ee4a5c 100644 --- a/libs/hwui/effects/StretchEffect.cpp +++ b/libs/hwui/effects/StretchEffect.cpp @@ -237,7 +237,7 @@ sk_sp<SkShader> StretchEffect::getShader(const sk_sp<SkImage>& snapshotImage) co } sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() { - const static SkRuntimeEffect::Result instance = SkRuntimeEffect::Make(stretchShader); + const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(stretchShader); return instance.effect; } diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index 2e4d7f62f671..90184432e8a4 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -239,7 +239,8 @@ static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr, static jlong RuntimeShader_createShaderBuilder(JNIEnv* env, jobject, jstring sksl) { ScopedUtfChars strSksl(env, sksl); - auto result = SkRuntimeEffect::Make(SkString(strSksl.c_str()), SkRuntimeEffect::Options{}); + auto result = SkRuntimeEffect::MakeForShader(SkString(strSksl.c_str()), + SkRuntimeEffect::Options{}); if (result.effect.get() == nullptr) { doThrowIAE(env, result.errorText.c_str()); return 0; diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp index f322bff8e6ee..547d7192090a 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -104,7 +104,7 @@ cc_test { name: "libincident_test", test_config: "AndroidTest.xml", defaults: ["libincidentpriv_defaults"], - test_suites: ["device-tests", "mts-statsd"], + test_suites: ["device-tests"], compile_multilib: "both", multilib: { lib64: { diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java index 307fb8783c51..51586d708018 100644 --- a/location/java/android/location/Geocoder.java +++ b/location/java/android/location/Geocoder.java @@ -45,6 +45,10 @@ import java.util.concurrent.TimeUnit; * empty list if there no backend service in the platform. Use the * isPresent() method to determine whether a Geocoder implementation * exists. + * + * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on + * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful or + * correct. Do not use this API for any safety-critical or regulatory compliance purpose. */ public final class Geocoder { @@ -95,15 +99,15 @@ public final class Geocoder { } /** - * Returns an array of Addresses that are known to describe the - * area immediately surrounding the given latitude and longitude. - * The returned addresses will be localized for the locale - * provided to this class's constructor. + * Returns an array of Addresses that attempt to describe the area immediately surrounding the + * given latitude and longitude. The returned addresses should be localized for the locale + * provided to this class's constructor. Results may be obtained by means of a network lookup + * and this method may take some time to return, and so should not be called on the main thread. * - * <p> The returned values may be obtained by means of a network lookup. - * The results are a best guess and are not guaranteed to be meaningful or - * correct. It may be useful to call this method from a thread separate from your - * primary UI thread. + * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on + * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful + * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance + * purposes. * * @param latitude the latitude a point for the search * @param longitude the longitude a point for the search @@ -134,17 +138,17 @@ public final class Geocoder { } /** - * Returns an array of Addresses that are known to describe the - * named location, which may be a place name such as "Dalvik, - * Iceland", an address such as "1600 Amphitheatre Parkway, - * Mountain View, CA", an airport code such as "SFO", etc.. The - * returned addresses will be localized for the locale provided to - * this class's constructor. - * - * <p> The query will block and returned values will be obtained by means of a network lookup. - * The results are a best guess and are not guaranteed to be meaningful or - * correct. It may be useful to call this method from a thread separate from your - * primary UI thread. + * Returns an array of Addresses that attempt to describe the named location, which may be a + * place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain + * View, CA", an airport code such as "SFO", and so forth. The returned addresses should be + * localized for the locale provided to this class's constructor. Results may be obtained by + * means of a network lookup and this method may take some time to return, and so should not be + * called on the main thread. + * + * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on + * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful + * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance + * purposes. * * @param locationName a user-supplied description of a location * @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended @@ -161,21 +165,20 @@ public final class Geocoder { } /** - * Returns an array of Addresses that are known to describe the - * named location, which may be a place name such as "Dalvik, - * Iceland", an address such as "1600 Amphitheatre Parkway, - * Mountain View, CA", an airport code such as "SFO", etc.. The - * returned addresses will be localized for the locale provided to - * this class's constructor. - * - * <p> You may specify a bounding box for the search results by including - * the Latitude and Longitude of the Lower Left point and Upper Right - * point of the box. - * - * <p> The query will block and returned values will be obtained by means of a network lookup. - * The results are a best guess and are not guaranteed to be meaningful or - * correct. It may be useful to call this method from a thread separate from your - * primary UI thread. + * Returns an array of Addresses that attempt to describe the named location, which may be a + * place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain + * View, CA", an airport code such as "SFO", and so forth. The returned addresses should be + * localized for the locale provided to this class's constructor. Results may be obtained by + * means of a network lookup and this method may take some time to return, and so should not be + * called on the main thread. + * + * <p> You may specify a bounding box for the search results by including the latitude and + * longitude of the lower left point and upper right point of the box. + * + * <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on + * availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful + * or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance + * purposes. * * @param locationName a user-supplied description of a location * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended diff --git a/native/android/Android.bp b/native/android/Android.bp index 5bb4fbc85bf9..24c93ce63b1e 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -64,7 +64,7 @@ cc_library_shared { "surface_texture.cpp", "system_fonts.cpp", "trace.cpp", - "thermal.cpp" + "thermal.cpp", ], shared_libs: [ @@ -77,7 +77,7 @@ cc_library_shared { "libbinder", "libui", "libgui", - "libharfbuzz_ng", // Only for including hb.h via minikin + "libharfbuzz_ng", // Only for including hb.h via minikin "libsensor", "libactivitymanager_aidl", "libandroid_runtime", @@ -98,7 +98,10 @@ cc_library_shared { "libarect", ], - header_libs: [ "libhwui_internal_headers", "libandroid_headers_private"], + header_libs: [ + "libhwui_internal_headers", + "libandroid_headers_private", + ], whole_static_libs: ["libnativewindow"], @@ -106,14 +109,17 @@ cc_library_shared { include_dirs: ["bionic/libc/dns/include"], - local_include_dirs: [ "include_platform", ], + local_include_dirs: ["include_platform"], - export_include_dirs: [ "include_platform", ], + export_include_dirs: ["include_platform"], version_script: "libandroid.map.txt", stubs: { symbol_file: "libandroid.map.txt", - versions: ["29", "31"], + versions: [ + "29", + "31", + ], }, } @@ -136,27 +142,26 @@ llndk_library { unversioned: true, } - // Aidl library for platform compat. cc_library_shared { name: "lib-platform-compat-native-api", cflags: [ - "-Wall", - "-Werror", - "-Wno-missing-field-initializers", - "-Wno-unused-variable", - "-Wunused-parameter", - ], + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-unused-variable", + "-Wunused-parameter", + ], shared_libs: [ "libbinder", - "libutils", + "libutils", ], aidl: { local_include_dirs: ["aidl"], export_aidl_headers: true, }, srcs: [ - ":platform-compat-native-aidl", + ":platform-compat-native-aidl", ], export_include_dirs: ["aidl"], } diff --git a/packages/Android.bp b/packages/Android.bp index 8b0698bc4339..00300150e501 100644 --- a/packages/Android.bp +++ b/packages/Android.bp @@ -13,6 +13,15 @@ // limitations under the License. // Defaults for platform apps +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + java_defaults { name: "platform_app_defaults", plugins: ["error_prone_android_framework"], diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt index c8b04a3f63f1..a8e2517a5e90 100644 --- a/packages/Connectivity/framework/api/module-lib-current.txt +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -11,7 +11,7 @@ package android.net { method @Nullable public android.net.ProxyInfo getGlobalProxy(); method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange(); method @NonNull public static String getPrivateDnsMode(@NonNull android.content.Context); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerDefaultNetworkCallbackAsUid(int, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerDefaultNetworkCallbackForUid(int, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); method @Deprecated public boolean requestRouteToHostAddress(int, java.net.InetAddress); @@ -166,11 +166,11 @@ package android.net { public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo { ctor public VpnTransportInfo(int, @Nullable String); method public int describeContents(); + method @Nullable public String getSessionId(); + method public int getType(); method @NonNull public android.net.VpnTransportInfo makeCopy(long); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR; - field @Nullable public final String sessionId; - field public final int type; } } diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt index 3ca74756df64..52673c9267d3 100644 --- a/packages/Connectivity/framework/api/system-current.txt +++ b/packages/Connectivity/framework/api/system-current.txt @@ -240,7 +240,7 @@ package android.net { method public final void sendSocketKeepaliveEvent(int, int); method @Deprecated public void setLegacySubtype(int, @NonNull String); method public void setLingerDuration(@NonNull java.time.Duration); - method public void setTeardownDelayMs(@IntRange(from=0, to=0x1388) int); + method public void setTeardownDelayMillis(@IntRange(from=0, to=0x1388) int); method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>); method public void unregister(); field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2 diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 5dfa93226dff..164b984bcdc1 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -4473,7 +4473,7 @@ public class ConnectivityManager { @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback, @NonNull Handler handler) { - registerDefaultNetworkCallbackAsUid(Process.INVALID_UID, networkCallback, handler); + registerDefaultNetworkCallbackForUid(Process.INVALID_UID, networkCallback, handler); } /** @@ -4503,7 +4503,7 @@ public class ConnectivityManager { @RequiresPermission(anyOf = { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) - public void registerDefaultNetworkCallbackAsUid(int uid, + public void registerDefaultNetworkCallbackForUid(int uid, @NonNull NetworkCallback networkCallback, @NonNull Handler handler) { CallbackHandler cbHandler = new CallbackHandler(handler); sendRequestForNetwork(uid, null /* need */, networkCallback, 0 /* timeoutMs */, diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java index 518d3f39d113..adcf338ba1b3 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java +++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java @@ -892,11 +892,11 @@ public abstract class NetworkAgent { * This method may be called at any time while the network is connected. It has no effect if * the network is already disconnected and the teardown delay timer is running. * - * @param teardownDelayMs the teardown delay to set, or 0 to disable teardown delay. + * @param teardownDelayMillis the teardown delay to set, or 0 to disable teardown delay. */ - public void setTeardownDelayMs( - @IntRange(from = 0, to = MAX_TEARDOWN_DELAY_MS) int teardownDelayMs) { - queueOrSendMessage(reg -> reg.sendTeardownDelayMs(teardownDelayMs)); + public void setTeardownDelayMillis( + @IntRange(from = 0, to = MAX_TEARDOWN_DELAY_MS) int teardownDelayMillis) { + queueOrSendMessage(reg -> reg.sendTeardownDelayMs(teardownDelayMillis)); } /** diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java index efd336377114..4071c9ab71b4 100644 --- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java +++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java @@ -40,10 +40,10 @@ import java.util.Objects; @SystemApi(client = MODULE_LIBRARIES) public final class VpnTransportInfo implements TransportInfo, Parcelable { /** Type of this VPN. */ - public final int type; + private final int mType; @Nullable - public final String sessionId; + private final String mSessionId; @Override public @RedactionType long getApplicableRedactions() { @@ -55,13 +55,28 @@ public final class VpnTransportInfo implements TransportInfo, Parcelable { */ @NonNull public VpnTransportInfo makeCopy(@RedactionType long redactions) { - return new VpnTransportInfo(type, - ((redactions & REDACT_FOR_NETWORK_SETTINGS) != 0) ? null : sessionId); + return new VpnTransportInfo(mType, + ((redactions & REDACT_FOR_NETWORK_SETTINGS) != 0) ? null : mSessionId); } public VpnTransportInfo(int type, @Nullable String sessionId) { - this.type = type; - this.sessionId = sessionId; + this.mType = type; + this.mSessionId = sessionId; + } + + /** + * Returns the session Id of this VpnTransportInfo. + */ + @Nullable + public String getSessionId() { + return mSessionId; + } + + /** + * Returns the type of this VPN. + */ + public int getType() { + return mType; } @Override @@ -69,17 +84,17 @@ public final class VpnTransportInfo implements TransportInfo, Parcelable { if (!(o instanceof VpnTransportInfo)) return false; VpnTransportInfo that = (VpnTransportInfo) o; - return (this.type == that.type) && TextUtils.equals(this.sessionId, that.sessionId); + return (this.mType == that.mType) && TextUtils.equals(this.mSessionId, that.mSessionId); } @Override public int hashCode() { - return Objects.hash(type, sessionId); + return Objects.hash(mType, mSessionId); } @Override public String toString() { - return String.format("VpnTransportInfo{type=%d, sessionId=%s}", type, sessionId); + return String.format("VpnTransportInfo{type=%d, sessionId=%s}", mType, mSessionId); } @Override @@ -89,8 +104,8 @@ public final class VpnTransportInfo implements TransportInfo, Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(type); - dest.writeString(sessionId); + dest.writeInt(mType); + dest.writeString(mSessionId); } public static final @NonNull Creator<VpnTransportInfo> CREATOR = diff --git a/packages/Connectivity/service/ServiceConnectivityResources/Android.bp b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp index 515498e4823a..f491cc762b3c 100644 --- a/packages/Connectivity/service/ServiceConnectivityResources/Android.bp +++ b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp @@ -31,6 +31,10 @@ android_app { apex_available: [ "com.android.tethering", ], - // TODO: use a dedicated cert once generated - certificate: "platform", + certificate: ":com.android.connectivity.resources.certificate", +} + +android_app_certificate { + name: "com.android.connectivity.resources.certificate", + certificate: "resources-certs/com.android.connectivity.resources", } diff --git a/packages/Connectivity/service/ServiceConnectivityResources/resources-certs/com.android.connectivity.resources.pk8 b/packages/Connectivity/service/ServiceConnectivityResources/resources-certs/com.android.connectivity.resources.pk8 Binary files differnew file mode 100644 index 000000000000..bfdc28b313ec --- /dev/null +++ b/packages/Connectivity/service/ServiceConnectivityResources/resources-certs/com.android.connectivity.resources.pk8 diff --git a/packages/Connectivity/service/ServiceConnectivityResources/resources-certs/com.android.connectivity.resources.x509.pem b/packages/Connectivity/service/ServiceConnectivityResources/resources-certs/com.android.connectivity.resources.x509.pem new file mode 100644 index 000000000000..70eca1cee1d0 --- /dev/null +++ b/packages/Connectivity/service/ServiceConnectivityResources/resources-certs/com.android.connectivity.resources.x509.pem @@ -0,0 +1,36 @@ +-----BEGIN CERTIFICATE----- +MIIGQzCCBCugAwIBAgIUZY8nxBMINp/79sziXU77MLPpEXowDQYJKoZIhvcNAQEL +BQAwga8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMSswKQYDVQQDDCJjb20uYW5kcm9pZC5jb25uZWN0aXZpdHkucmVzb3VyY2Vz +MSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMCAXDTIxMDQyMjA3 +MjkxMFoYDzQ3NTkwMzE5MDcyOTEwWjCBrzELMAkGA1UEBhMCVVMxEzARBgNVBAgM +CkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0Fu +ZHJvaWQxEDAOBgNVBAsMB0FuZHJvaWQxKzApBgNVBAMMImNvbS5hbmRyb2lkLmNv +bm5lY3Rpdml0eS5yZXNvdXJjZXMxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5k +cm9pZC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC361NT9qSz +h3uLcLBD67HNE1QX3ykwGyw8u7ExzqpsqLCzZsOCFRJQJY+CnrgNaAz0NXeNtx7D +Lpr9OCWWbG1KTQ/ANlR8g6xCqlAk4xdixsAnIlBUJB90+RlkcWrliEY7OwcqIu3x +/qe+5UR3irIFZOApNHOm760PjRl7VWAnYZC/PhkW0iKwnBuE96ddPIJc+KuiqCcP +KflgF4/jmbHTZ+5uvVV4qkfovc744HnQtQoCDoYR8WpsJv3YL5xrAv78o3WCRzx6 +xxB+eUlJpuyyfIee2lUCG4Ly4jgOsWaupnUglLDORnz/L8fhhnpv83wLal7E0Shx +sqvzZZbb1QLuwMWy++gfzdDvGWewES3BdSFp5NwYWXQGZWSkEEFbIiorKSurU1On +9OwB0jT/H2B/CAFKYJQ2V+hQ4I7PG+z9p7ZFNR6GZbZuhEr+Dpq1CwtI3W45izr3 +RJgcc2IP6Oj7/XC2MmKGMqZkybBWcvazdyAMHzk9EZIBT2Oru3dnOl3uVUUPeZRs +xRzqaA0MAlyj+GJ9uziEr3W1j+U1CFEnNWtlD/jqcTAwmaOsn1GhWyMAo1KOrJ/o +LcJvwk5P/0XEyeli7/DSUpGjYiAgWMHWCOn9s6aYw3YFb+A/SgX3/+FIDib/vHTX +i76JZfO0CfoKsbFDCH9KOMupHM9EO3ftQwIDAQABo1MwUTAdBgNVHQ4EFgQU/KGg +gmMqXD5YOe5+B0W+YezN9LcwHwYDVR0jBBgwFoAU/KGggmMqXD5YOe5+B0W+YezN +9LcwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAhr+AaNaIlRyM +WKyJ+2Aa35fH5e44Xr/xPpriM5HHxsj0evjMCODqCQ7kzfwSEmtXh5uZYYNKb/JP +ZMDHIFcYi1QCvm6E6YOd+Qn9CVxrwaDhigJv7ylhVf8q201GTvHhJIU99yFIrzJQ +RNhxw+pNo7FYMZr3J7JZPAy60DN1KZvRV4FjZx5qiPUMyu4zVygzDkr0v5Ilncdp +l9VVjOi7ocHyBKI+7RkXl97xN4SUe3vszwZQHCVyVopBw+YrMbDBCrknrQzUEgie +BuI+kj5oOeiQ0P1i1K+UCCAjrLwhNyc9H02rKUtBHxa2AVjw7YpAJlBesb49Qvq+ +5L6JjHFVSSOEbIjboNib26zNackjbiefF74meSUbGVGfcJ1OdkZsXZWphmER8V7X +Wz3Z8JwOXW1RLPgcbjilHUR5g8pEmWBv4KrTCSg5IvOJr4w3pyyMBiiVI9NI5sB7 +g5Mi9v3ifPD1OHA4Y3wYCb26mMEpRb8ogOhMHcGNbdnL3QtIUg4cmXGqGSY/LbpU +np0sIQDSjc46o79F0boPsLlaN3US5WZIu0nc9SHkjoNhd0CJQ5r9aEn4/wNrZgxs +s8OEKsqcS7OsWiIE6nG51TMDsCuyRBrGedtSUyFFSVSpivpYIrPVNKKlHsJ/o+Nv +Udb6dBjCraPvJB8binB1aojwya3MwRs= +-----END CERTIFICATE----- diff --git a/packages/Connectivity/service/ServiceConnectivityResources/resources-certs/key.pem b/packages/Connectivity/service/ServiceConnectivityResources/resources-certs/key.pem new file mode 100644 index 000000000000..38771c261518 --- /dev/null +++ b/packages/Connectivity/service/ServiceConnectivityResources/resources-certs/key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC361NT9qSzh3uL +cLBD67HNE1QX3ykwGyw8u7ExzqpsqLCzZsOCFRJQJY+CnrgNaAz0NXeNtx7DLpr9 +OCWWbG1KTQ/ANlR8g6xCqlAk4xdixsAnIlBUJB90+RlkcWrliEY7OwcqIu3x/qe+ +5UR3irIFZOApNHOm760PjRl7VWAnYZC/PhkW0iKwnBuE96ddPIJc+KuiqCcPKflg +F4/jmbHTZ+5uvVV4qkfovc744HnQtQoCDoYR8WpsJv3YL5xrAv78o3WCRzx6xxB+ +eUlJpuyyfIee2lUCG4Ly4jgOsWaupnUglLDORnz/L8fhhnpv83wLal7E0Shxsqvz +ZZbb1QLuwMWy++gfzdDvGWewES3BdSFp5NwYWXQGZWSkEEFbIiorKSurU1On9OwB +0jT/H2B/CAFKYJQ2V+hQ4I7PG+z9p7ZFNR6GZbZuhEr+Dpq1CwtI3W45izr3RJgc +c2IP6Oj7/XC2MmKGMqZkybBWcvazdyAMHzk9EZIBT2Oru3dnOl3uVUUPeZRsxRzq +aA0MAlyj+GJ9uziEr3W1j+U1CFEnNWtlD/jqcTAwmaOsn1GhWyMAo1KOrJ/oLcJv +wk5P/0XEyeli7/DSUpGjYiAgWMHWCOn9s6aYw3YFb+A/SgX3/+FIDib/vHTXi76J +ZfO0CfoKsbFDCH9KOMupHM9EO3ftQwIDAQABAoICAQCXM/GKqtAXBIBOT/Ops0C2 +n3hYM9BRy1UgDRKNJyG3OSwkIY0ECbzHhUmpkkEwTGWx8675JB43Sr6DBUDpnPRw +zE/xrvjgcQQSvqAq40PbohhhU/WEZzoxWYVFrXS7hcBve4TVYGgMtlZEO4qBWNYo +Vxlu5r9Z89tsWI0ldzgYyD5O64eG2nVIit6Y/11p6pAmTQ4WKHYMIm7xUA2siTPH +4L8F7cQx8pQxxLI+q5WaPuweasBQShA7IAc7T1EiLRFitCOsWlJfgf6Oa7oTwhcA +Wh7JOyf+Fo4ejlqVwcTwOss6YOPGge7LgQWr5HoORbeqTuXgmy/L4Z85+EABNOs1 +5muHZvsuPXSmW6g1bCi8zvQcjFIX31yBVg8zkdG8WRezFxiVlN8UFAx4rwo03aBs +rDyU4GCxoUBvF/M9534l1gKOyr0hlQ40nQ4kBabbm2wWOKCVzmLEtFmWX9RV0tjX +pEtTCqgsGlsIypLy21+uow8SBojhkZ+xORCF2XivGu6SKtvwGvjpYXpXrI6DN4Lw +kH5J5FwSu1SNY8tnIEJEmj8IMTp+Vw20kwNVTcwdC2nJDDiezJum4PqZRdWIuupm +BWzXD3fvMXqHmT02sJTQ+FRAgiQLLWDzNAYMJUofzuIwycs4iO9MOPHjkHScvk4N +FXLrzFBSbdw+wi1DdzzMuQKCAQEA5wx07O5bHBHybs6tpwuZ0TuJ3OIVXh/ocNVR +gSOCSMirv+K4u3jToXwjfTXUc9lcn+DenZPpGmUWF0sZ83ytZm1eFVgGZpP6941C +waSeb8zGsgbEyZIQTVILfgtyPDwdtgu0d1Ip+ppj9czXmnxMY/ruHOX1Do1UfZoA +UA1ytHJSjFKU6saAhHrdk91soTVzc/E3uo7U4Ff0L8/3tT3DAEFYxDXUCH8W2IZZ +6zVvlqnPH4elxsPYM6rtIwq52reOTLNxC+SFSamK/82zu09Kjj5sQ6HKlvKJFiL5 +bULWu4lenoDfEN0lng+QopJTgZq4/tgOLum43C/Zd0PGC9R6PwKCAQEAy8fvPqwM +gPbNasni9qGVG+FfiFd/wEMlgKlVVqi+WzF6tCAnXCQXXg3A7FpLQrX8hVKdMznq +wPgM5AXP4FOguBFNk65chZmPizBIUDPJ4TNHI8FcGgcxbKGvDdVHsUpa/h5rJlvV +GLJTKV4KjcsTjl5tlRsJ48bSfpBNQHpSKlCswT6jjteiDY6Rln0GFKQIKDHqp3I6 +Zn1E4yfdiIz9VnMPfg1fbjBeR7s1zNzlrR8Dv9oK9tkzI5G1wSbdzksg2O1q2tvg +WrZrTAA3Uw6sPUMft0vk5Jw6a6CLkrcfayv3xDHwvM/4P3HgP8j9WQ8at8ttHpfD +oWyt3fZ3pBuj/QKCAQANqxH7tjoTlgg2f+mL+Ua3NwN32rQS5mZUznnM3vHlJmHq +rxnolURHyFU9IgMYe2JcXuwsfESM+C/vXtUBL33+kje/oX53cQemv2eUlw18ZavX +ekkH96kZOeJOKZUvdQr46wZZDLZJCfsh3mVe0T2fqIePlBcELl4yM/sSwUjo3d5+ +SKBgpy+RJseW6MF1Y/kZgcqfMbXsM6fRcEciJK41hKggq2KIwiPy2TfWj0mzqwYC +wn6PHKTcoZ73tLm786Hqba8hWfp8mhgL+/pG+XDaq1yyP48BkQWFFrqUuSCE5aKA +U/VeRQblq9wNkgR4pVOOV++23MK/2+DMimjb6Ez3AoIBABIXK7wKlgmU32ONjKKM +capJ9asq6WJuE5Q6dCL/U/bQi64V9KiPY6ur2OailW/UrBhB30a+64I6AxrzESM/ +CVON5a8omXoayc13edP05QUjAjvAXKbK4K5eJCY8OuMYUL+if6ymFmLc4dkYSiOQ +Vaob4+qKvfQEoIcv1EvXEBhFlTCKmQaDShWeBHqxmqqWbUr0M3qt/1U95bGsxlPr +AEp+aG+uTDyB+ryvd/U53wHhcPnFJ5gGbC3KL7J3+tTngoD/gq7vOhmTfC8BDehH +sy61GMmy6R0KaX1IgVuC+j0PaC14qYB5jfZD675930/asWqDmqpOmsVn2n+L888T +zRkCggEBAIMuNhhfGGY6E4PLUcPM0LZA4tI/wTpeYEahunU1hWIYo/iZB9od2biz +EeYY4BtkzCoE5ZWYXqTgiMxN4hJ4ufB+5umZ4BO0Gyx4p2/Ik2uv1BXu++GbM+TI +eeFmaBh00dTtjccpeZEDgNkjAO7Rh9GV2ifl3uhqg0MnFXywPUX2Vm2bmwQXnfV9 +wY2TXgOmBN2epFBOArJwiA5IfV+bSqXCFCx8fgyOWpMNq9+zDRd6KCeHyge54ahm +jMhCncp1OPDPaV+gnUdgWDGcywYg0KQvu5dLuCFfvucnsWoH2txsVZrXFha5XSM4 +/4Pif3Aj5E9dm1zkUtZJYQbII5SKQ94= +-----END PRIVATE KEY----- diff --git a/packages/SettingsLib/SearchWidget/res/values-es-rUS/strings.xml b/packages/SettingsLib/SearchWidget/res/values-es-rUS/strings.xml index 9b735fece386..d38510189507 100644 --- a/packages/SettingsLib/SearchWidget/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/SearchWidget/res/values-es-rUS/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="search_menu" msgid="1914043873178389845">"Buscar configuraciones"</string> + <string name="search_menu" msgid="1914043873178389845">"Buscar configuración"</string> </resources> diff --git a/packages/SettingsLib/SearchWidget/res/values-kn/strings.xml b/packages/SettingsLib/SearchWidget/res/values-kn/strings.xml index a492ec07f6c1..eccf6c7ae3a0 100644 --- a/packages/SettingsLib/SearchWidget/res/values-kn/strings.xml +++ b/packages/SettingsLib/SearchWidget/res/values-kn/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="search_menu" msgid="1914043873178389845">"ಹುಡುಕಾಟ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> + <string name="search_menu" msgid="1914043873178389845">"ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಹುಡುಕಿ"</string> </resources> diff --git a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java index 6560a181d352..ed447f873610 100644 --- a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java +++ b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java @@ -25,6 +25,7 @@ import android.view.animation.Interpolator; import androidx.core.os.BuildCompat; +import com.google.android.material.transition.platform.FadeThroughProvider; import com.google.android.material.transition.platform.MaterialSharedAxis; import com.google.android.material.transition.platform.SlideDistanceProvider; @@ -35,6 +36,7 @@ public class SettingsTransitionHelper { private static final String TAG = "SettingsTransitionHelper"; private static final long DURATION = 450L; + private static final float FADE_THROUGH_THRESHOLD = 0.22F; private static MaterialSharedAxis createSettingsSharedAxis(Context context, boolean forward) { final MaterialSharedAxis transition = new MaterialSharedAxis(MaterialSharedAxis.X, forward); @@ -48,12 +50,14 @@ public class SettingsTransitionHelper { forwardDistanceProvider.setSlideDistance(distance); transition.setDuration(DURATION); + final FadeThroughProvider fadeThroughProvider = + (FadeThroughProvider) transition.getSecondaryAnimatorProvider(); + fadeThroughProvider.setProgressThreshold(FADE_THROUGH_THRESHOLD); + final Interpolator interpolator = AnimationUtils.loadInterpolator(context, R.interpolator.fast_out_extra_slow_in); transition.setInterpolator(interpolator); - // TODO(b/177480673): Update fade through threshold once (cl/362065364) is released - return transition; } diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java index 0a8570b1b180..782b483848e6 100644 --- a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java +++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java @@ -40,7 +40,7 @@ import java.util.regex.Pattern; */ public class UsageProgressBarPreference extends Preference { - private final Pattern mNumberPattern = Pattern.compile("[\\d]*\\.?[\\d]+"); + private final Pattern mNumberPattern = Pattern.compile("[\\d]*[\\.,]?[\\d]+"); private CharSequence mUsageSummary; private CharSequence mTotalSummary; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 9c7aac135571..4558a8a3d33e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -24,6 +24,10 @@ import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Resources; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -32,12 +36,16 @@ import android.os.SystemClock; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; +import android.util.LruCache; +import android.util.Pair; import androidx.annotation.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.settingslib.R; import com.android.settingslib.Utils; +import com.android.settingslib.utils.ThreadUtils; +import com.android.settingslib.widget.AdaptiveOutlineDrawable; import java.util.ArrayList; import java.util.Collection; @@ -100,6 +108,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> private boolean mIsHearingAidProfileConnectedFail = false; // Group second device for Hearing Aid private CachedBluetoothDevice mSubDevice; + @VisibleForTesting + LruCache<String, BitmapDrawable> mDrawableCache; private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override @@ -131,6 +141,19 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> mDevice = device; fillData(); mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID; + initDrawableCache(); + } + + private void initDrawableCache() { + int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); + int cacheSize = maxMemory / 8; + + mDrawableCache = new LruCache<String, BitmapDrawable>(cacheSize) { + @Override + protected int sizeOf(String key, BitmapDrawable bitmap) { + return bitmap.getBitmap().getByteCount() / 1024; + } + }; } /** @@ -381,6 +404,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> if (dev != null) { final boolean successful = dev.removeBond(); if (successful) { + releaseLruCache(); if (BluetoothUtils.D) { Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null)); } @@ -500,7 +524,21 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } void refresh() { - dispatchAttributesChanged(); + ThreadUtils.postOnBackgroundThread(() -> { + if (BluetoothUtils.isAdvancedDetailsHeader(mDevice)) { + Uri uri = BluetoothUtils.getUriMetaData(getDevice(), + BluetoothDevice.METADATA_MAIN_ICON); + if (uri != null && mDrawableCache.get(uri.toString()) == null) { + mDrawableCache.put(uri.toString(), + (BitmapDrawable) BluetoothUtils.getBtDrawableWithDescription( + mContext, this).first); + } + } + + ThreadUtils.postOnMainThread(() -> { + dispatchAttributesChanged(); + }); + }); } public void setJustDiscovered(boolean justDiscovered) { @@ -1178,4 +1216,28 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> mSubDevice.mJustDiscovered = tmpJustDiscovered; fetchActiveDevices(); } + + /** + * Get cached bluetooth icon with description + */ + public Pair<Drawable, String> getDrawableWithDescription() { + Uri uri = BluetoothUtils.getUriMetaData(mDevice, BluetoothDevice.METADATA_MAIN_ICON); + if (BluetoothUtils.isAdvancedDetailsHeader(mDevice) && uri != null) { + BitmapDrawable drawable = mDrawableCache.get(uri.toString()); + if (drawable != null) { + Resources resources = mContext.getResources(); + return new Pair<>(new AdaptiveOutlineDrawable( + resources, drawable.getBitmap()), + BluetoothUtils.getBtClassDrawableWithDescription(mContext, this).second); + } + + refresh(); + } + + return BluetoothUtils.getBtRainbowDrawableWithDescription(mContext, this); + } + + void releaseLruCache() { + mDrawableCache.evictAll(); + } } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java index 42fb5d0daca4..a9ad00d90ab8 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java @@ -75,20 +75,41 @@ public class UsageProgressBarPreferenceTest { final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary); final SpannedString summary = new SpannedString(usageSummary.getText()); - assertThat(summary.getSpans(0, summary.length(), AbsoluteSizeSpan.class).length) - .isEqualTo(1); + final AbsoluteSizeSpan[] spans = summary + .getSpans(0, summary.length(), AbsoluteSizeSpan.class); + assertThat(spans.length).isEqualTo(1); + assertThat(summary.getSpanStart(spans[0])).isEqualTo(0); + assertThat(summary.getSpanEnd(spans[0])).isEqualTo(2); } @Test - public void setUsageSummary_floatNumber_findAbsoluteSizeSpan() { + public void setUsageSummary_floatingPointNumber_findAbsoluteSizeSpan() { mUsageProgressBarPreference.setUsageSummary("3.14Test"); mUsageProgressBarPreference.onBindViewHolder(mViewHolder); final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary); final SpannedString summary = new SpannedString(usageSummary.getText()); - assertThat(summary.getSpans(0, summary.length(), AbsoluteSizeSpan.class).length) - .isEqualTo(1); + final AbsoluteSizeSpan[] spans = summary + .getSpans(0, summary.length(), AbsoluteSizeSpan.class); + assertThat(spans.length).isEqualTo(1); + assertThat(summary.getSpanStart(spans[0])).isEqualTo(0); + assertThat(summary.getSpanEnd(spans[0])).isEqualTo(4); + } + + @Test + public void setUsageSummary_commaFloatingPointNumber_findAbsoluteSizeSpan() { + mUsageProgressBarPreference.setUsageSummary("3,14Test"); + + mUsageProgressBarPreference.onBindViewHolder(mViewHolder); + + final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary); + final SpannedString summary = new SpannedString(usageSummary.getText()); + final AbsoluteSizeSpan[] spans = summary + .getSpans(0, summary.length(), AbsoluteSizeSpan.class); + assertThat(spans.length).isEqualTo(1); + assertThat(summary.getSpanStart(spans[0])).isEqualTo(0); + assertThat(summary.getSpanEnd(spans[0])).isEqualTo(4); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index 53a99ab8cbe4..38172f7600a6 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -35,6 +35,7 @@ import android.media.AudioManager; import com.android.settingslib.R; import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settingslib.widget.AdaptiveOutlineDrawable; import org.junit.Before; import org.junit.Test; @@ -957,4 +958,41 @@ public class CachedBluetoothDeviceTest { // Should not crash } + + @Test + public void getDrawableWithDescription_isAdvancedDevice_returnAdvancedIcon() { + when(mDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)) + .thenReturn("fake_uri".getBytes()); + when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) + .thenReturn("true".getBytes()); + + mCachedDevice.refresh(); + + assertThat(mCachedDevice.getDrawableWithDescription().first).isInstanceOf( + AdaptiveOutlineDrawable.class); + } + + @Test + public void getDrawableWithDescription_isNotAdvancedDevice_returnBluetoothIcon() { + when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) + .thenReturn("false".getBytes()); + + mCachedDevice.refresh(); + + assertThat(mCachedDevice.getDrawableWithDescription().first).isNotInstanceOf( + AdaptiveOutlineDrawable.class); + } + + @Test + public void releaseLruCache_lruCacheShouldBeRelease() { + when(mDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)) + .thenReturn("fake_uri".getBytes()); + when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) + .thenReturn("true".getBytes()); + + mCachedDevice.refresh(); + mCachedDevice.releaseLruCache(); + + assertThat(mCachedDevice.mDrawableCache.size()).isEqualTo(0); + } } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index c0e4df54c560..8f7f1faa4d91 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -138,6 +138,7 @@ public class GlobalSettingsValidators { new InclusiveIntegerRangeValidator( /* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT, /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT)); + VALIDATORS.put(Global.DISABLE_WINDOW_BLURS, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 635434b5f7ba..2f54e2124247 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -553,6 +553,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, GlobalSettingsProto.Development.ENABLE_NON_RESIZABLE_MULTI_WINDOW); + dumpSetting(s, p, + Settings.Global.DISABLE_WINDOW_BLURS, + GlobalSettingsProto.Development.DISABLE_WINDOW_BLURS); p.end(developmentToken); final long deviceToken = p.start(GlobalSettingsProto.DEVICE); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 150d10db2692..22e38f4008da 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -231,6 +231,7 @@ public class SettingsBackupTest { Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_VR, Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH, Settings.Global.DEVICE_DEMO_MODE, + Settings.Global.DISABLE_WINDOW_BLURS, Settings.Global.BATTERY_SAVER_CONSTANTS, Settings.Global.BATTERY_TIP_CONSTANTS, Settings.Global.DEFAULT_SM_DP_PLUS, diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java index 4fe48c9dbdda..8ead0e1d053f 100644 --- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java +++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java @@ -16,8 +16,12 @@ package com.android.systemui.plugins; +import android.app.PendingIntent; +import android.app.smartspace.SmartspaceAction; import android.app.smartspace.SmartspaceTarget; +import android.content.Intent; import android.os.Parcelable; +import android.view.View; import android.view.ViewGroup; import com.android.systemui.plugins.annotations.ProvidesInterface; @@ -68,5 +72,33 @@ public interface BcSmartspaceDataPlugin extends Plugin { * Range [0.0 - 1.0] when transitioning from Lockscreen to/from AOD */ void setDozeAmount(float amount); + + /** + * Overrides how Intents/PendingIntents gets launched. Mostly to support auth from + * the lockscreen. + */ + void setIntentStarter(IntentStarter intentStarter); + + /** + * When on the lockscreen, use the FalsingManager to help detect errant touches + */ + void setFalsingManager(FalsingManager falsingManager); + } + + /** Interface for launching Intents, which can differ on the lockscreen */ + interface IntentStarter { + default void startFromAction(SmartspaceAction action, View v) { + if (action.getIntent() != null) { + startIntent(v, action.getIntent()); + } else if (action.getPendingIntent() != null) { + startPendingIntent(action.getPendingIntent()); + } + } + + /** Start the intent */ + void startIntent(View v, Intent i); + + /** Start the PendingIntent */ + void startPendingIntent(PendingIntent pi); } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java index 4142e517243f..b75252b46785 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java @@ -58,7 +58,6 @@ public interface FalsingManager { /** Returns true if the gesture should be rejected. */ boolean isFalseTouch(int interactionType); - /** * Does basic checking to see if gesture looks like a tap. * @@ -127,8 +126,19 @@ public interface FalsingManager { /** Removes a {@link FalsingBeliefListener}. */ void removeFalsingBeliefListener(FalsingBeliefListener listener); + /** Adds a {@link FalsingTapListener}. */ + void addTapListener(FalsingTapListener falsingTapListener); + + /** Removes a {@link FalsingTapListener}. */ + void removeTapListener(FalsingTapListener falsingTapListener); + /** Listener that is alerted when falsing belief level crosses a predfined threshold. */ interface FalsingBeliefListener { void onFalse(); } + + /** Listener that is alerted when a double tap is required to confirm a single tap. */ + interface FalsingTapListener { + void onDoubleTapRequired(); + } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java index fffcafbf88fb..4d4c909d2894 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java @@ -14,7 +14,6 @@ package com.android.systemui.plugins.qs; -import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; @@ -57,7 +56,6 @@ public interface QS extends FragmentBase { void setQsExpansion(float qsExpansionFraction, float headerTranslation); void setHeaderListening(boolean listening); void notifyCustomizeChanged(); - void setContainer(ViewGroup container); void setExpandClickListener(OnClickListener onClickListener); @@ -75,6 +73,16 @@ public interface QS extends FragmentBase { return isShowingDetail(); } + /** + * If QS should translate as we pull it down, or if it should be static. + */ + void setTranslateWhileExpanding(boolean shouldTranslate); + + /** + * A rounded corner clipping that makes QS feel as if it were behind everything. + */ + void setFancyClipping(int top, int bottom, int cornerRadius, boolean visible); + @ProvidesInterface(version = HeightListener.VERSION) interface HeightListener { int VERSION = 1; diff --git a/packages/SystemUI/res-keyguard/color/notification_background_dimmed_color.xml b/packages/SystemUI/res-keyguard/color/notification_background_dimmed_color.xml deleted file mode 100644 index 3345e6e42500..000000000000 --- a/packages/SystemUI/res-keyguard/color/notification_background_dimmed_color.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2020 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:alpha="0.7" android:color="?android:attr/colorBackground" /> -</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/notification_material_bg_dim.xml b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml deleted file mode 100644 index 1127d3c247fd..000000000000 --- a/packages/SystemUI/res/drawable/notification_material_bg_dim.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2014 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<ripple xmlns:android="http://schemas.android.com/apk/res/android"> - <item> - <shape> - <solid android:color="@color/notification_background_dimmed_color" /> - </shape> - </item> -</ripple> diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml index 7e944162fd0d..f4a743490181 100644 --- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml +++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml @@ -18,6 +18,7 @@ <!-- Layout for media recommendations inside QSPanel carousel --> <com.android.systemui.util.animation.TransitionLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/media_recommendations" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -84,4 +85,99 @@ android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" /> + <!-- Constraints are set here as they are the same regardless of host --> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/qs_media_panel_outer_padding" + android:layout_marginStart="@dimen/qs_media_panel_outer_padding" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:id="@+id/recommendation_text" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textColor="?android:attr/textColorSecondary" + android:text="@string/controls_media_title" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintBottom_toTopOf="@id/remove_text" + app:layout_constraintVertical_chainStyle="spread_inside"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/qs_media_panel_outer_padding" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:id="@+id/remove_text" + android:fontFamily="@*android:string/config_headlineFontFamily" + android:singleLine="true" + android:textColor="?android:attr/textColorPrimary" + android:text="@string/controls_media_close_session" + app:layout_constraintTop_toBottomOf="@id/recommendation_text" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintBottom_toTopOf="@id/settings"/> + + <FrameLayout + android:id="@+id/settings" + android:background="@drawable/qs_media_light_source" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/qs_media_panel_outer_padding" + android:paddingBottom="@dimen/qs_media_panel_outer_padding" + android:minWidth="48dp" + android:minHeight="48dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/remove_text"> + + <TextView + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textColor="?android:attr/textColorPrimary" + android:text="@string/controls_media_settings_button" /> + </FrameLayout> + + <FrameLayout + android:id="@+id/cancel" + android:background="@drawable/qs_media_light_source" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:paddingBottom="@dimen/qs_media_panel_outer_padding" + android:minWidth="48dp" + android:minHeight="48dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/dismiss" > + + <TextView + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textColor="?android:attr/textColorPrimary" + android:text="@string/cancel" /> + </FrameLayout> + + <FrameLayout + android:id="@+id/dismiss" + android:background="@drawable/qs_media_light_source" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:paddingBottom="@dimen/qs_media_panel_outer_padding" + android:minWidth="48dp" + android:minHeight="48dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent"> + + <TextView + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textColor="?android:attr/textColorPrimary" + android:text="@string/controls_media_dismiss_button" + /> + </FrameLayout> + </com.android.systemui.util.animation.TransitionLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml index 112509272e58..ea644cfca68a 100644 --- a/packages/SystemUI/res/layout/notification_conversation_info.xml +++ b/packages/SystemUI/res/layout/notification_conversation_info.xml @@ -24,7 +24,6 @@ android:clipChildren="true" android:clipToPadding="true" android:orientation="vertical" - android:background="?android:attr/colorBackground" android:paddingStart="12dp"> <!-- Package Info --> diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml index c90fc3151ee7..a5e7f5d4cfe6 100644 --- a/packages/SystemUI/res/layout/ongoing_call_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml @@ -23,7 +23,6 @@ android:background="@drawable/ongoing_call_chip_bg" android:paddingStart="@dimen/ongoing_call_chip_side_padding" android:paddingEnd="@dimen/ongoing_call_chip_side_padding" - android:visibility="gone" > <ImageView diff --git a/packages/SystemUI/res/layout/people_space_placeholder_layout.xml b/packages/SystemUI/res/layout/people_space_placeholder_layout.xml index 2402bd710eca..abb771b98667 100644 --- a/packages/SystemUI/res/layout/people_space_placeholder_layout.xml +++ b/packages/SystemUI/res/layout/people_space_placeholder_layout.xml @@ -26,7 +26,7 @@ android:orientation="horizontal" android:gravity="center" android:layout_gravity="top" - android:paddingVertical="16dp" + android:paddingVertical="8dp" android:paddingHorizontal="16dp" android:layout_width="match_parent" android:layout_height="match_parent"> diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index 343b398e3003..93a47154d927 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -50,7 +50,8 @@ android:focusable="true" android:gravity="center_vertical" android:singleLine="true" - android:textAppearance="@style/TextAppearance.QS.Status" + android:textColor="?android:attr/textColorSecondary" + android:textAppearance="@style/TextAppearance.QS.Build" android:visibility="gone" /> <com.android.systemui.qs.PageIndicator diff --git a/packages/SystemUI/res/layout/status_bar_notification_shelf.xml b/packages/SystemUI/res/layout/status_bar_notification_shelf.xml index 781c015b2bdc..87a1bbb2aa5c 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_shelf.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_shelf.xml @@ -27,10 +27,6 @@ android:id="@+id/backgroundNormal" android:layout_width="match_parent" android:layout_height="match_parent" /> - <com.android.systemui.statusbar.notification.row.NotificationBackgroundView - android:id="@+id/backgroundDimmed" - android:layout_width="match_parent" - android:layout_height="match_parent" /> <com.android.systemui.statusbar.phone.NotificationIconContainer android:id="@+id/content" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 12c864cfcad3..bea50e87a29a 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -43,7 +43,7 @@ android:visibility="invisible" /> </com.android.systemui.statusbar.BackDropView> - <com.android.systemui.statusbar.ScrimView + <com.android.systemui.scrim.ScrimView android:id="@+id/scrim_behind" android:layout_width="match_parent" android:layout_height="match_parent" @@ -51,7 +51,7 @@ sysui:ignoreRightInset="true" /> - <com.android.systemui.statusbar.ScrimView + <com.android.systemui.scrim.ScrimView android:id="@+id/scrim_notifications" android:layout_width="match_parent" android:layout_height="match_parent" @@ -72,7 +72,7 @@ <include layout="@layout/brightness_mirror_container" /> - <com.android.systemui.statusbar.ScrimView + <com.android.systemui.scrim.ScrimView android:id="@+id/scrim_in_front" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 0577d46e4402..fff4a1b74e56 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1456,6 +1456,12 @@ screen directly in front of the sensor. --> <dimen name="physical_fingerprint_sensor_center_screen_location_y">610px</dimen> + <!-- Normalized location on the screen of the center of the physical usb charger port in + portrait mode. This is a reasonable default that should be overridden by device-specific + overlays. --> + <item name="physical_charger_port_location_normalized_x" type="dimen" format="float">0.5</item> + <item name="physical_charger_port_location_normalized_y" type="dimen" format="float">1</item> + <!-- Wallet activity screen specs --> <dimen name="wallet_icon_size">36sp</dimen> <dimen name="card_margin">16dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8a3ba4b3048c..c117069f794b 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2905,15 +2905,15 @@ <!-- Status text on the Conversation widget for a birthday today [CHAR LIMIT=20] --> <string name="birthday_status">Birthday</string> <!-- Content description text on the Conversation widget for a birthday today [CHAR LIMIT=150] --> - <string name="birthday_status_content_description">It\’s <xliff:g id="name" example="Anna">%1$s</xliff:g>\’s birthday</string> + <string name="birthday_status_content_description">It\'s <xliff:g id="name" example="Anna">%1$s</xliff:g>\’s birthday</string> <!-- Status text on the Conversation widget for an upcoming birthday [CHAR LIMIT=20] --> <string name="upcoming_birthday_status">Birthday soon</string> <!-- Content description text on the Conversation widget for an upcoming birthday [CHAR LIMIT=150] --> - <string name="upcoming_birthday_status_content_description">It\’s <xliff:g id="name" example="Anna">%1$s</xliff:g>\’s birthday soon</string> + <string name="upcoming_birthday_status_content_description">It\'s <xliff:g id="name" example="Anna">%1$s</xliff:g>\’s birthday soon</string> <!-- Status text on the Conversation widget for an anniversary [CHAR LIMIT=20] --> <string name="anniversary_status">Anniversary</string> <!-- Content description text on the Conversation widget for an anniversary [CHAR LIMIT=150] --> - <string name="anniversary_status_content_description">It\’s <xliff:g id="name" example="Anna">%1$s</xliff:g>\’s anniversary</string> + <string name="anniversary_status_content_description">It\'s <xliff:g id="name" example="Anna">%1$s</xliff:g>\’s anniversary</string> <!-- Status text on the Conversation widget for sharing location [CHAR LIMIT=20] --> <string name="location_status">Sharing location</string> <!-- Content description text on the Conversation widget for sharing location [CHAR LIMIT=150] --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 885227b556ac..5da75a1f2a70 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -225,6 +225,11 @@ <item name="android:textColor">@color/dark_mode_qs_icon_color_single_tone</item> </style> + <style name="TextAppearance.QS.Build"> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:textSize">14sp</item> + </style> + <style name="TextAppearance.DeviceManagementDialog"> <item name="android:textColor">?android:attr/textColorPrimary</item> </style> diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml index 68e6ca8f3f21..cce27e71dbd2 100644 --- a/packages/SystemUI/res/xml/people_space_widget_info.xml +++ b/packages/SystemUI/res/xml/people_space_widget_info.xml @@ -16,7 +16,7 @@ <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="136dp" - android:minHeight="55dp" + android:targetCellHeight="1" android:minResizeWidth="60dp" android:minResizeHeight="50dp" android:maxResizeHeight="207dp" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java index 1a4e2d1665f6..2b35bcd9a3ea 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java @@ -385,10 +385,8 @@ public class PluginInstanceManager<T extends Plugin> { } Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData( Uri.parse("package://" + component.flattenToString())); - // TODO(b/174161910) Please replace FLAG_MUTABLE_UNAUDITED below - // with either FLAG_IMMUTABLE (recommended) or FLAG_MUTABLE. PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, - PendingIntent.FLAG_MUTABLE_UNAUDITED); + PendingIntent.FLAG_IMMUTABLE); nb.addAction(new Action.Builder(null, "Disable plugin", pi).build()); mContext.getSystemService(NotificationManager.class) .notify(SystemMessage.NOTE_PLUGIN, nb.build()); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index dde20c13ea4f..1cc488fc6df0 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -16,7 +16,6 @@ package com.android.systemui.shared.system; -import android.graphics.Rect; import android.os.RemoteException; import android.util.Log; import android.view.IRecentsAnimationController; @@ -72,17 +71,16 @@ public class RecentsAnimationControllerCompat { } /** - * Sets the final bounds on a Task. This is used by Launcher to notify the system that - * animating Activity to PiP has completed and the associated task surface should be updated - * accordingly. This should be called before `finish` + * Sets the final surface transaction on a Task. This is used by Launcher to notify the system + * that animating Activity to PiP has completed and the associated task surface should be + * updated accordingly. This should be called before `finish` * @param taskId Task id of the Activity in PiP mode. - * @param destinationBounds Bounds of the PiP window on home. * @param finishTransaction leash operations for the final transform. */ - public void setFinishTaskBounds(int taskId, Rect destinationBounds, + public void setFinishTaskTransaction(int taskId, PictureInPictureSurfaceTransaction finishTransaction) { try { - mAnimationController.setFinishTaskBounds(taskId, destinationBounds, finishTransaction); + mAnimationController.setFinishTaskTransaction(taskId, finishTransaction); } catch (RemoteException e) { Log.d(TAG, "Failed to set finish task bounds", e); } 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 4e0204b75d2c..22d934edf661 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 @@ -25,6 +25,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.TransitionOldType; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; +import android.annotation.SuppressLint; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -241,10 +242,16 @@ public class RemoteAnimationAdapterCompat { final Runnable animationFinishedCallback = new Runnable() { @Override + @SuppressLint("NewApi") public void run() { try { counterLauncher.cleanUp(info.getRootLeash()); counterWallpaper.cleanUp(info.getRootLeash()); + // Release surface references now. This is apparently to free GPU + // memory while doing quick operations (eg. during CTS). + for (int i = 0; i < info.getChanges().size(); ++i) { + info.getChanges().get(i).getLeash().release(); + } finishCallback.onTransitionFinished(null /* wct */); } catch (RemoteException e) { Log.e("ActivityOptionsCompat", "Failed to call app controlled animation" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index 88d9d68b4458..351dfd5b10c4 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.graphics.Rect; import android.os.IBinder; import android.os.Parcelable; @@ -127,7 +128,7 @@ public class RemoteTransitionCompat implements Parcelable { } t.apply(); final RecentsAnimationControllerCompat wrapControl = - new RecentsControllerWrap(controller, finishedCallback, pausingTask); + new RecentsControllerWrap(controller, info, finishedCallback, pausingTask); recents.onAnimationStart(wrapControl, apps, wallpapers, new Rect(0, 0, 0, 0), new Rect()); } @@ -161,10 +162,12 @@ public class RemoteTransitionCompat implements Parcelable { private final RecentsAnimationControllerCompat mWrapped; private final IRemoteTransitionFinishedCallback mFinishCB; private final WindowContainerToken mPausingTask; + private final TransitionInfo mInfo; - RecentsControllerWrap(RecentsAnimationControllerCompat wrapped, + RecentsControllerWrap(RecentsAnimationControllerCompat wrapped, TransitionInfo info, IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask) { mWrapped = wrapped; + mInfo = info; mFinishCB = finishCB; mPausingTask = pausingTask; } @@ -185,14 +188,16 @@ public class RemoteTransitionCompat implements Parcelable { mWrapped.hideCurrentInputMethod(); } - @Override public void setFinishTaskBounds(int taskId, Rect destinationBounds, + @Override public void setFinishTaskTransaction(int taskId, PictureInPictureSurfaceTransaction finishTransaction) { if (mWrapped != null) { - mWrapped.setFinishTaskBounds(taskId, destinationBounds, finishTransaction); + mWrapped.setFinishTaskTransaction(taskId, finishTransaction); } } - @Override public void finish(boolean toHome, boolean sendUserLeaveHint) { + @Override + @SuppressLint("NewApi") + public void finish(boolean toHome, boolean sendUserLeaveHint) { try { if (!toHome && mPausingTask != null) { // The gesture went back to opening the app rather than continuing with @@ -207,6 +212,11 @@ public class RemoteTransitionCompat implements Parcelable { Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e); } if (mWrapped != null) mWrapped.finish(toHome, sendUserLeaveHint); + // Release surface references now. This is apparently to free GPU + // memory while doing quick operations (eg. during CTS). + for (int i = 0; i < mInfo.getChanges().size(); ++i) { + mInfo.getChanges().get(i).getLeash().release(); + } } @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index f2bebcebb6d6..5559a1818b4b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -19,10 +19,12 @@ package com.android.keyguard; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import android.app.PendingIntent; import android.app.WallpaperManager; import android.app.smartspace.SmartspaceConfig; import android.app.smartspace.SmartspaceManager; import android.app.smartspace.SmartspaceSession; +import android.content.Intent; import android.content.res.Resources; import android.text.TextUtils; import android.text.format.DateFormat; @@ -39,8 +41,11 @@ import com.android.systemui.SystemUIFactory; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.BcSmartspaceDataPlugin; +import com.android.systemui.plugins.BcSmartspaceDataPlugin.IntentStarter; import com.android.systemui.plugins.ClockPlugin; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.notification.AnimatableProperty; @@ -87,6 +92,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private SmartspaceSession.OnTargetsAvailableListener mSmartspaceCallback; private int mWallpaperTextColor; private ConfigurationController mConfigurationController; + private ActivityStarter mActivityStarter; + private FalsingManager mFalsingManager; /** * Listener for changes to the color palette. @@ -138,7 +145,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS @Main Executor uiExecutor, BatteryController batteryController, ConfigurationController configurationController, - SystemUIFactory systemUIFactory) { + SystemUIFactory systemUIFactory, + ActivityStarter activityStarter, + FalsingManager falsingManager) { super(keyguardClockSwitch); mStatusBarStateController = statusBarStateController; mColorExtractor = colorExtractor; @@ -151,6 +160,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mBatteryController = batteryController; mConfigurationController = configurationController; mSystemUIFactory = systemUIFactory; + mActivityStarter = activityStarter; + mFalsingManager = falsingManager; } /** @@ -200,6 +211,16 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mSmartspaceView = smartspaceDataPlugin.getView(mView); mSmartspaceView.registerDataProvider(smartspaceDataPlugin); + mSmartspaceView.setIntentStarter(new IntentStarter() { + public void startIntent(View v, Intent i) { + mActivityStarter.startActivity(i, true /* dismissShade */); + } + + public void startPendingIntent(PendingIntent pi) { + mActivityStarter.startPendingIntentDismissingKeyguard(pi); + } + }); + mSmartspaceView.setFalsingManager(mFalsingManager); updateWallpaperColor(); View asView = (View) mSmartspaceView; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index fc80dbe021a7..588f4bb20340 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import android.graphics.Rect; import android.os.UserHandle; import android.util.Slog; @@ -48,6 +49,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV private final ConfigurationController mConfigurationController; private final DozeParameters mDozeParameters; private final KeyguardVisibilityHelper mKeyguardVisibilityHelper; + private final Rect mClipBounds = new Rect(); private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; @@ -299,4 +301,17 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV mView.updateLogoutView(shouldShowLogout()); } }; + + /** + * Rect that specifies how KSV should be clipped, on its parent's coordinates. + */ + public void setClipBounds(Rect clipBounds) { + if (clipBounds != null) { + mClipBounds.set(clipBounds.left, (int) (clipBounds.top - mView.getY()), + clipBounds.right, (int) (clipBounds.bottom - mView.getY())); + mView.setClipBounds(mClipBounds); + } else { + mView.setClipBounds(null); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS b/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS index 8765c9a64b77..947466f3baaf 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS +++ b/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS @@ -1,7 +1,3 @@ set noparent -kchyn@google.com -jaggies@google.com -curtislb@google.com -ilyamaty@google.com -joshmccloskey@google.com +include /services/core/java/com/android/server/biometrics/OWNERS diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index edb90c21361d..7ebfb7266c11 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -143,6 +143,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); private final VibrationEffect mEffectHeavy = VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); + private final VibrationEffect mDoubleClick = + VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); private final Runnable mAcquiredVibration = new Runnable() { @Override public void run() { @@ -750,6 +752,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { return mEffectTextureTick; case "tick": return mEffectTick; + case "double_tap": + return mDoubleClick; default: return defaultEffect; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java index 01d59597197c..9e621b30a042 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java @@ -79,6 +79,7 @@ public class BrightLineFalsingManager implements FalsingManager { private final Collection<FalsingClassifier> mClassifiers; private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>(); + private List<FalsingTapListener> mFalsingTapListeners = new ArrayList<>(); private final SessionListener mSessionListener = new SessionListener() { @Override @@ -277,6 +278,7 @@ public class BrightLineFalsingManager implements FalsingManager { FalsingClassifier.Result.falsed( 0, getClass().getSimpleName(), "bad history")); logDebug("False Single Tap: true (bad history)"); + mFalsingTapListeners.forEach(FalsingTapListener::onDoubleTapRequired); return true; } else { mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(0.1)); @@ -356,6 +358,16 @@ public class BrightLineFalsingManager implements FalsingManager { } @Override + public void addTapListener(FalsingTapListener listener) { + mFalsingTapListeners.add(listener); + } + + @Override + public void removeTapListener(FalsingTapListener listener) { + mFalsingTapListeners.remove(listener); + } + + @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.println("BRIGHTLINE FALSING MANAGER"); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java index 206af314d40e..94e5c8ad1536 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java @@ -257,7 +257,7 @@ class FalsingCollectorImpl implements FalsingCollector { @Override public void onTouchEvent(MotionEvent ev) { - if (!mKeyguardStateController.isShowing()) { + if (!mKeyguardStateController.isShowing() || mStatusBarStateController.isDozing()) { avoidGesture(); return; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java index e557773fe295..e8445d40836e 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -146,4 +146,14 @@ public class FalsingManagerFake implements FalsingManager { public void removeFalsingBeliefListener(FalsingBeliefListener listener) { mFalsingBeliefListeners.remove(listener); } + + @Override + public void addTapListener(FalsingTapListener falsingTapListener) { + + } + + @Override + public void removeTapListener(FalsingTapListener falsingTapListener) { + + } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 1723291e26f8..6b819fbbbcf1 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -176,6 +176,16 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { } @Override + public void addTapListener(FalsingTapListener listener) { + mInternalFalsingManager.addTapListener(listener); + } + + @Override + public void removeTapListener(FalsingTapListener listener) { + mInternalFalsingManager.removeTapListener(listener); + } + + @Override public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { mInternalFalsingManager.onProximityEvent(proximityEvent); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index d8ade2bdd21f..41964652ac49 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -376,8 +376,8 @@ public class DozeLog implements Dumpable { case REASON_SENSOR_DOUBLE_TAP: return "doubletap"; case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress"; case PULSE_REASON_DOCKING: return "docking"; - case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen"; - case REASON_SENSOR_WAKE_UP: return "wakeup"; + case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "reach-wakelockscreen"; + case REASON_SENSOR_WAKE_UP: return "presence-wakeup"; case REASON_SENSOR_TAP: return "tap"; case REASON_SENSOR_UDFPS_LONG_PRESS: return "udfps"; case REASON_SENSOR_QUICK_PICKUP: return "quickPickup"; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 5cea31b5a905..39adabb06d40 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -40,11 +40,9 @@ import android.view.Display; import androidx.annotation.VisibleForTesting; -import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; -import com.android.internal.logging.nano.MetricsProto; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.biometrics.AuthController; import com.android.systemui.plugins.SensorManagerPlugin; @@ -491,10 +489,6 @@ public class DozeSensors { mHandler.post(mWakeLock.wrap(() -> { if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event)); if (mSensor != null && mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) { - int subType = (int) event.values[0]; - MetricsLogger.action( - mContext, MetricsProto.MetricsEvent.ACTION_AMBIENT_GESTURE, - subType); UI_EVENT_LOGGER.log(DozeSensorsUiEvent.ACTION_AMBIENT_GESTURE_PICKUP); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index ee559659801b..c45eb3558948 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.hardware.display.AmbientDisplayConfiguration; -import android.metrics.LogMaker; import android.os.SystemClock; import android.os.UserHandle; import android.text.format.Formatter; @@ -33,12 +32,8 @@ import android.util.Log; import android.view.Display; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.UiEventLoggerImpl; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.Dependency; import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Main; @@ -70,8 +65,6 @@ public class DozeTriggers implements DozeMachine.Part { /** adb shell am broadcast -a com.android.systemui.doze.pulse com.android.systemui */ private static final String PULSE_ACTION = "com.android.systemui.doze.pulse"; - private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl(); - /** * Last value sent by the wake-display sensor. * Assuming that the screen should start on. @@ -99,12 +92,11 @@ public class DozeTriggers implements DozeMachine.Part { private final BroadcastDispatcher mBroadcastDispatcher; private final AuthController mAuthController; private final DelayableExecutor mMainExecutor; + private final UiEventLogger mUiEventLogger; private long mNotificationPulseTime; private boolean mPulsePending; - private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); - /** see {@link #onProximityFar} prox for callback */ private boolean mWantProxSensor; private boolean mWantTouchScreenSensors; @@ -143,7 +135,10 @@ public class DozeTriggers implements DozeMachine.Part { DOZING_UPDATE_AUTH_TRIGGERED(657), @UiEvent(doc = "Dozing updated because quick pickup sensor woke up.") - DOZING_UPDATE_QUICK_PICKUP(708); + DOZING_UPDATE_QUICK_PICKUP(708), + + @UiEvent(doc = "Dozing updated - sensor wakeup timed out (from quick pickup or presence)") + DOZING_UPDATE_WAKE_TIMEOUT(794); private final int mId; @@ -182,7 +177,8 @@ public class DozeTriggers implements DozeMachine.Part { ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck, DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher, SecureSettings secureSettings, AuthController authController, - @Main DelayableExecutor mainExecutor) { + @Main DelayableExecutor mainExecutor, + UiEventLogger uiEventLogger) { mContext = context; mDozeHost = dozeHost; mConfig = config; @@ -200,6 +196,7 @@ public class DozeTriggers implements DozeMachine.Part { mBroadcastDispatcher = broadcastDispatcher; mAuthController = authController; mMainExecutor = mainExecutor; + mUiEventLogger = uiEventLogger; } @Override @@ -328,11 +325,8 @@ public class DozeTriggers implements DozeMachine.Part { private void gentleWakeUp(int reason) { // Log screen wake up reason (lift/pickup, tap, double-tap) - mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING) - .setType(MetricsEvent.TYPE_UPDATE) - .setSubtype(reason)); Optional.ofNullable(DozingUpdateUiEvent.fromReason(reason)) - .ifPresent(UI_EVENT_LOGGER::log); + .ifPresent(mUiEventLogger::log); if (mDozeParameters.getDisplayNeedsBlanking()) { // Let's prepare the display to wake-up by drawing black. // This will cover the hardware wake-up sequence, where the display @@ -401,10 +395,9 @@ public class DozeTriggers implements DozeMachine.Part { } if (state == DozeMachine.State.DOZE) { mMachine.requestState(DozeMachine.State.DOZE_AOD); - // Logs AOD open due to sensor wake up. - mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING) - .setType(MetricsEvent.TYPE_OPEN) - .setSubtype(reason)); + // Log sensor triggered + Optional.ofNullable(DozingUpdateUiEvent.fromReason(reason)) + .ifPresent(mUiEventLogger::log); if (isQuickPickup) { // schedule runnable to go back to DOZE @@ -427,10 +420,8 @@ public class DozeTriggers implements DozeMachine.Part { return; } mMachine.requestState(DozeMachine.State.DOZE); - // Logs AOD close due to sensor wake up. - mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING) - .setType(MetricsEvent.TYPE_CLOSE) - .setSubtype(reason)); + // log wake timeout + mUiEventLogger.log(DozingUpdateUiEvent.DOZING_UPDATE_WAKE_TIMEOUT); } } } @@ -563,10 +554,8 @@ public class DozeTriggers implements DozeMachine.Part { }, !mDozeParameters.getProxCheckBeforePulse() || performedProxCheck, reason); // Logs request pulse reason on AOD screen. - mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING) - .setType(MetricsEvent.TYPE_UPDATE).setSubtype(reason)); Optional.ofNullable(DozingUpdateUiEvent.fromReason(reason)) - .ifPresent(UI_EVENT_LOGGER::log); + .ifPresent(mUiEventLogger::log); } private boolean canPulse() { diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index e44e305b6120..19edeb80c10c 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -98,7 +98,6 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.colorextraction.ColorExtractor.GradientColors; -import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; @@ -117,6 +116,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; +import com.android.systemui.scrim.ScrimDrawable; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 767d7ab5a490..411e0f07f2be 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -30,11 +30,11 @@ import android.widget.ProgressBar; import android.widget.TextView; import com.android.internal.R; -import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.plugins.GlobalActions; +import com.android.systemui.scrim.ScrimDrawable; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.ScrimController; diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 2ecd40578d68..c3c617c636da 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -319,7 +319,7 @@ class MediaCarouselController @Inject constructor( val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) newRecs.recommendationViewHolder?.recommendations?.setLayoutParams(lp) - newRecs.bindRecommendation(data, bgColor, { v -> removePlayer(key) }) + newRecs.bindRecommendation(data, bgColor) MediaPlayerData.addMediaPlayer(key, newRecs) updatePlayerToState(newRecs, noAnimation = true) reorderAllPlayers() @@ -348,11 +348,11 @@ class MediaCarouselController @Inject constructor( if (dismissMediaData) { // Inform the media manager of a potentially late dismissal - mediaManager.dismissMediaData(key, 0L) + mediaManager.dismissMediaData(key, 0L /* delaye */) } if (dismissRecommendation) { // Inform the media manager of a potentially late dismissal - mediaManager.dismissSmartspaceRecommendation() + mediaManager.dismissSmartspaceRecommendation(0L /* delay */) } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 495461e5b860..e6eb3178bfe4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -54,6 +54,7 @@ import com.android.systemui.animation.GhostedViewLaunchAnimatorController; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.util.animation.TransitionLayout; @@ -241,6 +242,21 @@ public class MediaControlPanel { TransitionLayout recommendations = vh.getRecommendations(); mMediaViewController.attach(recommendations, MediaViewController.TYPE.RECOMMENDATION); + + mRecommendationViewHolder.getRecommendations().setOnLongClickListener(v -> { + if (!mMediaViewController.isGutsVisible()) { + mMediaViewController.openGuts(); + return true; + } else { + return false; + } + }); + mRecommendationViewHolder.getCancel().setOnClickListener(v -> { + closeGuts(); + }); + mRecommendationViewHolder.getSettings().setOnClickListener(v -> { + mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */); + }); } /** Bind this player view based on the data given. */ @@ -460,10 +476,7 @@ public class MediaControlPanel { } /** Bind this recommendation view based on the data given. */ - public void bindRecommendation( - @NonNull SmartspaceTarget target, - @NonNull int backgroundColor, - @Nullable View.OnClickListener callback) { + public void bindRecommendation(@NonNull SmartspaceTarget target, @NonNull int backgroundColor) { if (mRecommendationViewHolder == null) { return; } @@ -519,7 +532,10 @@ public class MediaControlPanel { mediaCoverImageView.setImageIcon(recommendation.getIcon()); // Set up the click listener if applicable. - setSmartspaceOnClickListener(mediaCoverImageView, recommendation, callback); + setSmartspaceRecItemOnClickListener(mediaCoverImageView, recommendation, + view -> mMediaDataManagerLazy + .get() + .dismissSmartspaceRecommendation(0L /* delay */)); setVisibleAndAlpha(expandedSet, mediaCoverItemsResIds.get(i), true); setVisibleAndAlpha(expandedSet, mediaLogoItemsResIds.get(i), true); @@ -527,6 +543,16 @@ public class MediaControlPanel { setVisibleAndAlpha(collapsedSet, mediaLogoItemsResIds.get(i), true); } + // Set up long press to show guts setting panel. + mRecommendationViewHolder.getDismiss().setOnClickListener(v -> { + closeGuts(); + mKeyguardDismissUtil.executeWhenUnlocked(() -> { + mMediaDataManagerLazy.get().dismissSmartspaceRecommendation( + MediaViewController.GUTS_ANIMATION_DURATION + 100L); + return true; + }, true /* requiresShadeOpen */); + }); + mController = null; mMediaViewController.refreshState(); } @@ -611,7 +637,7 @@ public class MediaControlPanel { set.setAlpha(actionId, visible ? 1.0f : 0.0f); } - private void setSmartspaceOnClickListener( + private void setSmartspaceRecItemOnClickListener( @NonNull View view, @NonNull SmartspaceAction action, @Nullable View.OnClickListener callback) { @@ -630,4 +656,14 @@ public class MediaControlPanel { } }); } + + private int getSurfaceForSmartspaceLogging(int currentEndLocation) { + if (currentEndLocation == MediaHierarchyManager.LOCATION_QQS + || currentEndLocation == MediaHierarchyManager.LOCATION_QS) { + return SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE; + } else if (currentEndLocation == MediaHierarchyManager.LOCATION_LOCKSCREEN) { + return SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN; + } + return SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DEFAULT_SURFACE; + } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt index 0ed96eeac402..6ef29d694873 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt @@ -104,7 +104,12 @@ data class MediaData( /** * Set from the notification and used as fallback when PlaybackState cannot be determined */ - val isClearable: Boolean = true + val isClearable: Boolean = true, + + /** + * Timestamp when this player was last active. + */ + var lastActive: Long = 0L ) /** State of a media action. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt index a274eabed198..2bd7729f58d4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt @@ -17,6 +17,7 @@ package com.android.systemui.media import android.app.smartspace.SmartspaceTarget +import android.os.SystemProperties import android.util.Log import com.android.internal.annotations.VisibleForTesting import com.android.systemui.broadcast.BroadcastDispatcher @@ -24,14 +25,23 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.statusbar.NotificationLockscreenUserManager import java.util.concurrent.Executor +import java.util.concurrent.TimeUnit import javax.inject.Inject private const val TAG = "MediaDataFilter" private const val DEBUG = true /** + * Maximum age of a media control to re-activate on smartspace signal. If there is no media control + * available within this time window, smartspace recommendations will be shown instead. + */ +private val SMARTSPACE_MAX_AGE = SystemProperties + .getLong("debug.sysui.smartspace_max_age", TimeUnit.HOURS.toMillis(3)) + +/** * Filters data updates from [MediaDataCombineLatest] based on the current user ID, and handles user - * switches (removing entries for the previous user, adding back entries for the current user) + * switches (removing entries for the previous user, adding back entries for the current user). Also + * filters out smartspace updates in favor of local recent media, when avaialble. * * This is added at the end of the pipeline since we may still need to handle callbacks from * background users (e.g. timeouts). @@ -52,6 +62,7 @@ class MediaDataFilter @Inject constructor( // The filtered userEntries, which will be a subset of all userEntries in MediaDataManager private val userEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() private var hasSmartspace: Boolean = false + private var reactivatedKey: String? = null init { userTracker = object : CurrentUserTracker(broadcastDispatcher) { @@ -86,6 +97,30 @@ class MediaDataFilter @Inject constructor( override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) { hasSmartspace = true + + // Before forwarding the smartspace target, first check if we have recently inactive media + val now = System.currentTimeMillis() + val sorted = userEntries.toSortedMap(compareBy { + userEntries.get(it)?.lastActive ?: -1 + }) + if (sorted.size > 0) { + val lastActiveKey = sorted.lastKey() // most recently active + val timeSinceActive = sorted.get(lastActiveKey)?.let { + now - it.lastActive + } ?: Long.MAX_VALUE + if (timeSinceActive < SMARTSPACE_MAX_AGE) { + // Notify listeners to consider this media active + Log.d(TAG, "reactivating $lastActiveKey instead of smartspace") + reactivatedKey = lastActiveKey + val mediaData = sorted.get(lastActiveKey)!!.copy(active = true) + listeners.forEach { + it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData) + } + return + } + } + + // If no recent media, continue with smartspace update listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data) } } @@ -101,6 +136,21 @@ class MediaDataFilter @Inject constructor( override fun onSmartspaceMediaDataRemoved(key: String) { hasSmartspace = false + + // First check if we had reactivated media instead of forwarding smartspace + reactivatedKey?.let { + val lastActiveKey = it + reactivatedKey = null + Log.d(TAG, "expiring reactivated key $lastActiveKey") + // Notify listeners to update with actual active value + userEntries.get(lastActiveKey)?.let { mediaData -> + listeners.forEach { + it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData) + } + } + return + } + listeners.forEach { it.onSmartspaceMediaDataRemoved(key) } } @@ -137,10 +187,11 @@ class MediaDataFilter @Inject constructor( if (DEBUG) Log.d(TAG, "Media carousel swiped away") val mediaKeys = userEntries.keys.toSet() mediaKeys.forEach { - mediaDataManager.setTimedOut(it, timedOut = true) + // Force updates to listeners, needed for re-activated card + mediaDataManager.setTimedOut(it, timedOut = true, forceUpdate = true) } if (hasSmartspace) { - mediaDataManager.dismissSmartspaceRecommendation() + mediaDataManager.dismissSmartspaceRecommendation(0L /* delay */) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 5ba04a03a8d5..a0708617ef66 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -394,9 +394,9 @@ class MediaDataManager( * This will make the player not active anymore, hiding it from QQS and Keyguard. * @see MediaData.active */ - internal fun setTimedOut(token: String, timedOut: Boolean) { + internal fun setTimedOut(token: String, timedOut: Boolean, forceUpdate: Boolean = false) { mediaEntries[token]?.let { - if (it.active == !timedOut) { + if (it.active == !timedOut && !forceUpdate) { return } it.active = !timedOut @@ -429,12 +429,13 @@ class MediaDataManager( * This will make the recommendation view to not be shown anymore during this headphone * connection session. */ - fun dismissSmartspaceRecommendation() { + fun dismissSmartspaceRecommendation(delay: Long) { Log.d(TAG, "Dismissing Smartspace media target") // Do not set smartspaceMediaTarget to null. So the instance is preserved during the entire // headphone connection, and will ONLY be set to null when headphones are disconnected. smartspaceMediaTarget?.let { - notifySmartspaceMediaDataRemoved(it.smartspaceTargetId) + foregroundExecutor.executeDelayed( + { notifySmartspaceMediaDataRemoved(it.smartspaceTargetId) }, delay) } } @@ -470,12 +471,13 @@ class MediaDataManager( } val mediaAction = getResumeMediaAction(resumeAction) + val lastActive = System.currentTimeMillis() foregroundExecutor.execute { onMediaDataLoaded(packageName, null, MediaData(userId, true, bgColor, appName, null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0), packageName, token, appIntent, device = null, active = false, resumeAction = resumeAction, resumption = true, notificationKey = packageName, - hasCheckedForResume = true)) + hasCheckedForResume = true, lastActive = lastActive)) } } @@ -586,9 +588,9 @@ class MediaDataManager( } val isLocalSession = mediaController.playbackInfo?.playbackType == - MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL ?: true + MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null - + val lastActive = System.currentTimeMillis() foregroundExecutor.execute { val resumeAction: Runnable? = mediaEntries[key]?.resumeAction val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true @@ -598,7 +600,8 @@ class MediaDataManager( actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null, active, resumeAction = resumeAction, isLocalSession = isLocalSession, notificationKey = key, hasCheckedForResume = hasCheckedForResume, - isPlaying = isPlaying, isClearable = sbn.isClearable())) + isPlaying = isPlaying, isClearable = sbn.isClearable(), + lastActive = lastActive)) } } @@ -724,7 +727,7 @@ class MediaDataManager( Assert.isMainThread() val removed = mediaEntries.remove(key) if (useMediaResumption && removed?.resumeAction != null && - !isBlockedFromResume(removed.packageName)) { + !isBlockedFromResume(removed.packageName) && removed?.isLocalSession == true) { Log.d(TAG, "Not removing $key because resumable") // Move to resume key (aka package name) if that key doesn't already exist. val resumeAction = getResumeMediaAction(removed.resumeAction!!) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt index 80d13715ca07..b0be57668d08 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt @@ -173,7 +173,7 @@ class MediaResumeListener @Inject constructor( mediaBrowser?.disconnect() // If we don't have a resume action, check if we haven't already if (data.resumeAction == null && !data.hasCheckedForResume && - !blockedApps.contains(data.packageName)) { + !blockedApps.contains(data.packageName) && data.isLocalSession) { // TODO also check for a media button receiver intended for restarting (b/154127084) Log.d(TAG, "Checking for service component for " + data.packageName) val pm = context.packageManager diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt index ce72991d01c0..8bfe94bbd0de 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt @@ -51,36 +51,53 @@ class MediaTimeoutListener @Inject constructor( lateinit var timeoutCallback: (String, Boolean) -> Unit override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) { - if (mediaListeners.containsKey(key)) { - return + var reusedListener: PlaybackStateListener? = null + + // First check if we already have a listener + mediaListeners.get(key)?.let { + if (!it.destroyed) { + return + } + + // If listener was destroyed previously, we'll need to re-register it + if (DEBUG) { + Log.d(TAG, "Reusing destroyed listener $key") + } + reusedListener = it } + // Having an old key means that we're migrating from/to resumption. We should update // the old listener to make sure that events will be dispatched to the new location. val migrating = oldKey != null && key != oldKey if (migrating) { - val reusedListener = mediaListeners.remove(oldKey) + reusedListener = mediaListeners.remove(oldKey) if (reusedListener != null) { - val wasPlaying = reusedListener.playing ?: false if (DEBUG) Log.d(TAG, "migrating key $oldKey to $key, for resumption") - reusedListener.mediaData = data - reusedListener.key = key - mediaListeners[key] = reusedListener - if (wasPlaying != reusedListener.playing) { - // If a player becomes active because of a migration, we'll need to broadcast - // its state. Doing it now would lead to reentrant callbacks, so let's wait - // until we're done. - mainExecutor.execute { - if (mediaListeners[key]?.playing == true) { - if (DEBUG) Log.d(TAG, "deliver delayed playback state for $key") - timeoutCallback.invoke(key, false /* timedOut */) - } - } - } - return } else { Log.w(TAG, "Old key $oldKey for player $key doesn't exist. Continuing...") } } + + reusedListener?.let { + val wasPlaying = it.playing ?: false + if (DEBUG) Log.d(TAG, "updating listener for $key, was playing? $wasPlaying") + it.mediaData = data + it.key = key + mediaListeners[key] = it + if (wasPlaying != it.playing) { + // If a player becomes active because of a migration, we'll need to broadcast + // its state. Doing it now would lead to reentrant callbacks, so let's wait + // until we're done. + mainExecutor.execute { + if (mediaListeners[key]?.playing == true) { + if (DEBUG) Log.d(TAG, "deliver delayed playback state for $key") + timeoutCallback.invoke(key, false /* timedOut */) + } + } + } + return + } + mediaListeners[key] = PlaybackStateListener(key, data) } @@ -99,9 +116,11 @@ class MediaTimeoutListener @Inject constructor( var timedOut = false var playing: Boolean? = null + var destroyed = false var mediaData: MediaData = data set(value) { + destroyed = false mediaController?.unregisterCallback(this) field = value mediaController = if (field.token != null) { @@ -126,15 +145,25 @@ class MediaTimeoutListener @Inject constructor( fun destroy() { mediaController?.unregisterCallback(this) cancellation?.run() + destroyed = true } override fun onPlaybackStateChanged(state: PlaybackState?) { processState(state, dispatchEvents = true) } + override fun onSessionDestroyed() { + // If the session is destroyed, the controller is no longer valid, and we will need to + // recreate it if this key is updated later + if (DEBUG) { + Log.d(TAG, "Session destroyed for $key") + } + destroy() + } + private fun processState(state: PlaybackState?, dispatchEvents: Boolean) { if (DEBUG) { - Log.v(TAG, "processState: $state") + Log.v(TAG, "processState $key: $state") } val isPlaying = state != null && isPlayingState(state.state) @@ -173,8 +202,7 @@ class MediaTimeoutListener @Inject constructor( private fun expireMediaTimeout(mediaKey: String, reason: String) { cancellation?.apply { if (DEBUG) { - Log.v(TAG, - "media timeout cancelled for $mediaKey, reason: $reason") + Log.v(TAG, "media timeout cancelled for $mediaKey, reason: $reason") } run() } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt index 7cfe4c4073d2..f78556f18492 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt @@ -70,11 +70,10 @@ class MediaViewController @Inject constructor( * finished */ @MediaLocation - private var currentEndLocation: Int = -1 + var currentEndLocation: Int = -1 /** - * The ending location of the view where it ends when all animations and transitions have - * finished + * The starting location of the view where it starts for all animations and transitions */ @MediaLocation private var currentStartLocation: Int = -1 @@ -253,16 +252,30 @@ class MediaViewController @Inject constructor( * [TransitionViewState]. */ private fun setGutsViewState(viewState: TransitionViewState) { - PlayerViewHolder.controlsIds.forEach { id -> - viewState.widgetStates.get(id)?.let { state -> - // Make sure to use the unmodified state if guts are not visible - state.alpha = if (isGutsVisible) 0f else state.alpha - state.gone = if (isGutsVisible) true else state.gone + if (type == TYPE.PLAYER) { + PlayerViewHolder.controlsIds.forEach { id -> + viewState.widgetStates.get(id)?.let { state -> + // Make sure to use the unmodified state if guts are not visible. + state.alpha = if (isGutsVisible) 0f else state.alpha + state.gone = if (isGutsVisible) true else state.gone + } + } + PlayerViewHolder.gutsIds.forEach { id -> + viewState.widgetStates.get(id)?.alpha = if (isGutsVisible) 1f else 0f + viewState.widgetStates.get(id)?.gone = !isGutsVisible + } + } else { + RecommendationViewHolder.controlsIds.forEach { id -> + viewState.widgetStates.get(id)?.let { state -> + // Make sure to use the unmodified state if guts are not visible. + state.alpha = if (isGutsVisible) 0f else state.alpha + state.gone = if (isGutsVisible) true else state.gone + } + } + RecommendationViewHolder.gutsIds.forEach { id -> + viewState.widgetStates.get(id)?.alpha = if (isGutsVisible) 1f else 0f + viewState.widgetStates.get(id)?.gone = !isGutsVisible } - } - PlayerViewHolder.gutsIds.forEach { id -> - viewState.widgetStates.get(id)?.alpha = if (isGutsVisible) 1f else 0f - viewState.widgetStates.get(id)?.gone = !isGutsVisible } } diff --git a/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt index ac201a804e38..19c83bc83ac3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt @@ -20,6 +20,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView +import android.widget.TextView import androidx.annotation.IntegerRes import com.android.systemui.R import com.android.systemui.util.animation.TransitionLayout @@ -28,6 +29,8 @@ import com.android.systemui.util.animation.TransitionLayout class RecommendationViewHolder private constructor(itemView: View) { val recommendations = itemView as TransitionLayout + + // Recommendation screen val mediaCoverItems = listOf<ImageView>( itemView.requireViewById(R.id.media_cover1), itemView.requireViewById(R.id.media_cover2), @@ -49,16 +52,27 @@ class RecommendationViewHolder private constructor(itemView: View) { R.id.media_logo3, R.id.media_logo4) + // Settings/Guts screen + val cancel = itemView.requireViewById<View>(R.id.cancel) + val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss) + val dismissLabel = dismiss.getChildAt(0) + val recommendationText = itemView.requireViewById<TextView>(R.id.recommendation_text) + val settings = itemView.requireViewById<View>(R.id.settings) + init { (recommendations.background as IlluminationDrawable).let { background -> mediaCoverItems.forEach { background.registerLightSource(it) } mediaLogoItems.forEach { background.registerLightSource(it) } + background.registerLightSource(cancel) + background.registerLightSource(dismiss) + background.registerLightSource(dismissLabel) + background.registerLightSource(settings) } } companion object { /** - * Creates a PlayerViewHolder. + * Creates a RecommendationViewHolder. * * @param inflater LayoutInflater to use to inflate the layout. * @param parent Parent of inflated view. @@ -76,5 +90,26 @@ class RecommendationViewHolder private constructor(itemView: View) { itemView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE return RecommendationViewHolder(itemView) } + + // Res Ids for the control components on the recommendation view. + val controlsIds = setOf( + R.id.media_cover1, + R.id.media_cover2, + R.id.media_cover3, + R.id.media_cover4, + R.id.media_logo1, + R.id.media_logo2, + R.id.media_logo3, + R.id.media_logo4 + ) + + // Res Ids for the components on the guts panel. + val gutsIds = setOf( + R.id.recommendation_text, + R.id.remove_text, + R.id.cancel, + R.id.dismiss, + R.id.settings + ) } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java b/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java index 68a829ca2e38..0efef02cd191 100644 --- a/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java @@ -42,7 +42,7 @@ import java.util.Set; /** Helper functions to handle notifications in People Tiles. */ public class NotificationHelper { private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; - private static final String TAG = "PeopleNotificationHelper"; + private static final String TAG = "PeopleNotifHelper"; /** Returns the notification with highest priority to be shown in People Tiles. */ public static NotificationEntry getHighestPriorityNotification( @@ -209,5 +209,30 @@ public class NotificationHelper { } return null; } + + /** Returns whether {@code notification} is a group conversation. */ + private static boolean isGroupConversation(Notification notification) { + return notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION, false); + } + + /** + * Returns {@code message}'s sender's name if {@code notification} is from a group conversation. + */ + public static CharSequence getSenderIfGroupConversation(Notification notification, + Notification.MessagingStyle.Message message) { + if (!isGroupConversation(notification)) { + if (DEBUG) { + Log.d(TAG, "Notification is not from a group conversation, not checking sender."); + } + return null; + } + Person person = message.getSenderPerson(); + if (person == null) { + if (DEBUG) Log.d(TAG, "Notification from group conversation doesn't include sender."); + return null; + } + if (DEBUG) Log.d(TAG, "Returning sender from group conversation notification."); + return person.getName(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index ff14abe118ab..98d886661cea 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -132,7 +132,9 @@ public class PeopleSpaceActivity extends Activity { /** Sets {@code tileView} with the data in {@code conversation}. */ private void setTileView(PeopleSpaceTileView tileView, PeopleSpaceTile tile) { try { - tileView.setName(tile.getUserName().toString()); + if (tile.getUserName() != null) { + tileView.setName(tile.getUserName().toString()); + } tileView.setPersonIcon(getPersonIconBitmap(mContext, tile, getSizeInDp(mContext, R.dimen.avatar_size_for_medium, mContext.getResources().getDisplayMetrics().density))); diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index eefe5ca93793..99a17ffbe77a 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 com.android.systemui.people.NotificationHelper.getContactUri; import static com.android.systemui.people.NotificationHelper.getMessagingStyleMessages; +import static com.android.systemui.people.NotificationHelper.getSenderIfGroupConversation; import static com.android.systemui.people.NotificationHelper.hasReadContactsPermission; import static com.android.systemui.people.NotificationHelper.isMissedCall; import static com.android.systemui.people.NotificationHelper.shouldMatchNotificationByUri; @@ -233,6 +234,7 @@ public class PeopleSpaceUtils { // Reset notification content. .setNotificationKey(null) .setNotificationContent(null) + .setNotificationSender(null) .setNotificationDataUri(null) .setMessagesCount(0) // Reset missed calls category. @@ -245,9 +247,9 @@ public class PeopleSpaceUtils { * {@code messagesCount}. */ public static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile, - NotificationEntry notificationEntry, int messagesCount) { + PeopleTileKey key, NotificationEntry notificationEntry, int messagesCount) { if (notificationEntry == null || notificationEntry.getSbn().getNotification() == null) { - if (DEBUG) Log.d(TAG, "Notification is null"); + if (DEBUG) Log.d(TAG, "Tile key: " + key.toString() + ". Notification is null"); return removeNotificationFields(tile); } Notification notification = notificationEntry.getSbn().getNotification(); @@ -256,7 +258,7 @@ public class PeopleSpaceUtils { getMessagingStyleMessages(notification); if (!isMissedCall && ArrayUtils.isEmpty(messages)) { - if (DEBUG) Log.d(TAG, "Notification has no content"); + if (DEBUG) Log.d(TAG, "Tile key: " + key.toString() + ". Notification has no content"); return removeNotificationFields(tile); } @@ -268,13 +270,18 @@ public class PeopleSpaceUtils { CharSequence content = (isMissedCall && !hasMessageText) ? context.getString(R.string.missed_call) : message.getText(); Uri dataUri = message != null ? message.getDataUri() : null; - if (DEBUG) Log.d(TAG, "Notification message has text: " + hasMessageText); + if (DEBUG) { + Log.d(TAG, "Tile key: " + key.toString() + ". Notification message has text: " + + hasMessageText); + } + CharSequence sender = getSenderIfGroupConversation(notification, message); return tile .toBuilder() .setNotificationKey(notificationEntry.getSbn().getKey()) .setNotificationCategory(notification.category) .setNotificationContent(content) + .setNotificationSender(sender) .setNotificationDataUri(dataUri) .setMessagesCount(messagesCount) .build(); @@ -459,7 +466,7 @@ public class PeopleSpaceUtils { } if (DEBUG) { Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName() + ", " - + tile.getPackageName()); + + tile.getPackageName() + ". Updating app widget view."); } RemoteViews views = new PeopleTileViewHelper(context, tile, appWidgetId, options).getViews(); @@ -472,7 +479,9 @@ public class PeopleSpaceUtils { public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager, Context context, int appWidgetId, PeopleSpaceTile tile) { if (tile == null) { - Log.d(TAG, "Tile is null, skipping storage and update."); + if (DEBUG) { + Log.w(TAG, "Widget: " + appWidgetId + "Tile is null, skipping storage and update."); + } return; } Bundle options = AppWidgetOptionsHelper.setPeopleTile(appWidgetManager, appWidgetId, tile); @@ -483,7 +492,10 @@ public class PeopleSpaceUtils { public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager, Context context, int appWidgetId, Optional<PeopleSpaceTile> optionalTile) { if (!optionalTile.isPresent()) { - Log.d(TAG, "Tile is null, skipping storage and update."); + if (DEBUG) { + Log.w(TAG, "Widget: " + appWidgetId + + "Optional tile is not present, skipping storage and update."); + } return; } updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, optionalTile.get()); diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java index 145fee5e762a..81df107836b9 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java @@ -23,16 +23,17 @@ import android.content.pm.PackageManager; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; +import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.IconDrawableFactory; import android.util.Log; import android.util.TypedValue; -import com.android.launcher3.icons.BaseIconFactory; +import com.android.settingslib.Utils; import com.android.systemui.R; -class PeopleStoryIconFactory extends BaseIconFactory { +class PeopleStoryIconFactory implements AutoCloseable { private static final int PADDING = 2; private static final int RING_WIDTH = 2; @@ -44,11 +45,13 @@ class PeopleStoryIconFactory extends BaseIconFactory { private int mAccentColor; private float mDensity; private float mIconSize; + private Context mContext; + + private final int mIconBitmapSize; PeopleStoryIconFactory(Context context, PackageManager pm, IconDrawableFactory iconDrawableFactory, int iconSizeDp) { - super(context, context.getResources().getConfiguration().densityDpi, - (int) (iconSizeDp * context.getResources().getDisplayMetrics().density)); + mIconBitmapSize = (int) (iconSizeDp * context.getResources().getDisplayMetrics().density); mDensity = context.getResources().getDisplayMetrics().density; mIconSize = mDensity * iconSizeDp; mPackageManager = pm; @@ -57,6 +60,7 @@ class PeopleStoryIconFactory extends BaseIconFactory { TypedValue typedValue = new TypedValue(); context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true); mAccentColor = context.getColor(typedValue.resourceId); + mContext = context; } @@ -69,7 +73,7 @@ class PeopleStoryIconFactory extends BaseIconFactory { try { final ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser( packageName, PackageManager.GET_META_DATA, userId); - badge = mIconDrawableFactory.getBadgedIcon(appInfo, userId); + badge = Utils.getBadgedIcon(mContext, appInfo); } catch (PackageManager.NameNotFoundException e) { badge = mPackageManager.getDefaultActivityIcon(); } @@ -209,7 +213,11 @@ class PeopleStoryIconFactory extends BaseIconFactory { @Override public int getOpacity() { - return 0; + return PixelFormat.TRANSLUCENT; } } -} + + @Override + public void close() { + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index 3883c00f40b3..d4ddc6546a19 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -59,6 +59,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.people.widget.LaunchConversationActivity; import com.android.systemui.people.widget.PeopleSpaceWidgetProvider; +import com.android.systemui.people.widget.PeopleTileKey; import java.text.NumberFormat; import java.time.Duration; @@ -148,6 +149,8 @@ public class PeopleTileViewHelper { * content, then birthdays, then the most recent status, and finally last interaction. */ private RemoteViews getViewForTile() { + PeopleTileKey key = new PeopleTileKey(mTile); + if (DEBUG) Log.d(TAG, "Creating view for tile key: " + key.toString()); if (Objects.equals(mTile.getNotificationCategory(), CATEGORY_MISSED_CALL)) { if (DEBUG) Log.d(TAG, "Create missed call view"); return createMissedCallRemoteViews(); @@ -179,7 +182,7 @@ public class PeopleTileViewHelper { return createLastInteractionRemoteViews(); } - private void setMaxLines(RemoteViews views) { + private void setMaxLines(RemoteViews views, boolean showSender) { int textSize = mLayoutSize == LAYOUT_LARGE ? getSizeInDp( R.dimen.content_text_size_for_medium) : getSizeInDp(R.dimen.content_text_size_for_medium); @@ -187,6 +190,9 @@ public class PeopleTileViewHelper { int notificationContentHeight = getContentHeightForLayout(lineHeight); int maxAdaptiveLines = Math.floorDiv(notificationContentHeight, lineHeight); int maxLines = Math.max(MIN_CONTENT_MAX_LINES, maxAdaptiveLines); + + // Save a line for sender's name, if present. + if (showSender) maxLines--; views.setInt(R.id.text_content, "setMaxLines", maxLines); } @@ -305,7 +311,9 @@ public class PeopleTileViewHelper { views.setViewVisibility(R.id.availability, View.GONE); } - views.setTextViewText(R.id.name, mTile.getUserName().toString()); + if (mTile.getUserName() != null) { + views.setTextViewText(R.id.name, mTile.getUserName().toString()); + } views.setBoolean(R.id.image, "setClipToOutline", true); views.setImageViewBitmap(R.id.person_icon, getPersonIconBitmap(mContext, mTile, maxAvatarSize)); @@ -348,7 +356,7 @@ public class PeopleTileViewHelper { RemoteViews views = getViewForContentLayout(); views.setViewVisibility(R.id.predefined_icon, View.VISIBLE); views.setViewVisibility(R.id.messages_count, View.GONE); - setMaxLines(views); + setMaxLines(views, false); views.setTextViewText(R.id.text_content, mTile.getNotificationContent()); views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_phone_missed); return views; @@ -356,6 +364,7 @@ public class PeopleTileViewHelper { private RemoteViews createNotificationRemoteViews() { RemoteViews views = getViewForContentLayout(); + CharSequence sender = mTile.getNotificationSender(); Uri image = mTile.getNotificationDataUri(); if (image != null) { // TODO: Use NotificationInlineImageCache @@ -364,7 +373,7 @@ public class PeopleTileViewHelper { views.setViewVisibility(R.id.text_content, View.GONE); views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_photo_camera); } else { - setMaxLines(views); + setMaxLines(views, !TextUtils.isEmpty(sender)); CharSequence content = mTile.getNotificationContent(); views = setPunctuationRemoteViewsFields(views, content); views.setColorAttr(R.id.text_content, "setTextColor", android.R.attr.textColorPrimary); @@ -380,9 +389,12 @@ public class PeopleTileViewHelper { views.setViewVisibility(R.id.predefined_icon, View.GONE); } } - // TODO: Set subtext as Group Sender name once storing the name in PeopleSpaceTile and - // subtract 1 from maxLines when present. - views.setViewVisibility(R.id.subtext, View.GONE); + if (!TextUtils.isEmpty(sender)) { + views.setViewVisibility(R.id.subtext, View.VISIBLE); + views.setTextViewText(R.id.subtext, sender); + } else { + views.setViewVisibility(R.id.subtext, View.GONE); + } return views; } @@ -412,7 +424,7 @@ public class PeopleTileViewHelper { } views.setViewVisibility(R.id.predefined_icon, View.VISIBLE); views.setViewVisibility(R.id.messages_count, View.GONE); - setMaxLines(views); + setMaxLines(views, false); // Secondary text color for statuses. views.setColorAttr(R.id.text_content, "setTextColor", android.R.attr.textColorSecondary); views.setTextViewText(R.id.text_content, statusText); diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java b/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java index 7254eec71d07..73c43ebfdaf0 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/AppWidgetOptionsHelper.java @@ -41,7 +41,7 @@ public class AppWidgetOptionsHelper { PeopleSpaceTile tile) { Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); if (tile == null) { - if (DEBUG) Log.d(TAG, "Requested to store null tile"); + if (DEBUG) Log.w(TAG, "Requested to store null tile"); return options; } options.putParcelable(OPTIONS_PEOPLE_TILE, tile); 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 f11c1bdc6bfb..64a6509ea0aa 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -236,7 +236,7 @@ public class PeopleSpaceWidgetManager { @Nullable public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key) { if (!key.isValid()) { - Log.e(TAG, "PeopleTileKey invalid: " + key); + Log.e(TAG, "PeopleTileKey invalid: " + key.toString()); return null; } @@ -267,7 +267,14 @@ public class PeopleSpaceWidgetManager { */ public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction) { - if (DEBUG) Log.d(TAG, "updateWidgetsWithNotificationChanged called"); + if (DEBUG) { + Log.d(TAG, "updateWidgetsWithNotificationChanged called"); + if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { + Log.d(TAG, "Notification posted, key: " + sbn.getKey()); + } else { + Log.d(TAG, "Notification removed, key: " + sbn.getKey()); + } + } if (mIsForTesting) { updateWidgetsWithNotificationChangedInBackground(sbn, notificationAction); return; @@ -282,7 +289,7 @@ public class PeopleSpaceWidgetManager { PeopleTileKey key = new PeopleTileKey( sbn.getShortcutId(), sbn.getUser().getIdentifier(), sbn.getPackageName()); if (!key.isValid()) { - Log.d(TAG, "Invalid key from sbn"); + Log.d(TAG, "Sbn doesn't contain valid PeopleTileKey: " + key.toString()); return; } int[] widgetIds = mAppWidgetManager.getAppWidgetIds( @@ -309,8 +316,12 @@ public class PeopleSpaceWidgetManager { /** Updates {@code widgetIdsToUpdate} with {@code action}. */ private void updateWidgetIdsBasedOnNotifications(Set<String> widgetIdsToUpdate) { - Log.d(TAG, "Fetching grouped notifications"); + if (widgetIdsToUpdate.isEmpty()) { + if (DEBUG) Log.d(TAG, "No widgets to update, returning."); + return; + } try { + if (DEBUG) Log.d(TAG, "Fetching grouped notifications"); Map<PeopleTileKey, Set<NotificationEntry>> groupedNotifications = getGroupedConversationNotifications(); @@ -331,14 +342,15 @@ public class PeopleSpaceWidgetManager { * Augments {@code tile} based on notifications returned from {@code notificationEntryManager}. */ public PeopleSpaceTile augmentTileFromNotificationEntryManager(PeopleSpaceTile tile) { - Log.d(TAG, "Augmenting tile from NotificationEntryManager widget: " + tile.getId()); + PeopleTileKey key = new PeopleTileKey(tile); + Log.d(TAG, "Augmenting tile from NotificationEntryManager widget: " + key.toString()); Map<PeopleTileKey, Set<NotificationEntry>> notifications = getGroupedConversationNotifications(); String contactUri = null; if (tile.getContactUri() != null) { contactUri = tile.getContactUri().toString(); } - return augmentTileFromNotifications(tile, contactUri, notifications); + return augmentTileFromNotifications(tile, key, contactUri, notifications); } /** Returns active and pending notifications grouped by {@link PeopleTileKey}. */ @@ -367,9 +379,9 @@ public class PeopleSpaceWidgetManager { } /** Augments {@code tile} based on {@code notifications}, matching {@code contactUri}. */ - public PeopleSpaceTile augmentTileFromNotifications(PeopleSpaceTile tile, String contactUri, - Map<PeopleTileKey, Set<NotificationEntry>> notifications) { - if (DEBUG) Log.d(TAG, "Augmenting tile from notifications. Tile id: " + tile.getId()); + public PeopleSpaceTile augmentTileFromNotifications(PeopleSpaceTile tile, PeopleTileKey key, + String contactUri, Map<PeopleTileKey, Set<NotificationEntry>> notifications) { + if (DEBUG) Log.d(TAG, "Augmenting tile from notifications. Tile key: " + key.toString()); boolean hasReadContactsPermission = mPackageManager.checkPermission(READ_CONTACTS, tile.getPackageName()) == PackageManager.PERMISSION_GRANTED; @@ -384,13 +396,12 @@ public class PeopleSpaceWidgetManager { } } - PeopleTileKey key = new PeopleTileKey(tile); Set<NotificationEntry> allNotifications = notifications.get(key); if (allNotifications == null) { allNotifications = new HashSet<>(); } if (allNotifications.isEmpty() && notificationsByUri.isEmpty()) { - if (DEBUG) Log.d(TAG, "No existing notifications for tile: " + key); + if (DEBUG) Log.d(TAG, "No existing notifications for tile: " + key.toString()); return removeNotificationFields(tile); } @@ -402,22 +413,28 @@ public class PeopleSpaceWidgetManager { NotificationEntry highestPriority = getHighestPriorityNotification(allNotifications); if (DEBUG) Log.d(TAG, "Augmenting tile from notification, key: " + key.toString()); - return augmentTileFromNotification(mContext, tile, highestPriority, messagesCount); + return augmentTileFromNotification(mContext, tile, key, highestPriority, messagesCount); } /** Returns an augmented tile for an existing widget. */ @Nullable public Optional<PeopleSpaceTile> getAugmentedTileForExistingWidget(int widgetId, Map<PeopleTileKey, Set<NotificationEntry>> notifications) { - Log.d(TAG, "Augmenting tile for widget: " + widgetId); + Log.d(TAG, "Augmenting tile for existing widget: " + widgetId); PeopleSpaceTile tile = getTileForExistingWidget(widgetId); if (tile == null) { + if (DEBUG) { + Log.w(TAG, "Widget: " + widgetId + + ". Null tile for existing widget, skipping update."); + } return Optional.empty(); } String contactUriString = mSharedPrefs.getString(String.valueOf(widgetId), null); // Should never be null, but using ofNullable for extra safety. + PeopleTileKey key = new PeopleTileKey(tile); + if (DEBUG) Log.d(TAG, "Existing widget: " + widgetId + ". Tile key: " + key.toString()); return Optional.ofNullable( - augmentTileFromNotifications(tile, contactUriString, notifications)); + augmentTileFromNotifications(tile, key, contactUriString, notifications)); } /** Returns stored widgets for the conversation specified. */ @@ -644,12 +661,12 @@ public class PeopleSpaceWidgetManager { } synchronized (mLock) { - if (DEBUG) Log.d(TAG, "Add storage for : " + tile.getId()); + if (DEBUG) Log.d(TAG, "Add storage for : " + key.toString()); PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId, tile.getContactUri()); } try { - if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId()); + if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + key.toString()); mLauncherApps.cacheShortcuts(tile.getPackageName(), Collections.singletonList(tile.getId()), tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); @@ -679,7 +696,7 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "Already registered listener"); return; } - if (DEBUG) Log.d(TAG, "Register listener for " + widgetId + " with " + key); + if (DEBUG) Log.d(TAG, "Register listener for " + widgetId + " with " + key.toString()); mListeners.put(key, newListener); } mPeopleManager.registerConversationListener(key.getPackageName(), @@ -750,7 +767,9 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "Cannot find listener to unregister"); return; } - if (DEBUG) Log.d(TAG, "Unregister listener for " + appWidgetId + " with " + key); + if (DEBUG) { + Log.d(TAG, "Unregister listener for " + appWidgetId + " with " + key.toString()); + } mListeners.remove(key); } mPeopleManager.unregisterConversationListener(registeredListener); diff --git a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java index 309b32fc85d2..cd3609108c28 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java @@ -29,6 +29,7 @@ public class NonInterceptingScrollView extends ScrollView { private final int mTouchSlop; private float mDownY; + private boolean mScrollEnabled = true; public NonInterceptingScrollView(Context context, AttributeSet attrs) { super(context, attrs); @@ -85,6 +86,16 @@ public class NonInterceptingScrollView extends ScrollView { return super.onInterceptTouchEvent(ev); } + @Override + public boolean canScrollVertically(int direction) { + return mScrollEnabled && super.canScrollVertically(direction); + } + + @Override + public boolean canScrollHorizontally(int direction) { + return mScrollEnabled && super.canScrollHorizontally(direction); + } + public int getScrollRange() { int scrollRange = 0; if (getChildCount() > 0) { @@ -94,4 +105,12 @@ public class NonInterceptingScrollView extends ScrollView { } return scrollRange; } + + /** + * Enable scrolling for this view. Needed because the view might be clipped but still intercepts + * touches on the lockscreen. + */ + public void setScrollingEnabled(boolean enabled) { + mScrollEnabled = enabled; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index cefcd4a5194c..294d76590fed 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -54,6 +54,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows"; public static final float EXPANDED_TILE_DELAY = .86f; + public static final float SHORT_PARALLAX_AMOUNT = 0.1f; private static final long QQS_FADE_IN_DURATION = 200L; // Fade out faster than fade in to finish before QQS hides. private static final long QQS_FADE_OUT_DURATION = 50L; @@ -101,6 +102,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private final Executor mExecutor; private final TunerService mTunerService; private boolean mShowCollapsedOnKeyguard; + private boolean mTranslateWhileExpanding; @Inject public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader, @@ -242,6 +244,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha int width = mQs.getView() != null ? mQs.getView().getMeasuredWidth() : 0; int heightDiff = height - mQs.getHeader().getBottom() + mQs.getHeader().getPaddingBottom(); + if (!mTranslateWhileExpanding) { + heightDiff *= SHORT_PARALLAX_AMOUNT; + } firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0); int qqsTileHeight = 0; @@ -570,6 +575,13 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha setCurrentPosition(); }; + /** + * True whe QS will be pulled from the top, false when it will be clipped. + */ + public void setTranslateWhileExpanding(boolean shouldTranslate) { + mTranslateWhileExpanding = shouldTranslate; + } + static class HeightExpansionAnimator { private final List<View> mViews = new ArrayList<>(); private final ValueAnimator mAnimator; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index 6b09e2eb7b8b..e89803d16779 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -20,6 +20,8 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Canvas; +import android.graphics.Path; import android.graphics.Point; import android.util.AttributeSet; import android.view.View; @@ -54,6 +56,10 @@ public class QSContainerImpl extends FrameLayout { private static final PhysicsAnimator.SpringConfig BACKGROUND_SPRING = new PhysicsAnimator.SpringConfig(SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY); + private int mFancyClippingTop; + private int mFancyClippingBottom; + private final float[] mFancyClippingRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0}; + private final Path mFancyClippingPath = new Path(); private int mBackgroundBottom = -1; private int mHeightOverride = -1; private View mQSDetail; @@ -70,6 +76,7 @@ public class QSContainerImpl extends FrameLayout { private int mContentPadding = -1; private boolean mAnimateBottomOnNextLayout; private int mNavBarInset = 0; + private boolean mClippingEnabled; public QSContainerImpl(Context context, AttributeSet attrs) { super(context, attrs); @@ -169,6 +176,15 @@ public class QSContainerImpl extends FrameLayout { MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY)); } + @Override + public void dispatchDraw(Canvas canvas) { + if (!mFancyClippingPath.isEmpty()) { + canvas.translate(0, -getTranslationY()); + canvas.clipOutPath(mFancyClippingPath); + canvas.translate(0, getTranslationY()); + } + super.dispatchDraw(canvas); + } @Override protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, @@ -187,6 +203,7 @@ public class QSContainerImpl extends FrameLayout { super.onLayout(changed, left, top, right, bottom); updateExpansion(mAnimateBottomOnNextLayout /* animate */); mAnimateBottomOnNextLayout = false; + updateClippingPath(); } public void disable(int state1, int state2, boolean animate) { @@ -281,6 +298,7 @@ public class QSContainerImpl extends FrameLayout { public void setExpansion(float expansion) { mQsExpansion = expansion; + mQSPanelContainer.setScrollingEnabled(expansion > 0.0f); updateExpansion(); } @@ -318,4 +336,46 @@ public class QSContainerImpl extends FrameLayout { } return mSizePoint.y; } + + /** + * Clip QS bottom using a concave shape. + */ + public void setFancyClipping(int top, int bottom, int radius, boolean enabled) { + boolean updatePath = false; + if (mFancyClippingRadii[0] != radius) { + mFancyClippingRadii[0] = radius; + mFancyClippingRadii[1] = radius; + mFancyClippingRadii[2] = radius; + mFancyClippingRadii[3] = radius; + updatePath = true; + } + if (mFancyClippingTop != top) { + mFancyClippingTop = top; + updatePath = true; + } + if (mFancyClippingBottom != bottom) { + mFancyClippingBottom = bottom; + updatePath = true; + } + if (mClippingEnabled != enabled) { + mClippingEnabled = enabled; + updatePath = true; + } + + if (updatePath) { + updateClippingPath(); + } + } + + private void updateClippingPath() { + mFancyClippingPath.reset(); + if (!mClippingEnabled) { + invalidate(); + return; + } + + mFancyClippingPath.addRoundRect(0, mFancyClippingTop, getWidth(), + mFancyClippingBottom, mFancyClippingRadii, Path.Direction.CW); + invalidate(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index b95194adb9cc..d5cb777416a7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -107,6 +107,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private QuickQSPanelController mQuickQSPanelController; private QSCustomizerController mQSCustomizerController; private FeatureFlags mFeatureFlags; + /** + * When true, QS will translate from outside the screen. It will be clipped with parallax + * otherwise. + */ + private boolean mTranslateWhileExpanding; @Inject public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler, @@ -254,6 +259,13 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } } + @Override + public void setFancyClipping(int top, int bottom, int cornerRadius, boolean visible) { + if (getView() instanceof QSContainerImpl) { + ((QSContainerImpl) getView()).setFancyClipping(top, bottom, cornerRadius, visible); + } + } + private void setEditLocation(View view) { View edit = view.findViewById(android.R.id.edit); int[] loc = edit.getLocationOnScreen(); @@ -394,16 +406,23 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } @Override + public void setTranslateWhileExpanding(boolean shouldTranslate) { + mTranslateWhileExpanding = shouldTranslate; + mQSAnimator.setTranslateWhileExpanding(shouldTranslate); + } + + @Override public void setQsExpansion(float expansion, float headerTranslation) { if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation); if (mQSAnimator != null) { final boolean showQSOnLockscreen = expansion > 0; - final boolean showQSUnlocked = headerTranslation == 0; + final boolean showQSUnlocked = headerTranslation == 0 || !mTranslateWhileExpanding; mQSAnimator.startAlphaAnimation(showQSOnLockscreen || showQSUnlocked); } mContainer.setExpansion(expansion); - final float translationScaleY = expansion - 1; + final float translationScaleY = (mTranslateWhileExpanding + ? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1); boolean onKeyguardAndExpanded = isKeyguardShowing() && !mShowCollapsedOnKeyguard; if (!mHeaderAnimating && !headerWillBeAnimating()) { getView().setTranslationY( diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt index 2168b1f66aa9..71f42d819a1a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt @@ -20,7 +20,6 @@ import android.content.ComponentName import android.content.Intent import android.os.Handler import android.os.Looper -import android.provider.Settings import android.service.quicksettings.Tile import android.view.View import com.android.internal.logging.MetricsLogger @@ -66,7 +65,6 @@ class DeviceControlsTile @Inject constructor( ) { private var hasControlsApps = AtomicBoolean(false) - private val intent = Intent(Settings.ACTION_DEVICE_CONTROLS_SETTINGS) private val icon = ResourceIcon.get(R.drawable.ic_device_light) @@ -91,13 +89,10 @@ class DeviceControlsTile @Inject constructor( override fun newTileState(): QSTile.State { return QSTile.State().also { it.state = Tile.STATE_UNAVAILABLE // Start unavailable matching `hasControlsApps` + it.handlesLongClick = false } } - override fun handleDestroy() { - super.handleDestroy() - } - override fun handleClick(view: View?) { if (state.state == Tile.STATE_ACTIVE) { mUiHandler.post { @@ -138,10 +133,12 @@ class DeviceControlsTile @Inject constructor( return 0 } - override fun getLongClickIntent(): Intent { - return intent + override fun getLongClickIntent(): Intent? { + return null } + override fun handleLongClick(view: View?) {} + override fun getTileLabel(): CharSequence { return mContext.getText(R.string.quick_controls_title) } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index 0106f4316390..b5e51c68b76a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -20,6 +20,8 @@ import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK; import static com.android.systemui.screenshot.LogConfig.DEBUG_STORAGE; import static com.android.systemui.screenshot.LogConfig.logTag; +import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType.QUICK_SHARE_ACTION; +import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType.REGULAR_SMART_ACTIONS; import android.app.ActivityTaskManager; import android.app.Notification; @@ -74,6 +76,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { private final ScreenshotSmartActions mScreenshotSmartActions; private final ScreenshotController.SaveImageInBackgroundData mParams; private final ScreenshotController.SavedImageData mImageData; + private final ScreenshotController.QuickShareData mQuickShareData; private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider; private String mScreenshotId; @@ -90,6 +93,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mContext = context; mScreenshotSmartActions = screenshotSmartActions; mImageData = new ScreenshotController.SavedImageData(); + mQuickShareData = new ScreenshotController.QuickShareData(); mSharedElementTransition = sharedElementTransition; mImageExporter = exporter; @@ -127,6 +131,13 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { Bitmap image = mParams.image; mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, requestId); try { + if (mSmartActionsEnabled && mParams.mQuickShareActionsReadyListener != null) { + // Since Quick Share target recommendation does not rely on image URL, it is + // queried and surfaced before image compress/export. Action intent would not be + // used, because it does not contain image URL. + queryQuickShareAction(image, user); + } + // Call synchronously here since already on a background thread. ListenableFuture<ImageExporter.Result> future = mImageExporter.export(Runnable::run, requestId, image); @@ -136,7 +147,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { CompletableFuture<List<Notification.Action>> smartActionsFuture = mScreenshotSmartActions.getSmartActionsFuture( - mScreenshotId, uri, image, mSmartActionsProvider, + mScreenshotId, uri, image, mSmartActionsProvider, REGULAR_SMART_ACTIONS, mSmartActionsEnabled, user); List<Notification.Action> smartActions = new ArrayList<>(); @@ -148,7 +159,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { smartActions.addAll(buildSmartActions( mScreenshotSmartActions.getSmartActions( mScreenshotId, smartActionsFuture, timeoutMs, - mSmartActionsProvider), + mSmartActionsProvider, REGULAR_SMART_ACTIONS), mContext)); } @@ -157,6 +168,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mImageData.shareTransition = createShareAction(mContext, mContext.getResources(), uri); mImageData.editTransition = createEditAction(mContext, mContext.getResources(), uri); mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri); + mImageData.quickShareAction = createQuickShareAction(mContext, + mQuickShareData.quickShareAction, uri); mParams.mActionsReadyListener.onActionsReady(mImageData); if (DEBUG_CALLBACK) { @@ -173,6 +186,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { } mParams.clearImage(); mImageData.reset(); + mQuickShareData.reset(); mParams.mActionsReadyListener.onActionsReady(mImageData); if (DEBUG_CALLBACK) { Log.d(TAG, "Calling (Consumer<Uri>) finisher.accept(null)"); @@ -197,6 +211,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { // params. The finisher is expected to always be called back, so just use the baked-in // params from the ctor in any case. mImageData.reset(); + mQuickShareData.reset(); mParams.mActionsReadyListener.onActionsReady(mImageData); if (DEBUG_CALLBACK) { Log.d(TAG, "onCancelled, calling (Consumer<Uri>) finisher.accept(null)"); @@ -389,4 +404,74 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { .putExtra(ScreenshotController.EXTRA_ID, screenshotId) .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled); } + + /** + * Populate image uri into intent of Quick Share action. + */ + @VisibleForTesting + private Notification.Action createQuickShareAction(Context context, Notification.Action action, + Uri uri) { + if (action == null) { + return null; + } + // Populate image URI into Quick Share chip intent + Intent sharingIntent = action.actionIntent.getIntent(); + sharingIntent.setType("image/png"); + sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); + String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime)); + String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate); + sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject); + // Include URI in ClipData also, so that grantPermission picks it up. + // We don't use setData here because some apps interpret this as "to:". + ClipData clipdata = new ClipData(new ClipDescription("content", + new String[]{"image/png"}), + new ClipData.Item(uri)); + sharingIntent.setClipData(clipdata); + sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + PendingIntent updatedPendingIntent = PendingIntent.getActivity( + context, 0, sharingIntent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); + + // Proxy smart actions through {@link GlobalScreenshot.SmartActionsReceiver} + // for logging smart actions. + Bundle extras = action.getExtras(); + String actionType = extras.getString( + ScreenshotNotificationSmartActionsProvider.ACTION_TYPE, + ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE); + Intent intent = new Intent(context, SmartActionsReceiver.class) + .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, updatedPendingIntent) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled); + PendingIntent broadcastIntent = PendingIntent.getBroadcast(context, + mRandom.nextInt(), + intent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); + return new Notification.Action.Builder(action.getIcon(), action.title, + broadcastIntent).setContextual(true).addExtras(extras).build(); + } + + /** + * Query and surface Quick Share chip if it is available. Action intent would not be used, + * because it does not contain image URL which would be populated in {@link + * #createQuickShareAction(Context, Notification.Action, Uri)} + */ + private void queryQuickShareAction(Bitmap image, UserHandle user) { + CompletableFuture<List<Notification.Action>> quickShareActionsFuture = + mScreenshotSmartActions.getSmartActionsFuture( + mScreenshotId, null, image, mSmartActionsProvider, + QUICK_SHARE_ACTION, + mSmartActionsEnabled, user); + int timeoutMs = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.SCREENSHOT_NOTIFICATION_QUICK_SHARE_ACTIONS_TIMEOUT_MS, + 500); + List<Notification.Action> quickShareActions = + mScreenshotSmartActions.getSmartActions( + mScreenshotId, quickShareActionsFuture, timeoutMs, + mSmartActionsProvider, QUICK_SHARE_ACTION); + if (!quickShareActions.isEmpty()) { + mQuickShareData.quickShareAction = quickShareActions.get(0); + mParams.mQuickShareActionsReadyListener.onActionsReady(mQuickShareData); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 9d0198615dcd..7c2d4768fe8f 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -114,6 +114,7 @@ public class ScreenshotController { public Bitmap image; public Consumer<Uri> finisher; public ScreenshotController.ActionsReadyListener mActionsReadyListener; + public ScreenshotController.QuickShareActionReadyListener mQuickShareActionsReadyListener; void clearImage() { image = null; @@ -129,6 +130,7 @@ public class ScreenshotController { public Supplier<ActionTransition> editTransition; public Notification.Action deleteAction; public List<Notification.Action> smartActions; + public Notification.Action quickShareAction; /** * POD for shared element transition. @@ -148,6 +150,21 @@ public class ScreenshotController { editTransition = null; deleteAction = null; smartActions = null; + quickShareAction = null; + } + } + + /** + * Structure returned by the QueryQuickShareInBackgroundTask + */ + static class QuickShareData { + public Notification.Action quickShareAction; + + /** + * Used to reset the return data on error + */ + public void reset() { + quickShareAction = null; } } @@ -155,6 +172,10 @@ public class ScreenshotController { void onActionsReady(ScreenshotController.SavedImageData imageData); } + interface QuickShareActionReadyListener { + void onActionsReady(ScreenshotController.QuickShareData quickShareData); + } + // These strings are used for communicating the action invoked to // ScreenshotNotificationSmartActionsProvider. static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type"; @@ -519,7 +540,8 @@ public class ScreenshotController { mScreenBitmap.setHasAlpha(false); mScreenBitmap.prepareToDraw(); - saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady); + saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady, + this::showUiOnQuickShareActionReady); // The window is focusable by default setWindowFocusable(true); @@ -664,20 +686,21 @@ public class ScreenshotController { saveScreenshotInWorkerThread( /* onComplete */ finisher, /* actionsReadyListener */ imageData -> { - if (DEBUG_CALLBACK) { - Log.d(TAG, "returning URI to finisher (Consumer<URI>): " + imageData.uri); - } - finisher.accept(imageData.uri); - if (imageData.uri == null) { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED); - mNotificationsController.notifyScreenshotError( - R.string.screenshot_failed_to_save_text); - } else { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED); - mScreenshotHandler.post(() -> Toast.makeText(mContext, - R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show()); - } - }); + if (DEBUG_CALLBACK) { + Log.d(TAG, "returning URI to finisher (Consumer<URI>): " + imageData.uri); + } + finisher.accept(imageData.uri); + if (imageData.uri == null) { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED); + mNotificationsController.notifyScreenshotError( + R.string.screenshot_failed_to_save_text); + } else { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED); + mScreenshotHandler.post(() -> Toast.makeText(mContext, + R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show()); + } + }, + null); } /** @@ -718,12 +741,15 @@ public class ScreenshotController { * Creates a new worker thread and saves the screenshot to the media store. */ private void saveScreenshotInWorkerThread(Consumer<Uri> finisher, - @Nullable ScreenshotController.ActionsReadyListener actionsReadyListener) { + @Nullable ScreenshotController.ActionsReadyListener actionsReadyListener, + @Nullable ScreenshotController.QuickShareActionReadyListener + quickShareActionsReadyListener) { ScreenshotController.SaveImageInBackgroundData data = new ScreenshotController.SaveImageInBackgroundData(); data.image = mScreenBitmap; data.finisher = finisher; data.mActionsReadyListener = actionsReadyListener; + data.mQuickShareActionsReadyListener = quickShareActionsReadyListener; if (mSaveInBgTask != null) { // just log success/failure for the pre-existing screenshot @@ -786,6 +812,30 @@ public class ScreenshotController { } /** + * Sets up the action shade and its entrance animation, once we get the Quick Share action data. + */ + private void showUiOnQuickShareActionReady(ScreenshotController.QuickShareData quickShareData) { + if (DEBUG_UI) { + Log.d(TAG, "Showing UI for Quick Share action"); + } + if (quickShareData.quickShareAction != null) { + mScreenshotHandler.post(() -> { + if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) { + mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + mScreenshotView.addQuickShareChip(quickShareData.quickShareAction); + } + }); + } else { + mScreenshotView.addQuickShareChip(quickShareData.quickShareAction); + } + }); + } + } + + /** * Supplies the necessary bits for the shared element transition to share sheet. * Note that once supplied, the action intent to share must be sent immediately after. */ diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java index 9bd7923016f2..99238cd2c267 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java @@ -60,11 +60,14 @@ public class ScreenshotSmartActions { CompletableFuture<List<Notification.Action>> getSmartActionsFuture( String screenshotId, Uri screenshotUri, Bitmap image, ScreenshotNotificationSmartActionsProvider smartActionsProvider, + ScreenshotSmartActionType actionType, boolean smartActionsEnabled, UserHandle userHandle) { if (DEBUG_ACTIONS) { - Log.d(TAG, String.format("getSmartActionsFuture id=%s, uri=%s, provider=%s, " - + "smartActionsEnabled=%b, userHandle=%s", screenshotId, screenshotUri, - smartActionsProvider.getClass(), smartActionsEnabled, userHandle)); + Log.d(TAG, String.format( + "getSmartActionsFuture id=%s, uri=%s, provider=%s, actionType=%s, " + + "smartActionsEnabled=%b, userHandle=%s", + screenshotId, screenshotUri, smartActionsProvider.getClass(), actionType, + smartActionsEnabled, userHandle)); } if (!smartActionsEnabled) { if (DEBUG_ACTIONS) { @@ -89,7 +92,7 @@ public class ScreenshotSmartActions { ? runningTask.topActivity : new ComponentName("", ""); smartActionsFuture = smartActionsProvider.getActions(screenshotId, screenshotUri, image, - componentName, ScreenshotSmartActionType.REGULAR_SMART_ACTIONS, userHandle); + componentName, actionType, userHandle); } catch (Throwable e) { long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList()); @@ -107,19 +110,21 @@ public class ScreenshotSmartActions { @VisibleForTesting List<Notification.Action> getSmartActions(String screenshotId, CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs, - ScreenshotNotificationSmartActionsProvider smartActionsProvider) { + ScreenshotNotificationSmartActionsProvider smartActionsProvider, + ScreenshotSmartActionType actionType) { long startTimeMs = SystemClock.uptimeMillis(); if (DEBUG_ACTIONS) { - Log.d(TAG, String.format("getSmartActions id=%s, timeoutMs=%d, provider=%s", - screenshotId, timeoutMs, smartActionsProvider.getClass())); + Log.d(TAG, + String.format("getSmartActions id=%s, timeoutMs=%d, actionType=%s, provider=%s", + screenshotId, timeoutMs, actionType, smartActionsProvider.getClass())); } try { List<Notification.Action> actions = smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS); long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; if (DEBUG_ACTIONS) { - Log.d(TAG, String.format("Got %d smart actions. Wait time: %d ms", - actions.size(), waitTimeMs)); + Log.d(TAG, String.format("Got %d smart actions. Wait time: %d ms, actionType=%s", + actions.size(), waitTimeMs, actionType)); } notifyScreenshotOp(screenshotId, smartActionsProvider, ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS, @@ -129,8 +134,9 @@ public class ScreenshotSmartActions { } catch (Throwable e) { long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; if (DEBUG_ACTIONS) { - Log.e(TAG, String.format("Error getting smart actions. Wait time: %d ms", - waitTimeMs), e); + Log.e(TAG, String.format( + "Error getting smart actions. Wait time: %d ms, actionType=%s", + waitTimeMs, actionType), e); } ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status = (e instanceof TimeoutException) @@ -165,7 +171,7 @@ public class ScreenshotSmartActions { SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider( context, THREAD_POOL_EXECUTOR, new Handler()); if (DEBUG_ACTIONS) { - Log.e(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b", + Log.d(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b", provider.getClass(), action, screenshotId, isSmartAction)); } provider.notifyAction(screenshotId, action, isSmartAction); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index d15f1ff97e7e..70b11337db93 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -137,6 +137,7 @@ public class ScreenshotView extends FrameLayout implements private ScreenshotActionChip mShareChip; private ScreenshotActionChip mEditChip; private ScreenshotActionChip mScrollChip; + private ScreenshotActionChip mQuickShareChip; private UiEventLogger mUiEventLogger; private ScreenshotViewCallback mCallbacks; @@ -151,7 +152,8 @@ public class ScreenshotView extends FrameLayout implements private enum PendingInteraction { PREVIEW, EDIT, - SHARE + SHARE, + QUICK_SHARE } public ScreenshotView(Context context) { @@ -505,6 +507,9 @@ public class ScreenshotView extends FrameLayout implements mShareChip.setOnClickListener(v -> { mShareChip.setIsPending(true); mEditChip.setIsPending(false); + if (mQuickShareChip != null) { + mQuickShareChip.setIsPending(false); + } mPendingInteraction = PendingInteraction.SHARE; }); chips.add(mShareChip); @@ -514,6 +519,9 @@ public class ScreenshotView extends FrameLayout implements mEditChip.setOnClickListener(v -> { mEditChip.setIsPending(true); mShareChip.setIsPending(false); + if (mQuickShareChip != null) { + mQuickShareChip.setIsPending(false); + } mPendingInteraction = PendingInteraction.EDIT; }); chips.add(mEditChip); @@ -521,6 +529,9 @@ public class ScreenshotView extends FrameLayout implements mScreenshotPreview.setOnClickListener(v -> { mShareChip.setIsPending(false); mEditChip.setIsPending(false); + if (mQuickShareChip != null) { + mQuickShareChip.setIsPending(false); + } mPendingInteraction = PendingInteraction.PREVIEW; }); @@ -582,6 +593,13 @@ public class ScreenshotView extends FrameLayout implements startSharedTransition( imageData.editTransition.get()); }); + if (mQuickShareChip != null) { + mQuickShareChip.setPendingIntent(imageData.quickShareAction.actionIntent, + () -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED); + animateDismissal(); + }); + } if (mPendingInteraction != null) { switch (mPendingInteraction) { @@ -594,6 +612,9 @@ public class ScreenshotView extends FrameLayout implements case EDIT: mEditChip.callOnClick(); break; + case QUICK_SHARE: + mQuickShareChip.callOnClick(); + break; } } else { LayoutInflater inflater = LayoutInflater.from(mContext); @@ -615,6 +636,25 @@ public class ScreenshotView extends FrameLayout implements } } + void addQuickShareChip(Notification.Action quickShareAction) { + if (mPendingInteraction == null) { + LayoutInflater inflater = LayoutInflater.from(mContext); + mQuickShareChip = (ScreenshotActionChip) inflater.inflate( + R.layout.global_screenshot_action_chip, mActionsView, false); + mQuickShareChip.setText(quickShareAction.title); + mQuickShareChip.setIcon(quickShareAction.getIcon(), false); + mQuickShareChip.setOnClickListener(v -> { + mShareChip.setIsPending(false); + mEditChip.setIsPending(false); + mQuickShareChip.setIsPending(true); + mPendingInteraction = PendingInteraction.QUICK_SHARE; + }); + mQuickShareChip.setAlpha(1); + mActionsView.addView(mQuickShareChip); + mSmartChips.add(mQuickShareChip); + } + } + boolean isDismissing() { return (mDismissAnimation != null && mDismissAnimation.isRunning()); } @@ -700,6 +740,7 @@ public class ScreenshotView extends FrameLayout implements mActionsView.removeView(chip); } mSmartChips.clear(); + mQuickShareChip = null; setAlpha(1); mDismissButton.setTranslationY(0); mActionsContainer.setTranslationY(0); diff --git a/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java index 1fc126eb161d..0a55fbe8bf75 100644 --- a/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 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.internal.colorextraction.drawable; +package com.android.systemui.scrim; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -49,6 +49,7 @@ public class ScrimDrawable extends Drawable { private float mCornerRadius; private Rect mBounds; private ConcaveInfo mConcaveInfo; + private int mBottomEdgePosition; public ScrimDrawable() { mPaint = new Paint(); @@ -143,21 +144,43 @@ public class ScrimDrawable extends Drawable { * Make bottom edge concave with provided corner radius */ public void setBottomEdgeConcave(float radius) { + if (radius == 0) { + // Disable clipping completely when there's no radius. + mConcaveInfo = null; + return; + } // only rounding top corners for clip out path float[] cornerRadii = new float[]{radius, radius, radius, radius, 0, 0, 0, 0}; mConcaveInfo = new ConcaveInfo(radius, cornerRadii); } + /** + * Location of concave edge. + * @see #setBottomEdgeConcave(float) + */ + public void setBottomEdgePosition(int y) { + if (mBottomEdgePosition == y) { + return; + } + mBottomEdgePosition = y; + if (mConcaveInfo == null) { + return; + } + updatePath(); + invalidateSelf(); + } + @Override public void draw(@NonNull Canvas canvas) { mPaint.setColor(mMainColor); mPaint.setAlpha(mAlpha); if (mConcaveInfo != null) { drawConcave(canvas); + } else { + canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right, + getBounds().bottom + mCornerRadius, + /* x radius*/ mCornerRadius, /* y radius*/ mCornerRadius, mPaint); } - canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right, - getBounds().bottom + mCornerRadius, - /* x radius*/ mCornerRadius, /* y radius*/ mCornerRadius, mPaint); } private void drawConcave(Canvas canvas) { @@ -165,19 +188,23 @@ public class ScrimDrawable extends Drawable { if (mBounds == null || getBounds().right != mBounds.right || getBounds().left != mBounds.left) { - mConcaveInfo.mPath.reset(); - float left = getBounds().left; - float right = getBounds().right; - float top = 0f; - float bottom = mConcaveInfo.mPathOverlap; - mConcaveInfo.mPath.addRoundRect(left, top, right, bottom, - mConcaveInfo.mCornerRadii, Path.Direction.CW); + mBounds = getBounds(); + updatePath(); } - mBounds = getBounds(); - int translation = (int) (mBounds.bottom - mConcaveInfo.mPathOverlap); - canvas.translate(0, translation); canvas.clipOutPath(mConcaveInfo.mPath); - canvas.translate(0, -translation); + canvas.drawRect(getBounds().left, getBounds().top, getBounds().right, + mBottomEdgePosition + mConcaveInfo.mPathOverlap, mPaint); + } + + private void updatePath() { + mConcaveInfo.mPath.reset(); + if (mBounds == null) { + mBounds = getBounds(); + } + float top = mBottomEdgePosition; + float bottom = mBottomEdgePosition + mConcaveInfo.mPathOverlap; + mConcaveInfo.mPath.addRoundRect(mBounds.left, top, mBounds.right, bottom, + mConcaveInfo.mCornerRadii, Path.Direction.CW); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java index a537299d4979..0d9ade6da49c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java +++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 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. @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.scrim; import static java.lang.Float.isNaN; @@ -38,7 +38,6 @@ import androidx.core.graphics.ColorUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; -import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.systemui.R; import java.util.concurrent.Executor; @@ -96,6 +95,9 @@ public class ScrimView extends View { }); } + /** + * Needed for WM Shell, which has its own thread structure. + */ public void setExecutor(Executor executor, Looper looper) { mExecutor = executor; mExecutorLooper = looper; @@ -108,7 +110,8 @@ public class ScrimView extends View { } } - public void setDrawable(Drawable drawable) { + @VisibleForTesting + void setDrawable(Drawable drawable) { executeOnExecutor(() -> { mDrawable = drawable; mDrawable.setCallback(this); @@ -144,16 +147,24 @@ public class ScrimView extends View { }); } + /** + * Sets the color of the scrim, without animating them. + */ public void setColors(@NonNull ColorExtractor.GradientColors colors) { setColors(colors, false); } + /** + * Sets the scrim colors, optionally animating them. + * @param colors The colors. + * @param animated If we should animate the transition. + */ public void setColors(@NonNull ColorExtractor.GradientColors colors, boolean animated) { if (colors == null) { throw new IllegalArgumentException("Colors cannot be null"); } executeOnExecutor(() -> { - synchronized(mColorLock) { + synchronized (mColorLock) { if (mColors.equals(colors)) { return; } @@ -168,17 +179,28 @@ public class ScrimView extends View { return mDrawable; } + /** + * Returns current scrim colors. + */ public ColorExtractor.GradientColors getColors() { - synchronized(mColorLock) { + synchronized (mColorLock) { mTmpColors.set(mColors); } return mTmpColors; } + /** + * Applies tint to this view, without animations. + */ public void setTint(int color) { setTint(color, false); } + /** + * Tints this view, optionally animating it. + * @param color The color. + * @param animated If we should animate. + */ public void setTint(int color, boolean animated) { executeOnExecutor(() -> { if (mTintColor == color) { @@ -200,8 +222,8 @@ public class ScrimView extends View { } else { boolean hasAlpha = Color.alpha(mTintColor) != 0; if (hasAlpha) { - PorterDuff.Mode targetMode = mColorFilter == null ? Mode.SRC_OVER : - mColorFilter.getMode(); + PorterDuff.Mode targetMode = mColorFilter == null + ? Mode.SRC_OVER : mColorFilter.getMode(); if (mColorFilter == null || mColorFilter.getColor() != mTintColor) { mColorFilter = new PorterDuffColorFilter(mTintColor, targetMode); } @@ -254,6 +276,9 @@ public class ScrimView extends View { return mViewAlpha; } + /** + * Sets a callback that is invoked whenever the alpha, color, or tint change. + */ public void setChangeRunnable(Runnable changeRunnable, Executor changeRunnableExecutor) { mChangeRunnable = changeRunnable; mChangeRunnableExecutor = changeRunnableExecutor; @@ -276,9 +301,9 @@ public class ScrimView extends View { * Make bottom edge concave so overlap between layers is not visible for alphas between 0 and 1 * @return height of concavity */ - public float enableBottomEdgeConcave() { + public float enableBottomEdgeConcave(boolean clipScrim) { if (mDrawable instanceof ScrimDrawable) { - float radius = getResources().getDimensionPixelSize(CORNER_RADIUS); + float radius = clipScrim ? getResources().getDimensionPixelSize(CORNER_RADIUS) : 0; ((ScrimDrawable) mDrawable).setBottomEdgeConcave(radius); return radius; } @@ -286,6 +311,16 @@ public class ScrimView extends View { } /** + * The position of the bottom of the scrim, used for clipping. + * @see #enableBottomEdgeConcave(boolean) + */ + public void setBottomEdgePosition(int y) { + if (mDrawable instanceof ScrimDrawable) { + ((ScrimDrawable) mDrawable).setBottomEdgePosition(y); + } + } + + /** * Enable view to have rounded corners with radius of {@link #CORNER_RADIUS} */ public void enableRoundedCorners() { diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java index 357256cba131..1ad253e7f867 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java @@ -24,7 +24,9 @@ import android.animation.ValueAnimator; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; +import android.hardware.display.BrightnessInfo; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManager.DisplayListener; import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; @@ -64,17 +66,11 @@ public class BrightnessController implements ToggleSlider.Listener { private static final Uri BRIGHTNESS_MODE_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE); - private static final Uri BRIGHTNESS_URI = - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); - private static final Uri BRIGHTNESS_FLOAT_URI = - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT); private static final Uri BRIGHTNESS_FOR_VR_FLOAT_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT); - private final float mDefaultBacklight; private final float mMinimumBacklightForVr; private final float mMaximumBacklightForVr; - private final float mDefaultBacklightForVr; private final int mDisplayId; private final Context mContext; @@ -86,6 +82,20 @@ public class BrightnessController implements ToggleSlider.Listener { private final Handler mBackgroundHandler; private final BrightnessObserver mBrightnessObserver; + private final DisplayListener mDisplayListener = new DisplayListener() { + @Override + public void onDisplayAdded(int displayId) {} + + @Override + public void onDisplayRemoved(int displayId) {} + + @Override + public void onDisplayChanged(int displayId) { + mBackgroundHandler.post(mUpdateSliderRunnable); + notifyCallbacks(); + } + }; + private ArrayList<BrightnessStateChangeCallback> mChangeCallbacks = new ArrayList<BrightnessStateChangeCallback>(); @@ -94,6 +104,8 @@ public class BrightnessController implements ToggleSlider.Listener { private boolean mListening; private boolean mExternalChange; private boolean mControlValueInitialized; + private float mBrightnessMin = PowerManager.BRIGHTNESS_MIN; + private float mBrightnessMax = PowerManager.BRIGHTNESS_MAX; private ValueAnimator mSliderAnimator; @@ -110,28 +122,19 @@ public class BrightnessController implements ToggleSlider.Listener { } @Override - public void onChange(boolean selfChange) { - onChange(selfChange, null); - } - - @Override public void onChange(boolean selfChange, Uri uri) { if (selfChange) return; if (BRIGHTNESS_MODE_URI.equals(uri)) { mBackgroundHandler.post(mUpdateModeRunnable); mBackgroundHandler.post(mUpdateSliderRunnable); - } else if (BRIGHTNESS_FLOAT_URI.equals(uri)) { - mBackgroundHandler.post(mUpdateSliderRunnable); } else if (BRIGHTNESS_FOR_VR_FLOAT_URI.equals(uri)) { mBackgroundHandler.post(mUpdateSliderRunnable); } else { mBackgroundHandler.post(mUpdateModeRunnable); mBackgroundHandler.post(mUpdateSliderRunnable); } - for (BrightnessStateChangeCallback cb : mChangeCallbacks) { - cb.onBrightnessLevelChanged(); - } + notifyCallbacks(); } public void startObserving() { @@ -141,19 +144,16 @@ public class BrightnessController implements ToggleSlider.Listener { BRIGHTNESS_MODE_URI, false, this, UserHandle.USER_ALL); cr.registerContentObserver( - BRIGHTNESS_URI, - false, this, UserHandle.USER_ALL); - cr.registerContentObserver( - BRIGHTNESS_FLOAT_URI, - false, this, UserHandle.USER_ALL); - cr.registerContentObserver( BRIGHTNESS_FOR_VR_FLOAT_URI, false, this, UserHandle.USER_ALL); + mDisplayManager.registerDisplayListener(mDisplayListener, mHandler, + DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS); } public void stopObserving() { final ContentResolver cr = mContext.getContentResolver(); cr.unregisterContentObserver(this); + mDisplayManager.unregisterDisplayListener(mDisplayListener); } } @@ -233,11 +233,15 @@ public class BrightnessController implements ToggleSlider.Listener { private final Runnable mUpdateSliderRunnable = new Runnable() { @Override public void run() { - final float valFloat; final boolean inVrMode = mIsVrModeEnabled; - valFloat = mDisplayManager.getBrightness(mDisplayId); + final BrightnessInfo info = mContext.getDisplay().getBrightnessInfo(); + if (info == null) { + return; + } + mBrightnessMax = info.brightnessMaximum; + mBrightnessMin = info.brightnessMinimum; // Value is passed as intbits, since this is what the message takes. - final int valueAsIntBits = Float.floatToIntBits(valFloat); + final int valueAsIntBits = Float.floatToIntBits(info.brightness); mHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits, inVrMode ? 1 : 0).sendToTarget(); } @@ -295,13 +299,10 @@ public class BrightnessController implements ToggleSlider.Listener { mDisplayId = mContext.getDisplayId(); PowerManager pm = context.getSystemService(PowerManager.class); - mDefaultBacklight = mContext.getDisplay().getBrightnessDefault(); mMinimumBacklightForVr = pm.getBrightnessConstraint( PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR); mMaximumBacklightForVr = pm.getBrightnessConstraint( PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR); - mDefaultBacklightForVr = pm.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR); mDisplayManager = context.getSystemService(DisplayManager.class); mVrManager = IVrManager.Stub.asInterface(ServiceManager.getService( @@ -337,7 +338,6 @@ public class BrightnessController implements ToggleSlider.Listener { final float minBacklight; final float maxBacklight; final int metric; - final String settingToChange; if (mIsVrModeEnabled) { metric = MetricsEvent.ACTION_BRIGHTNESS_FOR_VR; @@ -347,12 +347,12 @@ public class BrightnessController implements ToggleSlider.Listener { metric = mAutomatic ? MetricsEvent.ACTION_BRIGHTNESS_AUTO : MetricsEvent.ACTION_BRIGHTNESS; - minBacklight = PowerManager.BRIGHTNESS_MIN; - maxBacklight = PowerManager.BRIGHTNESS_MAX; + minBacklight = mBrightnessMin; + maxBacklight = mBrightnessMax; } - final float valFloat = MathUtils.min(convertGammaToLinearFloat(value, - minBacklight, maxBacklight), - 1.0f); + final float valFloat = MathUtils.min( + convertGammaToLinearFloat(value, minBacklight, maxBacklight), + maxBacklight); if (stopTracking) { // TODO(brightnessfloat): change to use float value instead. MetricsLogger.action(mContext, metric, @@ -403,8 +403,8 @@ public class BrightnessController implements ToggleSlider.Listener { min = mMinimumBacklightForVr; max = mMaximumBacklightForVr; } else { - min = PowerManager.BRIGHTNESS_MIN; - max = PowerManager.BRIGHTNESS_MAX; + min = mBrightnessMin; + max = mBrightnessMax; } // convertGammaToLinearFloat returns 0-1 if (BrightnessSynchronizer.floatEquals(brightnessValue, @@ -439,6 +439,13 @@ public class BrightnessController implements ToggleSlider.Listener { mSliderAnimator.start(); } + private void notifyCallbacks() { + final int size = mChangeCallbacks.size(); + for (int i = 0; i < size; i++) { + mChangeCallbacks.get(i).onBrightnessLevelChanged(); + } + } + /** Factory for creating a {@link BrightnessController}. */ public static class Factory { private final Context mContext; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index f4266a27c6b6..fb109f310e15 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -84,7 +84,6 @@ public class NotificationShelf extends ActivatableNotificationView implements private int mCutoutHeight; private int mGapHeight; private int mIndexOfFirstViewInShelf = -1; - private int mIndexOfFirstViewInOverflowingSection = -1; private NotificationShelfController mController; @@ -180,7 +179,6 @@ public class NotificationShelf extends ActivatableNotificationView implements viewState.xTranslation = getTranslationX(); viewState.hasItemsInStableShelf = lastViewState.inShelf; viewState.firstViewInShelf = algorithmState.firstViewInShelf; - viewState.firstViewInOverflowSection = algorithmState.firstViewInOverflowSection; if (mNotGoneIndex != -1) { viewState.notGoneIndex = Math.min(viewState.notGoneIndex, mNotGoneIndex); } @@ -268,17 +266,6 @@ public class NotificationShelf extends ActivatableNotificationView implements // TODO(b/172289889) scale mPaddingBetweenElements with expansion amount if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) { notificationClipEnd = stackEnd; - } else if (mAmbientState.isExpansionChanging()) { - if (mIndexOfFirstViewInOverflowingSection != -1 - && i >= mIndexOfFirstViewInOverflowingSection) { - // Clip notifications in (section overflowing into shelf) to shelf start. - notificationClipEnd = shelfStart - mPaddingBetweenElements; - } else { - // Clip notifications before the section overflowing into shelf - // to stackEnd because we do not show the shelf if the section right before the - // shelf is still hidden. - notificationClipEnd = stackEnd; - } } else { notificationClipEnd = shelfStart - mPaddingBetweenElements; } @@ -692,7 +679,6 @@ public class NotificationShelf extends ActivatableNotificationView implements private void setHideBackground(boolean hideBackground) { if (mHideBackground != hideBackground) { mHideBackground = hideBackground; - updateBackground(); updateOutline(); } } @@ -702,10 +688,6 @@ public class NotificationShelf extends ActivatableNotificationView implements return !mHideBackground && super.needsOutline(); } - @Override - protected boolean shouldHideBackground() { - return super.shouldHideBackground() || mHideBackground; - } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { @@ -836,11 +818,6 @@ public class NotificationShelf extends ActivatableNotificationView implements mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf); } - public void setFirstViewInOverflowingSection(ExpandableView firstViewInOverflowingSection) { - mIndexOfFirstViewInOverflowingSection = - mHostLayoutController.indexOfChild(firstViewInOverflowingSection); - } - private class ShelfState extends ExpandableViewState { private boolean hasItemsInStableShelf; private ExpandableView firstViewInShelf; @@ -854,7 +831,6 @@ public class NotificationShelf extends ActivatableNotificationView implements super.applyToView(view); setIndexOfFirstViewInShelf(firstViewInShelf); - setFirstViewInOverflowingSection(firstViewInOverflowSection); updateAppearance(); setHasItemsInStableShelf(hasItemsInStableShelf); mShelfIcons.setAnimationsEnabled(mAnimationsEnabled); @@ -868,7 +844,6 @@ public class NotificationShelf extends ActivatableNotificationView implements super.animateTo(child, properties); setIndexOfFirstViewInShelf(firstViewInShelf); - setFirstViewInOverflowingSection(firstViewInOverflowSection); updateAppearance(); setHasItemsInStableShelf(hasItemsInStableShelf); mShelfIcons.setAnimationsEnabled(mAnimationsEnabled); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt index 48e28f7b48f3..3bf1ff2cf7fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.charging import android.content.Context +import android.content.res.Configuration import android.graphics.PixelFormat import android.graphics.PointF import android.os.SystemProperties @@ -32,6 +33,7 @@ import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.leak.RotationUtils +import com.android.systemui.R import java.io.PrintWriter import javax.inject.Inject @@ -50,6 +52,10 @@ class WiredChargingRippleController @Inject constructor( private var charging: Boolean? = null private val rippleEnabled: Boolean = featureFlags.isChargingRippleEnabled && !SystemProperties.getBoolean("persist.debug.suppress-charging-ripple", false) + private var normalizedPortPosX: Float = context.resources.getFloat( + R.dimen.physical_charger_port_location_normalized_x) + private var normalizedPortPosY: Float = context.resources.getFloat( + R.dimen.physical_charger_port_location_normalized_y) private val windowLayoutParams = WindowManager.LayoutParams().apply { width = WindowManager.LayoutParams.MATCH_PARENT height = WindowManager.LayoutParams.MATCH_PARENT @@ -98,6 +104,13 @@ class WiredChargingRippleController @Inject constructor( override fun onOverlayChanged() { updateRippleColor() } + + override fun onConfigChanged(newConfig: Configuration?) { + normalizedPortPosX = context.resources.getFloat( + R.dimen.physical_charger_port_location_normalized_x) + normalizedPortPosY = context.resources.getFloat( + R.dimen.physical_charger_port_location_normalized_y) + } } configurationController.addCallback(configurationChangedListener) @@ -134,23 +147,19 @@ class WiredChargingRippleController @Inject constructor( val width = displayMetrics.widthPixels val height = displayMetrics.heightPixels rippleView.radius = Integer.max(width, height).toFloat() - - // Always show the ripple from the charging cable location. - // Currently assuming the charging cable is at the bottom of the screen. - // TODO(shanh): Pull charging port location into configurations. rippleView.origin = when (RotationUtils.getRotation(context)) { RotationUtils.ROTATION_LANDSCAPE -> { - PointF(width.toFloat(), height / 2f) + PointF(width * normalizedPortPosY, height * (1 - normalizedPortPosX)) } RotationUtils.ROTATION_UPSIDE_DOWN -> { - PointF(width / 2f, 0f) + PointF(width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY)) } RotationUtils.ROTATION_SEASCAPE -> { - PointF(0f, height / 2f) + PointF(width * (1 - normalizedPortPosY), height * normalizedPortPosX) } else -> { // ROTATION_NONE - PointF(width / 2f, height.toFloat()) + PointF(width * normalizedPortPosX, height * normalizedPortPosY) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt index fb42c424f603..fad0e49b3637 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.collection +import android.app.Notification import android.app.NotificationManager.IMPORTANCE_HIGH import android.app.NotificationManager.IMPORTANCE_MIN import android.service.notification.NotificationListenerService.Ranking @@ -25,6 +26,7 @@ import com.android.systemui.statusbar.NotificationMediaManager import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger import com.android.systemui.statusbar.notification.NotificationFilter import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON @@ -33,11 +35,11 @@ import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVI import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT import com.android.systemui.statusbar.notification.stack.PriorityBucket -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.policy.HeadsUpManager import dagger.Lazy import java.util.Objects import javax.inject.Inject +import kotlin.Comparator private const val TAG = "NotifRankingManager" @@ -77,6 +79,9 @@ open class NotificationRankingManager @Inject constructor( val aIsFsn = a.isColorizedForegroundService() val bIsFsn = b.isColorizedForegroundService() + val aCall = a.isImportantCall() + val bCall = b.isImportantCall() + val aPersonType = a.getPeopleNotificationType() val bPersonType = b.getPeopleNotificationType() @@ -96,6 +101,7 @@ open class NotificationRankingManager @Inject constructor( // Provide consistent ranking with headsUpManager aHeadsUp -> headsUpManager.compare(a, b) aIsFsn != bIsFsn -> if (aIsFsn) -1 else 1 + aCall != bCall -> if (aCall) -1 else 1 usePeopleFiltering && aPersonType != bPersonType -> peopleNotificationIdentifier.compareTo(aPersonType, bPersonType) // Upsort current media notification. @@ -150,11 +156,12 @@ open class NotificationRankingManager @Inject constructor( @PriorityBucket private fun getBucketForEntry(entry: NotificationEntry): Int { + val isImportantCall = entry.isImportantCall() val isHeadsUp = entry.isRowHeadsUp val isMedia = entry.isImportantMedia() val isSystemMax = entry.isSystemMax() return when { - entry.isColorizedForegroundService() -> BUCKET_FOREGROUND_SERVICE + entry.isColorizedForegroundService() || isImportantCall -> BUCKET_FOREGROUND_SERVICE usePeopleFiltering && entry.isConversation() -> BUCKET_PEOPLE isHeadsUp || isMedia || isSystemMax || entry.isHighPriority() -> BUCKET_ALERTING else -> BUCKET_SILENT @@ -186,7 +193,7 @@ open class NotificationRankingManager @Inject constructor( } private fun NotificationEntry.isImportantMedia() = - key == mediaManager.mediaNotificationKey && ranking.importance > IMPORTANCE_MIN + key == mediaManager.mediaNotificationKey && importance > IMPORTANCE_MIN private fun NotificationEntry.isConversation() = getPeopleNotificationType() != TYPE_NON_PERSON @@ -204,6 +211,10 @@ private fun NotificationEntry.isSystemMax() = private fun StatusBarNotification.isSystemNotification() = "android" == packageName || "com.android.systemui" == packageName +private fun NotificationEntry.isImportantCall() = + sbn.notification.extras?.getString(Notification.EXTRA_TEMPLATE) == + "android.app.Notification\$CallStyle" && importance > IMPORTANCE_MIN + private fun NotificationEntry.isColorizedForegroundService() = sbn.notification.run { isForegroundService && isColorized && importance > IMPORTANCE_MIN } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index f8543f7b198d..b237f6f0a46e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -22,13 +22,11 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.RectF; import android.util.AttributeSet; import android.util.MathUtils; import android.view.MotionEvent; import android.view.View; -import android.view.ViewAnimationUtils; import android.view.accessibility.AccessibilityManager; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; @@ -50,9 +48,6 @@ import com.android.systemui.statusbar.notification.stack.StackStateAnimator; */ public abstract class ActivatableNotificationView extends ExpandableOutlineView { - private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220; - private static final int ACTIVATE_ANIMATION_LENGTH = 220; - /** * The amount of width, which is kept in the end when performing a disappear animation (also * the amount from which the horizontal appearing begins) @@ -97,8 +92,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private int mNormalRippleColor; private Gefingerpoken mTouchHandler; - private boolean mDimmed; - int mBgTint = NO_COLOR; /** @@ -115,7 +108,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private Interpolator mCurrentAlphaInterpolator; NotificationBackgroundView mBackgroundNormal; - private NotificationBackgroundView mBackgroundDimmed; private ObjectAnimator mBackgroundAnimator; private RectF mAppearAnimationRect = new RectF(); private float mAnimationTranslationY; @@ -129,13 +121,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private long mLastActionUpTime; private float mNormalBackgroundVisibilityAmount; - private float mDimmedBackgroundFadeInAmount = -1; private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { setNormalBackgroundVisibilityAmount(mBackgroundNormal.getAlpha()); - mDimmedBackgroundFadeInAmount = mBackgroundDimmed.getAlpha(); } }; private FakeShadowView mFakeShadow; @@ -145,18 +135,12 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private int mOverrideTint; private float mOverrideAmount; private boolean mShadowHidden; - /** - * Similar to mDimmed but is also true if it's not dimmable but should be - */ - private boolean mNeedsDimming; - private int mDimmedAlpha; private boolean mIsHeadsUpAnimation; private int mHeadsUpAddStartLocation; private float mHeadsUpLocation; private boolean mIsAppearing; private boolean mDismissed; private boolean mRefocusOnDismiss; - private OnDimmedListener mOnDimmedListener; private AccessibilityManager mAccessibilityManager; public ActivatableNotificationView(Context context, AttributeSet attrs) { @@ -176,8 +160,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView R.color.notification_ripple_tinted_color); mNormalRippleColor = mContext.getColor( R.color.notification_ripple_untinted_color); - mDimmedAlpha = Color.alpha(mContext.getColor( - R.color.notification_background_dimmed_color)); } private void initDimens() { @@ -206,21 +188,18 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundNormal = findViewById(R.id.backgroundNormal); mFakeShadow = findViewById(R.id.fake_shadow); mShadowHidden = mFakeShadow.getVisibility() != VISIBLE; - mBackgroundDimmed = findViewById(R.id.backgroundDimmed); initBackground(); - updateBackground(); updateBackgroundTint(); updateOutlineAlpha(); } /** - * Sets the custom backgrounds on {@link #mBackgroundNormal} and {@link #mBackgroundDimmed}. + * Sets the custom background on {@link #mBackgroundNormal} * This method can also be used to reload the backgrounds on both of those views, which can * be useful in a configuration change. */ protected void initBackground() { mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg); - mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim); } @@ -264,20 +243,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } @Override - public void drawableHotspotChanged(float x, float y) { - if (!mDimmed){ - mBackgroundNormal.drawableHotspotChanged(x, y); - } - } - - @Override protected void drawableStateChanged() { super.drawableStateChanged(); - if (mDimmed) { - mBackgroundDimmed.setState(getDrawableState()); - } else { - mBackgroundNormal.setState(getDrawableState()); - } + mBackgroundNormal.setState(getDrawableState()); } void setRippleAllowed(boolean allowed) { @@ -285,7 +253,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } void makeActive() { - startActivateAnimation(false /* reverse */); mActivated = true; if (mOnActivatedListener != null) { mOnActivatedListener.onActivated(this); @@ -296,106 +263,18 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView return mActivated; } - private void startActivateAnimation(final boolean reverse) { - if (!isAttachedToWindow()) { - return; - } - if (!isDimmable()) { - return; - } - int widthHalf = mBackgroundNormal.getWidth()/2; - int heightHalf = mBackgroundNormal.getActualHeight()/2; - float radius = (float) Math.sqrt(widthHalf*widthHalf + heightHalf*heightHalf); - Animator animator; - if (reverse) { - animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal, - widthHalf, heightHalf, radius, 0); - } else { - animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal, - widthHalf, heightHalf, 0, radius); - } - mBackgroundNormal.setVisibility(View.VISIBLE); - Interpolator interpolator; - Interpolator alphaInterpolator; - if (!reverse) { - interpolator = Interpolators.LINEAR_OUT_SLOW_IN; - alphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN; - } else { - interpolator = ACTIVATE_INVERSE_INTERPOLATOR; - alphaInterpolator = ACTIVATE_INVERSE_ALPHA_INTERPOLATOR; - } - animator.setInterpolator(interpolator); - animator.setDuration(ACTIVATE_ANIMATION_LENGTH); - if (reverse) { - mBackgroundNormal.setAlpha(1f); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - updateBackground(); - } - }); - animator.start(); - } else { - mBackgroundNormal.setAlpha(0.4f); - animator.start(); - } - mBackgroundNormal.animate() - .alpha(reverse ? 0f : 1f) - .setInterpolator(alphaInterpolator) - .setUpdateListener(animation -> { - float animatedFraction = animation.getAnimatedFraction(); - if (reverse) { - animatedFraction = 1.0f - animatedFraction; - } - setNormalBackgroundVisibilityAmount(animatedFraction); - }) - .setDuration(ACTIVATE_ANIMATION_LENGTH); - } - /** * Cancels the hotspot and makes the notification inactive. */ public void makeInactive(boolean animate) { if (mActivated) { mActivated = false; - if (mDimmed) { - if (animate) { - startActivateAnimation(true /* reverse */); - } else { - updateBackground(); - } - } } if (mOnActivatedListener != null) { mOnActivatedListener.onActivationReset(this); } } - public void setDimmed(boolean dimmed, boolean fade) { - mNeedsDimming = dimmed; - if (mOnDimmedListener != null) { - mOnDimmedListener.onSetDimmed(dimmed); - } - dimmed &= isDimmable(); - if (mDimmed != dimmed) { - mDimmed = dimmed; - resetBackgroundAlpha(); - if (fade) { - fadeDimmedBackground(); - } else { - updateBackground(); - } - } - } - - public boolean isDimmable() { - return true; - } - - public boolean isDimmed() { - return mDimmed; - } - private void updateOutlineAlpha() { float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED; alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount); @@ -448,7 +327,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView public void setDistanceToTopRoundness(float distanceToTopRoundness) { super.setDistanceToTopRoundness(distanceToTopRoundness); mBackgroundNormal.setDistanceToTopRoundness(distanceToTopRoundness); - mBackgroundDimmed.setDistanceToTopRoundness(distanceToTopRoundness); } /** Sets whether this view is the last notification in a section. */ @@ -457,7 +335,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView if (lastInSection != mLastInSection) { super.setLastInSection(lastInSection); mBackgroundNormal.setLastInSection(lastInSection); - mBackgroundDimmed.setLastInSection(lastInSection); } } @@ -467,7 +344,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView if (firstInSection != mFirstInSection) { super.setFirstInSection(firstInSection); mBackgroundNormal.setFirstInSection(firstInSection); - mBackgroundDimmed.setFirstInSection(firstInSection); } } @@ -486,13 +362,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mOverrideAmount = overrideAmount; int newColor = calculateBgColor(); setBackgroundTintColor(newColor); - if (!isDimmable() && mNeedsDimming) { - mBackgroundNormal.setDrawableAlpha((int) NotificationUtils.interpolate(255, - mDimmedAlpha, - overrideAmount)); - } else { - mBackgroundNormal.setDrawableAlpha(255); - } } protected void updateBackgroundTint() { @@ -504,7 +373,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundColorAnimator.cancel(); } int rippleColor = getRippleColor(); - mBackgroundDimmed.setRippleColor(rippleColor); mBackgroundNormal.setRippleColor(rippleColor); int color = calculateBgColor(); if (!animated) { @@ -537,110 +405,12 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView // We don't need to tint a normal notification color = 0; } - mBackgroundDimmed.setTint(color); mBackgroundNormal.setTint(color); } } - /** - * Fades the background when the dimmed state changes. - */ - private void fadeDimmedBackground() { - mBackgroundDimmed.animate().cancel(); - mBackgroundNormal.animate().cancel(); - if (mActivated) { - updateBackground(); - return; - } - if (!shouldHideBackground()) { - if (mDimmed) { - mBackgroundDimmed.setVisibility(View.VISIBLE); - } else { - mBackgroundNormal.setVisibility(View.VISIBLE); - } - } - float startAlpha = mDimmed ? 1f : 0; - float endAlpha = mDimmed ? 0 : 1f; - int duration = BACKGROUND_ANIMATION_LENGTH_MS; - // Check whether there is already a background animation running. - if (mBackgroundAnimator != null) { - startAlpha = (Float) mBackgroundAnimator.getAnimatedValue(); - duration = (int) mBackgroundAnimator.getCurrentPlayTime(); - mBackgroundAnimator.removeAllListeners(); - mBackgroundAnimator.cancel(); - if (duration <= 0) { - updateBackground(); - return; - } - } - mBackgroundNormal.setAlpha(startAlpha); - mBackgroundAnimator = - ObjectAnimator.ofFloat(mBackgroundNormal, View.ALPHA, startAlpha, endAlpha); - mBackgroundAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - mBackgroundAnimator.setDuration(duration); - mBackgroundAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - updateBackground(); - mBackgroundAnimator = null; - mDimmedBackgroundFadeInAmount = -1; - } - }); - mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater); - mBackgroundAnimator.start(); - } - - protected void updateBackgroundAlpha(float transformationAmount) { - float bgAlpha = isChildInGroup() && mDimmed ? transformationAmount : 1f; - if (mDimmedBackgroundFadeInAmount != -1) { - bgAlpha *= mDimmedBackgroundFadeInAmount; - } - mBackgroundDimmed.setAlpha(bgAlpha); - } - - protected void resetBackgroundAlpha() { - updateBackgroundAlpha(0f /* transformationAmount */); - } - - protected void updateBackground() { - cancelFadeAnimations(); - if (shouldHideBackground()) { - mBackgroundDimmed.setVisibility(INVISIBLE); - mBackgroundNormal.setVisibility(mActivated ? VISIBLE : INVISIBLE); - } else if (mDimmed) { - // When groups are animating to the expanded state from the lockscreen, show the - // normal background instead of the dimmed background. - final boolean dontShowDimmed = isGroupExpansionChanging() && isChildInGroup(); - mBackgroundDimmed.setVisibility(dontShowDimmed ? View.INVISIBLE : View.VISIBLE); - mBackgroundNormal.setVisibility((mActivated || dontShowDimmed) - ? View.VISIBLE - : View.INVISIBLE); - } else { - mBackgroundDimmed.setVisibility(View.INVISIBLE); - mBackgroundNormal.setVisibility(View.VISIBLE); - mBackgroundNormal.setAlpha(1f); - // make in inactive to avoid it sticking around active - makeInactive(false /* animate */); - } - setNormalBackgroundVisibilityAmount( - mBackgroundNormal.getVisibility() == View.VISIBLE ? 1.0f : 0.0f); - } - protected void updateBackgroundClipping() { mBackgroundNormal.setBottomAmountClips(!isChildInGroup()); - mBackgroundDimmed.setBottomAmountClips(!isChildInGroup()); - } - - protected boolean shouldHideBackground() { - return false; - } - - private void cancelFadeAnimations() { - if (mBackgroundAnimator != null) { - mBackgroundAnimator.cancel(); - } - mBackgroundDimmed.animate().cancel(); - mBackgroundNormal.animate().cancel(); } @Override @@ -654,21 +424,18 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView super.setActualHeight(actualHeight, notifyListeners); setPivotY(actualHeight / 2); mBackgroundNormal.setActualHeight(actualHeight); - mBackgroundDimmed.setActualHeight(actualHeight); } @Override public void setClipTopAmount(int clipTopAmount) { super.setClipTopAmount(clipTopAmount); mBackgroundNormal.setClipTopAmount(clipTopAmount); - mBackgroundDimmed.setClipTopAmount(clipTopAmount); } @Override public void setClipBottomAmount(int clipBottomAmount) { super.setClipBottomAmount(clipBottomAmount); mBackgroundNormal.setClipBottomAmount(clipBottomAmount); - mBackgroundDimmed.setClipBottomAmount(clipBottomAmount); } @Override @@ -891,13 +658,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } private void applyBackgroundRoundness(float topRadius, float bottomRadius) { - mBackgroundDimmed.setRadius(topRadius, bottomRadius); mBackgroundNormal.setRadius(topRadius, bottomRadius); } @Override protected void setBackgroundTop(int backgroundTop) { - mBackgroundDimmed.setBackgroundTop(backgroundTop); mBackgroundNormal.setBackgroundTop(backgroundTop); } @@ -1033,10 +798,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mTouchHandler = touchHandler; } - void setOnDimmedListener(OnDimmedListener onDimmedListener) { - mOnDimmedListener = onDimmedListener; - } - public void setAccessibilityManager(AccessibilityManager accessibilityManager) { mAccessibilityManager = accessibilityManager; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java index edd97afce2ca..0a63e19acc1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java @@ -85,7 +85,6 @@ public class ActivatableNotificationViewController mExpandableOutlineViewController.init(); mView.setOnTouchListener(mTouchHandler); mView.setTouchHandler(mTouchHandler); - mView.setOnDimmedListener(dimmed -> mNeedsDimming = dimmed); mView.setAccessibilityManager(mAccessibilityManager); } @@ -116,14 +115,8 @@ public class ActivatableNotificationViewController if (mAccessibilityManager.isTouchExplorationEnabled()) { return false; } - if (mNeedsDimming && mView.isInteractive()) { - if (mNeedsDimming && !mView.isDimmed()) { - // We're actually dimmed, but our content isn't dimmable, - // let's ensure we have a ripple - return false; - } - result = mNotificationTapHelper.onTouchEvent(ev, mView.getActualHeight()); - } else if (ev.getAction() == MotionEvent.ACTION_UP) { + + if (ev.getAction() == MotionEvent.ACTION_UP) { // If this is a false tap, capture the even so it doesn't result in a click. return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY); } @@ -132,17 +125,6 @@ public class ActivatableNotificationViewController @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mNeedsDimming && ev.getActionMasked() == MotionEvent.ACTION_DOWN - && mView.disallowSingleClick(ev) - && !mAccessibilityManager.isTouchExplorationEnabled()) { - if (!mView.isActive()) { - return true; - } else if (mFalsingManager.isFalseDoubleTap()) { - mBlockNextTouch = true; - mView.makeInactive(true /* animate */); - return true; - } - } return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index d21af1135e35..b5d2ea5b45f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -630,17 +630,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mSecureStateProvider = secureStateProvider; } - @Override - public boolean isDimmable() { - if (!getShowingLayout().isDimmable()) { - return false; - } - if (showingPulsing()) { - return false; - } - return super.isDimmable(); - } - private void updateLimits() { for (NotificationContentView l : mLayouts) { updateLimitsForView(l); @@ -853,7 +842,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mNotificationParent = isChildInGroup ? parent : null; mPrivateLayout.setIsChildInGroup(isChildInGroup); - resetBackgroundAlpha(); updateBackgroundForGroupState(); updateClickAndFocus(); if (mNotificationParent != null) { @@ -895,11 +883,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override - protected boolean shouldHideBackground() { - return super.shouldHideBackground() || mShowNoBackground; - } - - @Override public boolean isSummaryWithChildren() { return mIsSummaryWithChildren; } @@ -2873,7 +2856,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mShowNoBackground = false; } updateOutline(); - updateBackground(); } public int getPositionOfChild(ExpandableNotificationRow childRow) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index a90e76bf1515..884ad8d5cee4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -693,7 +693,6 @@ public class NotificationContentView extends FrameLayout { endColor = NotificationUtils.interpolateColors(startColor, endColor, transformationAmount); } - mContainingNotification.updateBackgroundAlpha(transformationAmount); mContainingNotification.setContentBackground(endColor, false, this); } @@ -868,7 +867,6 @@ public class NotificationContentView extends FrameLayout { public void updateBackgroundColor(boolean animate) { int customBackgroundColor = getBackgroundColor(mVisibleType); - mContainingNotification.resetBackgroundAlpha(); mContainingNotification.setContentBackground(customBackgroundColor, animate, this); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 4fc49ed26f64..36a370c71216 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -780,7 +780,7 @@ public class NotificationStackScrollLayoutController { return mView.isLayoutRtl(); } - public float getLeft() { + public int getLeft() { return mView.getLeft(); } 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 6cacec71db39..e3c5546d18da 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 @@ -33,9 +33,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.FooterView; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * The Algorithm of the {@link com.android.systemui.statusbar.notification.stack @@ -255,16 +253,16 @@ public class StackScrollAlgorithm { } } - state.firstViewInShelf = null; - // Save y, sectionStart, sectionEnd from when shade is fully expanded. - // Consider updating these states in updateContentView instead so that we don't have to - // recalculate in every frame. + // Save (height of view before shelf, index of first view in shelf) from when shade is fully + // expanded. Consider updating these states in updateContentView instead so that we don't + // have to recalculate in every frame. float currentY = -scrollY; - int sectionStartIndex = 0; - int sectionEndIndex = 0; + float previousY = 0; + state.firstViewInShelf = null; + state.viewHeightBeforeShelf = -1; for (int i = 0; i < state.visibleChildren.size(); i++) { final ExpandableView view = state.visibleChildren.get(i); - // Add space between sections. + final boolean applyGapHeight = childNeedsGapHeight( ambientState.getSectionProvider(), i, view, getPreviousView(i, state)); @@ -273,88 +271,27 @@ public class StackScrollAlgorithm { } if (ambientState.getShelf() != null) { - // Save index of first view in the shelf final float shelfStart = ambientState.getStackEndHeight() - ambientState.getShelf().getIntrinsicHeight(); if (currentY >= shelfStart && !(view instanceof FooterView) && state.firstViewInShelf == null) { state.firstViewInShelf = view; + // There might be a section gap right before the shelf. + // Limit the height of the view before the shelf so that it does not include + // a gap and become taller than it normally is. + state.viewHeightBeforeShelf = Math.min(getMaxAllowedChildHeight(view), + ambientState.getStackEndHeight() + - ambientState.getShelf().getIntrinsicHeight() + - mPaddingBetweenElements + - previousY); } } - - // Record y position when fully expanded - ExpansionData expansionData = new ExpansionData(); - expansionData.fullyExpandedY = currentY; - state.expansionData.put(view, expansionData); - - if (ambientState.getSectionProvider() - .beginsSection(view, getPreviousView(i, state))) { - - // Save section start/end for views in the section before this new section - ExpandableView sectionStartView = state.visibleChildren.get(sectionStartIndex); - final float sectionStart = - state.expansionData.get(sectionStartView).fullyExpandedY; - - ExpandableView sectionEndView = state.visibleChildren.get(sectionEndIndex); - float sectionEnd = state.expansionData.get(sectionEndView).fullyExpandedY - + sectionEndView.getIntrinsicHeight(); - - if (ambientState.getShelf() != null) { - // If we show the shelf, trim section end to shelf start - // This means section end > start for views in the shelf - final float shelfStart = ambientState.getStackEndHeight() - - ambientState.getShelf().getIntrinsicHeight(); - if (state.firstViewInShelf != null && sectionEnd > shelfStart) { - sectionEnd = shelfStart; - } - } - - // Update section bounds of every view in the previous section - // Consider using shared SectionInfo for views in same section to avoid looping back - for (int j = sectionStartIndex; j < i; j++) { - ExpandableView sectionView = state.visibleChildren.get(j); - ExpansionData viewExpansionData = - state.expansionData.get(sectionView); - viewExpansionData.sectionStart = sectionStart; - viewExpansionData.sectionEnd = sectionEnd; - state.expansionData.put(sectionView, viewExpansionData); - } - sectionStartIndex = i; - - if (view instanceof FooterView) { - // Also record section bounds for FooterView (same as its own) - // because it is the last view and we won't get to this point again - // after the loop ends - ExpansionData footerExpansionData = state.expansionData.get(view); - footerExpansionData.sectionStart = expansionData.fullyExpandedY; - footerExpansionData.sectionEnd = expansionData.fullyExpandedY - + view.getIntrinsicHeight(); - state.expansionData.put(view, footerExpansionData); - } - } - sectionEndIndex = i; + previousY = currentY; currentY = currentY + getMaxAllowedChildHeight(view) + mPaddingBetweenElements; } - - // Which view starts the section of the view right before the shelf? - // Save it for later when we clip views in that section to shelf start. - state.firstViewInOverflowSection = null; - if (state.firstViewInShelf != null) { - ExpandableView nextView = null; - final int startIndex = state.visibleChildren.indexOf(state.firstViewInShelf); - for (int i = startIndex - 1; i >= 0; i--) { - ExpandableView view = state.visibleChildren.get(i); - if (nextView != null && ambientState.getSectionProvider() - .beginsSection(nextView, view)) { - break; - } - nextView = view; - } - state.firstViewInOverflowSection = nextView; - } } private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex, @@ -394,6 +331,26 @@ public class StackScrollAlgorithm { } } + /** + * @return Fraction to apply to view height and gap between views. + * Does not include shelf height even if shelf is showing. + */ + private float getExpansionFractionWithoutShelf( + StackScrollAlgorithmState algorithmState, + AmbientState ambientState) { + + final boolean isShowingShelf = ambientState.getShelf() != null + && algorithmState.firstViewInShelf != null; + + final float stackHeight = ambientState.getStackHeight() + - (isShowingShelf ? ambientState.getShelf().getIntrinsicHeight() : 0f); + + float stackEndHeight = ambientState.getStackEndHeight() + - (isShowingShelf ? ambientState.getShelf().getIntrinsicHeight() : 0f); + + return stackHeight / stackEndHeight; + } + // TODO(b/172289889) polish shade open from HUN /** * Populates the {@link ExpandableViewState} for a single child. @@ -426,22 +383,8 @@ public class StackScrollAlgorithm { viewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation(); } - // TODO(b/172289889) move sectionFraction and showSection to initAlgorithmState - // Get fraction of section showing, and later apply it to view height and gaps between views - float sectionFraction = 1f; - boolean showSection = true; - - if (!ambientState.isOnKeyguard() - && !ambientState.isPulseExpanding() - && ambientState.isExpansionChanging()) { - - final ExpansionData expansionData = algorithmState.expansionData.get(view); - final float sectionHeight = expansionData.sectionEnd - expansionData.sectionStart; - sectionFraction = MathUtils.constrain( - (ambientState.getStackHeight() - expansionData.sectionStart) / sectionHeight, - 0f, 1f); - showSection = expansionData.sectionStart < ambientState.getStackHeight(); - } + final float expansionFraction = getExpansionFractionWithoutShelf( + algorithmState, ambientState); // Add gap between sections. final boolean applyGapHeight = @@ -449,46 +392,58 @@ public class StackScrollAlgorithm { ambientState.getSectionProvider(), i, view, getPreviousView(i, algorithmState)); if (applyGapHeight) { - currentYPosition += sectionFraction * mGapHeight; + currentYPosition += expansionFraction * mGapHeight; } viewState.yTranslation = currentYPosition; - if (view instanceof SectionHeaderView) { // Add padding before sections for overscroll effect. - viewState.yTranslation += ambientState.getSectionPadding(); + viewState.yTranslation += expansionFraction * ambientState.getSectionPadding(); } - if (view != ambientState.getTrackedHeadsUpRow()) { + if (view instanceof FooterView) { + viewState.yTranslation = Math.min(viewState.yTranslation, + ambientState.getStackHeight()); + // Hide footer if shelf is showing + viewState.hidden = algorithmState.firstViewInShelf != null; + } else if (view != ambientState.getTrackedHeadsUpRow()) { if (ambientState.isExpansionChanging()) { - viewState.hidden = !showSection; + // Show all views. Views below the shelf will later be clipped (essentially hidden) + // in NotificationShelf. + viewState.hidden = false; viewState.inShelf = algorithmState.firstViewInShelf != null && i >= algorithmState.visibleChildren.indexOf( - algorithmState.firstViewInShelf) - && !(view instanceof FooterView); + algorithmState.firstViewInShelf); } else if (ambientState.getShelf() != null) { // When pulsing (incoming notification on AOD), innerHeight is 0; clamp all // to shelf start, thereby hiding all notifications (except the first one, which we // later unhide in updatePulsingState) final int shelfStart = ambientState.getInnerHeight() - ambientState.getShelf().getIntrinsicHeight(); - if (!(view instanceof FooterView)) { - viewState.yTranslation = Math.min(viewState.yTranslation, shelfStart); - } + viewState.yTranslation = Math.min(viewState.yTranslation, shelfStart); if (viewState.yTranslation >= shelfStart) { viewState.hidden = !view.isExpandAnimationRunning() - && !view.hasExpandingChild() - && !(view instanceof FooterView); + && !view.hasExpandingChild(); viewState.inShelf = true; // Notifications in the shelf cannot be visible HUNs. viewState.headsUpIsVisible = false; } } - viewState.height = (int) MathUtils.lerp( - 0, getMaxAllowedChildHeight(view), sectionFraction); + + // Clip height of view right before shelf. + float maxViewHeight = getMaxAllowedChildHeight(view); + if (ambientState.isExpansionChanging() + && algorithmState.viewHeightBeforeShelf != -1) { + final int indexOfFirstViewInShelf = algorithmState.visibleChildren.indexOf( + algorithmState.firstViewInShelf); + if (i == indexOfFirstViewInShelf - 1) { + maxViewHeight = algorithmState.viewHeightBeforeShelf; + } + } + viewState.height = (int) MathUtils.lerp(0, maxViewHeight, expansionFraction); } - currentYPosition += viewState.height + sectionFraction * mPaddingBetweenElements; + currentYPosition += viewState.height + expansionFraction * mPaddingBetweenElements; setLocation(view.getViewState(), currentYPosition, i); viewState.yTranslation += ambientState.getStackY(); return currentYPosition; @@ -743,35 +698,6 @@ public class StackScrollAlgorithm { this.mIsExpanded = isExpanded; } - /** - * Data used to layout views while shade expansion changes. - */ - public class ExpansionData { - - /** - * Y position of top of first view in section. - */ - public float sectionStart; - - /** - * Y position of bottom of last view in section. - */ - public float sectionEnd; - - /** - * Y position of view when shade is fully expanded. - * Does not include distance between top notifications panel and top of screen. - */ - public float fullyExpandedY; - - /** - * Whether this notification is in the same section as the notification right before the - * shelf. Used to determine which notification should be clipped to shelf start while - * shade expansion changes. - */ - public boolean inOverflowingSection; - } - public class StackScrollAlgorithmState { /** @@ -785,16 +711,9 @@ public class StackScrollAlgorithm { public ExpandableView firstViewInShelf; /** - * First view in section overflowing into shelf while shade expansion changes. - */ - public ExpandableView firstViewInOverflowSection; - - /** - * Map of view to ExpansionData used for layout during shade expansion. - * Use view instead of index as key, because visibleChildren indices do not match the ones - * used in the shelf. + * Height of view right before the shelf. */ - public Map<ExpandableView, ExpansionData> expansionData = new HashMap<>(); + public float viewHeightBeforeShelf; /** * The children from the host view which are not gone. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index a4ee9eee7151..539909491261 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -411,13 +411,22 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp Trace.endSection(); break; case MODE_UNLOCK_COLLAPSING: + Trace.beginSection("MODE_UNLOCK_COLLAPSING"); + if (!wasDeviceInteractive) { + mPendingShowBouncer = true; + } else { + showBouncer(); + mKeyguardViewController.notifyKeyguardAuthenticated( + false /* strongAuth */); + } + Trace.endSection(); + break; case MODE_SHOW_BOUNCER: - Trace.beginSection("MODE_UNLOCK_COLLAPSING or MODE_SHOW_BOUNCER"); + Trace.beginSection("MODE_SHOW_BOUNCER"); if (!wasDeviceInteractive) { mPendingShowBouncer = true; } else { showBouncer(); - mKeyguardViewController.notifyKeyguardAuthenticated(false /* strongAuth */); } Trace.endSection(); break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index 48c7b89f50c1..76657ad5ab07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS; import static android.app.StatusBarManager.DISABLE_CLOCK; import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS; +import static android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP; import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO; import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN; @@ -92,6 +93,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private OngoingCallController mOngoingCallController; private final SystemStatusAnimationScheduler mAnimationScheduler; private final PrivacyDotViewController mDotViewController; + private NotificationIconAreaController mNotificationIconAreaController; private List<String> mBlockedIcons = new ArrayList<>(); @@ -106,13 +108,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onOngoingCallStarted(boolean animate) { disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate); - animateShow(mOngoingCallChip, animate); } @Override public void onOngoingCallEnded(boolean animate) { disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate); - animateHiddenState(mOngoingCallChip, View.GONE, animate); } }; @@ -120,11 +120,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue public CollapsedStatusBarFragment( OngoingCallController ongoingCallController, SystemStatusAnimationScheduler animationScheduler, - PrivacyDotViewController dotViewController + PrivacyDotViewController dotViewController, + NotificationIconAreaController notificationIconAreaController ) { mOngoingCallController = ongoingCallController; mAnimationScheduler = animationScheduler; mDotViewController = dotViewController; + mNotificationIconAreaController = notificationIconAreaController; } @Override @@ -168,6 +170,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue showClock(false); initEmergencyCryptkeeperText(); initOperatorName(); + initNotificationIconArea(); mAnimationScheduler.addCallback(this); } @@ -204,11 +207,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } } - public void initNotificationIconArea(NotificationIconAreaController - notificationIconAreaController) { + /** Initializes views related to the notification icon area. */ + public void initNotificationIconArea() { ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area); mNotificationIconAreaInner = - notificationIconAreaController.getNotificationInnerAreaView(); + mNotificationIconAreaController.getNotificationInnerAreaView(); if (mNotificationIconAreaInner.getParent() != null) { ((ViewGroup) mNotificationIconAreaInner.getParent()) .removeView(mNotificationIconAreaInner); @@ -216,15 +219,15 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue notificationIconArea.addView(mNotificationIconAreaInner); ViewGroup statusBarCenteredIconArea = mStatusBar.findViewById(R.id.centered_icon_area); - mCenteredIconArea = notificationIconAreaController.getCenteredNotificationAreaView(); + mCenteredIconArea = mNotificationIconAreaController.getCenteredNotificationAreaView(); if (mCenteredIconArea.getParent() != null) { ((ViewGroup) mCenteredIconArea.getParent()) .removeView(mCenteredIconArea); } statusBarCenteredIconArea.addView(mCenteredIconArea); - // Default to showing until we know otherwise. - showNotificationIconArea(false); + // #disable should have already been called, so use the disable values to set visibility. + updateNotificationIconAreaAndCallChip(mDisabled1, false); } @Override @@ -249,13 +252,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue showOperatorName(animate); } } - if ((diff1 & DISABLE_NOTIFICATION_ICONS) != 0) { - if ((state1 & DISABLE_NOTIFICATION_ICONS) != 0) { - hideNotificationIconArea(animate); - } else { - showNotificationIconArea(animate); - } + + // The ongoing call chip and notification icon visibilities are intertwined, so update both + // if either change. + if (((diff1 & DISABLE_ONGOING_CALL_CHIP) != 0) + || ((diff1 & DISABLE_NOTIFICATION_ICONS) != 0)) { + updateNotificationIconAreaAndCallChip(state1, animate); } + // The clock may have already been hidden, but we might want to shift its // visibility to GONE from INVISIBLE or vice versa if ((diff1 & DISABLE_CLOCK) != 0 || mClockView.getVisibility() != clockHiddenMode()) { @@ -273,10 +277,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue state |= DISABLE_CLOCK; } - if (mOngoingCallController.hasOngoingCall()) { - state |= DISABLE_NOTIFICATION_ICONS; - } - if (!mKeyguardStateController.isLaunchTransitionFadingAway() && !mKeyguardStateController.isKeyguardFadingAway() && shouldHideNotificationIcons() @@ -304,9 +304,40 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO; } + if (mOngoingCallController.hasOngoingCall()) { + state &= ~DISABLE_ONGOING_CALL_CHIP; + } else { + state |= DISABLE_ONGOING_CALL_CHIP; + } + return state; } + /** + * Updates the visibility of the notification icon area and ongoing call chip based on disabled1 + * state. + */ + private void updateNotificationIconAreaAndCallChip(int state1, boolean animate) { + boolean disableNotifications = (state1 & DISABLE_NOTIFICATION_ICONS) != 0; + boolean hasOngoingCall = (state1 & DISABLE_ONGOING_CALL_CHIP) == 0; + + // Hide notifications if the disable flag is set or we have an ongoing call. + if (disableNotifications || hasOngoingCall) { + hideNotificationIconArea(animate); + } else { + showNotificationIconArea(animate); + } + + // Show the ongoing call chip only if there is an ongoing call *and* notification icons + // are allowed. (The ongoing call chip occupies the same area as the notification icons, + // so if the icons are disabled then the call chip should be, too.) + if (hasOngoingCall && !disableNotifications) { + showOngoingCallChip(animate); + } else { + hideOngoingCallChip(animate); + } + } + private boolean shouldHideNotificationIcons() { if (!mStatusBar.isClosed() && mStatusBarComponent.hideStatusBarIconsWhenExpanded()) { return true; @@ -336,6 +367,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue animateShow(mClockView, animate); } + /** Hides the ongoing call chip. */ + public void hideOngoingCallChip(boolean animate) { + animateHiddenState(mOngoingCallChip, View.GONE, animate); + } + + /** Displays the ongoing call chip. */ + public void showOngoingCallChip(boolean animate) { + animateShow(mOngoingCallChip, animate); + } + /** * If panel is expanded/expanding it usually means QS shade is opening, so * don't set the clock GONE otherwise it'll mess up the animation. @@ -457,7 +498,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onDozingChanged(boolean isDozing) { - disable(getContext().getDisplayId(), mDisabled1, mDisabled1, false /* animate */); + disable(getContext().getDisplayId(), mDisabled1, mDisabled2, false /* animate */); } @Override 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 8cef23f21383..cabfbca26dfb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -935,7 +935,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private void onWalletClick(View v) { // More coming here; need to inform the user about how to proceed - mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY); + if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + return; + } if (mHasCard) { Intent intent = new Intent(mContext, WalletActivity.class) 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 961699e9e600..9449836999f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -53,6 +53,7 @@ import android.os.Bundle; import android.os.PowerManager; import android.os.SystemClock; import android.os.UserManager; +import android.os.VibrationEffect; import android.service.quickaccesswallet.QuickAccessWalletClient; import android.util.Log; import android.util.MathUtils; @@ -101,6 +102,7 @@ import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.FalsingManager.FalsingTapListener; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -206,6 +208,7 @@ public class NotificationPanelViewController extends PanelViewController { private final ExpansionCallback mExpansionCallback = new ExpansionCallback(); private final BiometricUnlockController mBiometricUnlockController; private final NotificationPanelView mView; + private final VibratorHelper mVibratorHelper; private final MetricsLogger mMetricsLogger; private final ActivityManager mActivityManager; private final ConfigurationController mConfigurationController; @@ -515,6 +518,7 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mDelayShowingKeyguardStatusBar; private boolean mAnimatingQS; + private final Rect mKeyguardStatusAreaClipBounds = new Rect(); private int mOldLayoutDirection; private NotificationShelfController mNotificationShelfController; @@ -522,6 +526,7 @@ public class NotificationPanelViewController extends PanelViewController { private final Executor mUiExecutor; private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; + private int mScrimCornerRadius; private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { @Override @@ -543,6 +548,14 @@ public class NotificationPanelViewController extends PanelViewController { } }; + private final FalsingTapListener mFalsingTapListener = new FalsingTapListener() { + @Override + public void onDoubleTapRequired() { + showTransientIndication(R.string.notification_tap_again); + mVibratorHelper.vibrate(VibrationEffect.EFFECT_STRENGTH_MEDIUM); + } + }; + @Inject public NotificationPanelViewController(NotificationPanelView view, @Main Resources resources, @@ -590,6 +603,7 @@ public class NotificationPanelViewController extends PanelViewController { statusBarKeyguardViewManager, latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager, ambientState); mView = view; + mVibratorHelper = vibratorHelper; mMetricsLogger = metricsLogger; mActivityManager = activityManager; mConfigurationController = configurationController; @@ -628,6 +642,7 @@ public class NotificationPanelViewController extends PanelViewController { mDozeParameters = dozeParameters; mBiometricUnlockController = biometricUnlockController; mScrimController = scrimController; + mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade); mUserManager = userManager; mMediaDataManager = mediaDataManager; mQuickAccessWalletClient = quickAccessWalletClient; @@ -852,10 +867,16 @@ public class NotificationPanelViewController extends PanelViewController { public void updateResources() { mSplitShadeNotificationsTopPadding = mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade); + mScrimCornerRadius = + mResources.getDimensionPixelSize(R.dimen.notification_scrim_corner_radius); int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width); int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width); mShouldUseSplitNotificationShade = Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources); + mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade); + if (mQs != null) { + mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade); + } // To change the constraints at runtime, all children of the ConstraintLayout must have ids ensureAllViewsHaveIds(mNotificationContainerParent); ConstraintSet constraintSet = new ConstraintSet(); @@ -2003,15 +2024,23 @@ public class NotificationPanelViewController extends PanelViewController { mDepthController.setQsPanelExpansion(qsExpansionFraction); } - private void setNotificationBounds(float qsExpansionFraction, int qsPanelBottomY) { - float top = 0; - float bottom = 0; - float left = 0; - float right = 0; - if (qsPanelBottomY > 0) { - // notification shade is expanding/expanded + /** + * Updates scrim bounds, QS clipping, and KSV clipping as well based on the bounds of the shade + * and QS state. + * + * @param qsFraction QS expansion fraction, from getQsExpansionFraction(). + * @param qsPanelBottomY Absolute y position of the bottom of QS as it's being pulled. + */ + private void setNotificationBounds(float qsFraction, int qsPanelBottomY) { + int top = 0; + int bottom = 0; + int left = 0; + int right = 0; + boolean visible = qsFraction > 0 || qsPanelBottomY > 0; + if (visible || !mShouldUseSplitNotificationShade) { if (!mShouldUseSplitNotificationShade) { - top = qsPanelBottomY; + float notificationTop = mAmbientState.getStackY() - mQsNotificationTopPadding; + top = (int) Math.min(qsPanelBottomY, notificationTop); bottom = getView().getBottom(); left = getView().getLeft(); right = getView().getRight(); @@ -2022,6 +2051,17 @@ public class NotificationPanelViewController extends PanelViewController { right = mNotificationStackScrollLayoutController.getRight(); } } + + if (!mShouldUseSplitNotificationShade) { + // Fancy clipping for quick settings + if (mQs != null) { + mQs.setFancyClipping(top, bottom, mScrimCornerRadius, visible); + } + // The padding on this area is large enough that we can use a cheaper clipping strategy + mKeyguardStatusAreaClipBounds.set(left, top, right, bottom); + mKeyguardStatusViewController.setClipBounds(visible + ? mKeyguardStatusAreaClipBounds : null); + } mScrimController.setNotificationsBounds(left, top, right, bottom); } @@ -2493,6 +2533,10 @@ public class NotificationPanelViewController extends PanelViewController { float appearAmount = mNotificationStackScrollLayoutController .calculateAppearFraction(mExpandedHeight); float startHeight = -mQsExpansionHeight; + if (!mShouldUseSplitNotificationShade && mBarState == StatusBarState.SHADE) { + // Small parallax as we pull down and clip QS + startHeight = -mQsExpansionHeight * 0.2f; + } if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard() && mNotificationStackScrollLayoutController.isPulseExpanding()) { if (!mPulseExpansionHandler.isExpanding() @@ -3128,8 +3172,10 @@ public class NotificationPanelViewController extends PanelViewController { mQs.setPanelView(mHeightListener); mQs.setExpandClickListener(mOnClickListener); mQs.setHeaderClickable(mQsExpansionEnabled); + mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade); updateQSPulseExpansion(); mQs.setOverscrolling(mStackScrollerOverscrolling); + mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade); // recompute internal state when qspanel height changes mQs.getView().addOnLayoutChangeListener( @@ -3925,7 +3971,10 @@ public class NotificationPanelViewController extends PanelViewController { // animate out // the top of QS if (!mQsExpanded) { - mQs.animateHeaderSlidingOut(); + // TODO(b/185683835) Nicer clipping when using new spacial model + if (mShouldUseSplitNotificationShade) { + mQs.animateHeaderSlidingOut(); + } } } else { mKeyguardStatusBar.setAlpha(1f); @@ -3978,6 +4027,7 @@ public class NotificationPanelViewController extends PanelViewController { // window, so // force a call to onThemeChanged mConfigurationListener.onThemeChanged(); + mFalsingManager.addTapListener(mFalsingTapListener); } @Override @@ -3986,6 +4036,7 @@ public class NotificationPanelViewController extends PanelViewController { mStatusBarStateController.removeCallback(mStatusBarStateListener); mConfigurationController.removeCallback(mConfigurationListener); mUpdateMonitor.removeCallback(mKeyguardUpdateCallback); + mFalsingManager.removeTapListener(mFalsingTapListener); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 064086a828cb..5a2a6f21f94a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -147,9 +147,6 @@ public abstract class PanelViewController { private float mInitialTouchX; private boolean mTouchDisabled; - // AmbientState will never be null since it provides an @Inject constructor for Dagger to call. - private AmbientState mAmbientState; - /** * Whether or not the PanelView can be expanded or collapsed with a drag. */ @@ -172,6 +169,7 @@ public abstract class PanelViewController { protected final Resources mResources; protected final KeyguardStateController mKeyguardStateController; protected final SysuiStatusBarStateController mStatusBarStateController; + protected final AmbientState mAmbientState; protected void onExpandingFinished() { mBar.onExpandingFinished(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 5e9c758da07a..0d96ead964f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -48,8 +48,8 @@ import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; +import com.android.systemui.scrim.ScrimView; import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.stack.ViewState; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -96,6 +96,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump * When at least 1 scrim is fully opaque (alpha set to 1.) */ public static final int OPAQUE = 2; + private boolean mClipsQsScrim; @IntDef(prefix = {"VISIBILITY_"}, value = { TRANSPARENT, @@ -165,6 +166,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump // Assuming the shade is expanded during initialization private float mExpansionFraction = 1f; private float mQsExpansion; + private boolean mQsBottomVisible; private boolean mDarkenWhileDragging; private boolean mExpansionAffectsAlpha = true; @@ -183,6 +185,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private int mInFrontTint; private int mBehindTint; + private int mNotificationsTint; private int mBubbleTint; private boolean mWallpaperVisibilityTimedOut; @@ -265,6 +268,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mScrimForBubble = scrimForBubble; updateThemeColors(); + behindScrim.enableBottomEdgeConcave(mClipsQsScrim); mNotificationsScrim.enableRoundedCorners(); if (mScrimBehindChangeRunnable != null) { @@ -331,14 +335,17 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mInFrontTint = state.getFrontTint(); mBehindTint = state.getBehindTint(); + mNotificationsTint = state.getNotifTint(); mBubbleTint = state.getBubbleTint(); mInFrontAlpha = state.getFrontAlpha(); mBehindAlpha = state.getBehindAlpha(); mBubbleAlpha = state.getBubbleAlpha(); - if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha)) { + mNotificationsAlpha = state.getNotifAlpha(); + if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) { throw new IllegalStateException("Scrim opacity is NaN for state: " + state + ", front: " - + mInFrontAlpha + ", back: " + mBehindAlpha); + + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: " + + mNotificationsAlpha); } applyExpansionToAlpha(); @@ -397,7 +404,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump scheduleUpdate(); } - dispatchScrimState(mScrimBehind.getViewAlpha()); + dispatchBackScrimState(mScrimBehind.getViewAlpha()); } private boolean shouldFadeAwayWallpaper() { @@ -491,21 +498,25 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump */ public void setNotificationsBounds(float left, float top, float right, float bottom) { mNotificationsScrim.setDrawableBounds(left, top, right, bottom); + if (mClipsQsScrim) { + mScrimBehind.setBottomEdgePosition((int) top); + } } /** * Current state of the QuickSettings when pulling it from the top. * * @param expansionFraction From 0 to 1 where 0 means collapsed and 1 expanded. - * @param qsPanelBottomY absolute Y position of qs panel bottom + * @param qsPanelBottomY Absolute Y position of qs panel bottom */ public void setQsPosition(float expansionFraction, int qsPanelBottomY) { if (isNaN(expansionFraction)) { return; } - updateNotificationsScrimAlpha(expansionFraction, qsPanelBottomY); - if (mQsExpansion != expansionFraction) { + boolean qsBottomVisible = qsPanelBottomY > 0; + if (mQsExpansion != expansionFraction || mQsBottomVisible != qsBottomVisible) { mQsExpansion = expansionFraction; + mQsBottomVisible = qsBottomVisible; boolean relevantState = (mState == ScrimState.SHADE_LOCKED || mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING @@ -517,31 +528,36 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } } - private void updateNotificationsScrimAlpha(float qsExpansion, int qsPanelBottomY) { - float newAlpha = 0; - if (qsPanelBottomY > 0) { - float interpolator = 0; - if (mState == ScrimState.UNLOCKED || mState == ScrimState.SHADE_LOCKED) { - interpolator = getInterpolatedFraction(); - } else { - interpolator = qsExpansion; - } - newAlpha = MathUtils.lerp(0, 1, interpolator); + /** + * If QS and notification scrims should not overlap, and should be clipped to each other's + * bounds instead. + */ + public void setClipsQsScrim(boolean clipScrim) { + if (clipScrim == mClipsQsScrim) { + return; } - if (newAlpha != mNotificationsAlpha) { - mNotificationsAlpha = newAlpha; - // update alpha without animating - mNotificationsScrim.setViewAlpha(newAlpha); + mClipsQsScrim = clipScrim; + for (ScrimState state : ScrimState.values()) { + state.setClipQsScrim(mClipsQsScrim); + } + if (mScrimBehind != null) { + mScrimBehind.enableBottomEdgeConcave(mClipsQsScrim); } } + @VisibleForTesting + public boolean getClipQsScrim() { + return mClipsQsScrim; + } + private void setOrAdaptCurrentAnimation(@Nullable View scrim) { if (scrim == null) { return; } float alpha = getCurrentScrimAlpha(scrim); - if (isAnimating(scrim)) { + boolean qsScrimPullingDown = scrim == mScrimBehind && mQsBottomVisible; + if (isAnimating(scrim) && !qsScrimPullingDown) { // Adapt current animation. ValueAnimator previousAnimator = (ValueAnimator) scrim.getTag(TAG_KEY_ANIM); float previousEndValue = (Float) scrim.getTag(TAG_END_ALPHA); @@ -562,7 +578,19 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump return; } - if (mState == ScrimState.UNLOCKED || mState == ScrimState.BUBBLE_EXPANDED) { + if (mState == ScrimState.UNLOCKED) { + // Darken scrim as you pull down the shade when unlocked + float behindFraction = getInterpolatedFraction(); + behindFraction = (float) Math.pow(behindFraction, 0.8f); + if (mClipsQsScrim) { + mBehindAlpha = 1; + mNotificationsAlpha = behindFraction * mDefaultScrimAlpha; + } else { + mBehindAlpha = behindFraction * mDefaultScrimAlpha; + mNotificationsAlpha = mBehindAlpha; + } + mInFrontAlpha = 0; + } else if (mState == ScrimState.BUBBLE_EXPANDED) { // Darken scrim as you pull down the shade when unlocked float behindFraction = getInterpolatedFraction(); behindFraction = (float) Math.pow(behindFraction, 0.8f); @@ -573,27 +601,45 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump // Either darken of make the scrim transparent when you // pull down the shade float interpolatedFract = getInterpolatedFraction(); - float alphaBehind = mState.getBehindAlpha(); + float stateBehind = mClipsQsScrim ? mState.getNotifAlpha() : mState.getBehindAlpha(); + float backAlpha; if (mDarkenWhileDragging) { - mBehindAlpha = MathUtils.lerp(mDefaultScrimAlpha, alphaBehind, + backAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind, interpolatedFract); - mInFrontAlpha = mState.getFrontAlpha(); } else { - mBehindAlpha = MathUtils.lerp(0 /* start */, alphaBehind, + backAlpha = MathUtils.lerp(0 /* start */, stateBehind, interpolatedFract); - mInFrontAlpha = mState.getFrontAlpha(); } - mBehindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(), - mState.getBehindTint(), interpolatedFract); + mInFrontAlpha = mState.getFrontAlpha(); + int backTint; + if (mClipsQsScrim) { + backTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(), + mState.getNotifTint(), interpolatedFract); + } else { + backTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(), + mState.getBehindTint(), interpolatedFract); + } if (mQsExpansion > 0) { - mBehindAlpha = MathUtils.lerp(mBehindAlpha, mDefaultScrimAlpha, mQsExpansion); - mBehindTint = ColorUtils.blendARGB(mBehindTint, - ScrimState.SHADE_LOCKED.getBehindTint(), mQsExpansion); + backAlpha = MathUtils.lerp(backAlpha, mDefaultScrimAlpha, mQsExpansion); + int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint() + : ScrimState.SHADE_LOCKED.getBehindTint(); + backTint = ColorUtils.blendARGB(backTint, stateTint, mQsExpansion); + } + if (mClipsQsScrim) { + mNotificationsAlpha = backAlpha; + mNotificationsTint = backTint; + mBehindAlpha = 1; + mBehindTint = Color.BLACK; + } else { + mBehindAlpha = backAlpha; + mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion); + mBehindTint = backTint; } } - if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha)) { + if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) { throw new IllegalStateException("Scrim opacity is NaN for state: " + mState - + ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha); + + ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: " + + mNotificationsAlpha); } } @@ -606,7 +652,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump setOrAdaptCurrentAnimation(mNotificationsScrim); setOrAdaptCurrentAnimation(mScrimInFront); setOrAdaptCurrentAnimation(mScrimForBubble); - dispatchScrimState(mScrimBehind.getViewAlpha()); + dispatchBackScrimState(mScrimBehind.getViewAlpha()); // Reset wallpaper timeout if it's already timeout like expanding panel while PULSING // and docking. @@ -693,10 +739,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump && !mBlankScreen; mScrimInFront.setColors(mColors, animateScrimInFront); - mScrimBehind.setColors(mColors, animateScrimNotifications); + mScrimBehind.setColors(mColors, animateBehindScrim); mNotificationsScrim.setColors(mColors, animateScrimNotifications); - dispatchScrimState(mScrimBehind.getViewAlpha()); + dispatchBackScrimState(mScrimBehind.getViewAlpha()); } // We want to override the back scrim opacity for the AOD state @@ -723,15 +769,21 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump dispatchScrimsVisible(); } - private void dispatchScrimState(float alpha) { + private void dispatchBackScrimState(float alpha) { + // When clipping QS, the notification scrim is the one that feels behind. + // mScrimBehind will be drawing black and its opacity will always be 1. + if (mClipsQsScrim && mQsBottomVisible) { + alpha = mNotificationsAlpha; + } mScrimStateListener.accept(mState, alpha, mScrimInFront.getColors()); } private void dispatchScrimsVisible() { + final ScrimView backScrim = mClipsQsScrim ? mNotificationsScrim : mScrimBehind; final int currentScrimVisibility; - if (mScrimInFront.getViewAlpha() == 1 || mScrimBehind.getViewAlpha() == 1) { + if (mScrimInFront.getViewAlpha() == 1 || backScrim.getViewAlpha() == 1) { currentScrimVisibility = OPAQUE; - } else if (mScrimInFront.getViewAlpha() == 0 && mScrimBehind.getViewAlpha() == 0) { + } else if (mScrimInFront.getViewAlpha() == 0 && backScrim.getViewAlpha() == 0) { currentScrimVisibility = TRANSPARENT; } else { currentScrimVisibility = SEMI_TRANSPARENT; @@ -859,7 +911,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } else if (scrim == mScrimBehind) { return mBehindTint; } else if (scrim == mNotificationsScrim) { - return Color.TRANSPARENT; + return mNotificationsTint; } else if (scrim == mScrimForBubble) { return mBubbleTint; } else { @@ -917,9 +969,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump if (mState == ScrimState.UNLOCKED) { mInFrontTint = Color.TRANSPARENT; mBehindTint = mState.getBehindTint(); + mNotificationsTint = mState.getNotifTint(); mBubbleTint = Color.TRANSPARENT; updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint); updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint); + updateScrimColor(mNotificationsScrim, mNotificationsAlpha, mNotificationsTint); if (mScrimForBubble != null) { updateScrimColor(mScrimForBubble, mBubbleAlpha, mBubbleTint); } @@ -964,7 +1018,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } if (scrim == mScrimBehind) { - dispatchScrimState(alpha); + dispatchBackScrimState(alpha); } final boolean wantsAlphaUpdate = alpha != currentAlpha; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index a9774d850fd9..1469cdab2d62 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -22,7 +22,7 @@ import android.os.Trace; import androidx.annotation.Nullable; import com.android.systemui.dock.DockManager; -import com.android.systemui.statusbar.ScrimView; +import com.android.systemui.scrim.ScrimView; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; /** @@ -80,11 +80,16 @@ public enum ScrimState { } mFrontTint = Color.BLACK; mBehindTint = Color.BLACK; + mNotifTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT; mBubbleTint = Color.TRANSPARENT; mFrontAlpha = 0; - mBehindAlpha = mScrimBehindAlphaKeyguard; + mBehindAlpha = mClipQsScrim ? 1 : mScrimBehindAlphaKeyguard; + mNotifAlpha = mClipQsScrim ? mScrimBehindAlphaKeyguard : 0; mBubbleAlpha = 0; + if (mClipQsScrim) { + updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + } } }, @@ -105,7 +110,10 @@ public enum ScrimState { BOUNCER { @Override public void prepare(ScrimState previousState) { - mBehindAlpha = mDefaultScrimAlpha; + mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha; + mBehindTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT; + mNotifAlpha = mClipQsScrim ? mDefaultScrimAlpha : 0; + mNotifTint = Color.TRANSPARENT; mFrontAlpha = 0f; mBubbleAlpha = 0f; } @@ -126,10 +134,15 @@ public enum ScrimState { SHADE_LOCKED { @Override public void prepare(ScrimState previousState) { - mBehindAlpha = mDefaultScrimAlpha; + mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha; + mNotifAlpha = 1f; mBubbleAlpha = 0f; mFrontAlpha = 0f; mBehindTint = Color.BLACK; + + if (mClipQsScrim) { + updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + } } // to make sure correct color is returned before "prepare" is called @@ -224,7 +237,8 @@ public enum ScrimState { @Override public void prepare(ScrimState previousState) { // State that UI will sync to. - mBehindAlpha = 0; + mBehindAlpha = mClipQsScrim ? 1 : 0; + mNotifAlpha = 0; mFrontAlpha = 0; mBubbleAlpha = 0; @@ -253,6 +267,10 @@ public enum ScrimState { mBubbleTint = Color.BLACK; mBlankScreen = true; } + + if (mClipQsScrim) { + updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + } } }, @@ -264,7 +282,7 @@ public enum ScrimState { public void prepare(ScrimState previousState) { mFrontTint = Color.TRANSPARENT; mBehindTint = Color.TRANSPARENT; - mBubbleTint = Color.TRANSPARENT; + mBubbleTint = Color.BLACK; mFrontAlpha = 0f; mBehindAlpha = mDefaultScrimAlpha; @@ -279,12 +297,14 @@ public enum ScrimState { int mFrontTint = Color.TRANSPARENT; int mBehindTint = Color.TRANSPARENT; int mBubbleTint = Color.TRANSPARENT; + int mNotifTint = Color.TRANSPARENT; boolean mAnimateChange = true; float mAodFrontScrimAlpha; float mFrontAlpha; float mBehindAlpha; float mBubbleAlpha; + float mNotifAlpha; float mScrimBehindAlphaKeyguard; float mDefaultScrimAlpha; @@ -301,6 +321,7 @@ public enum ScrimState { boolean mWakeLockScreenSensorActive; boolean mKeyguardFadingAway; long mKeyguardFadingAwayDuration; + boolean mClipQsScrim; public void init(ScrimView scrimInFront, ScrimView scrimBehind, ScrimView scrimForBubble, DozeParameters dozeParameters, DockManager dockManager) { @@ -325,6 +346,10 @@ public enum ScrimState { return mBehindAlpha; } + public float getNotifAlpha() { + return mNotifAlpha; + } + public float getBubbleAlpha() { return mBubbleAlpha; } @@ -337,6 +362,10 @@ public enum ScrimState { return mBehindTint; } + public int getNotifTint() { + return mNotifTint; + } + public int getBubbleTint() { return mBubbleTint; } @@ -406,4 +435,8 @@ public enum ScrimState { mKeyguardFadingAway = fadingAway; mKeyguardFadingAwayDuration = duration; } + + public void setClipQsScrim(boolean clipsQsScrim) { + mClipQsScrim = clipsQsScrim; + } }
\ No newline at end of file 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 026072bf0d6a..9d0285a5042f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -21,7 +21,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.WindowType; import static android.app.StatusBarManager.WindowVisibleState; import static android.app.StatusBarManager.windowStateToString; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.containsType; @@ -180,6 +179,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSFragment; import com.android.systemui.qs.QSPanelController; import com.android.systemui.recents.ScreenPinningRequest; +import com.android.systemui.scrim.ScrimView; import com.android.systemui.settings.brightness.BrightnessSlider; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.AutoHideUiElement; @@ -202,7 +202,6 @@ import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.PowerButtonReveal; import com.android.systemui.statusbar.PulseExpansionHandler; -import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; @@ -1139,7 +1138,6 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarView.setScrimController(mScrimController); mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners); - statusBarFragment.initNotificationIconArea(mNotificationIconAreaController); // CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of // mStatusBarView.mExpanded and mStatusBarView.mBouncerShowing are false. // PhoneStatusBarView's new instance will set to be gone in @@ -1182,7 +1180,8 @@ public class StatusBar extends SystemUI implements DemoMode, new CollapsedStatusBarFragment( mOngoingCallController, mAnimationScheduler, - mDotViewController), + mDotViewController, + mNotificationIconAreaController), CollapsedStatusBarFragment.TAG) .commit(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 088f9475c7d8..2b5caf91e024 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -364,7 +364,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, MetricsEvent.ACTION_LS_NOTE, 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_NOTIFICATION_FALSE_TOUCH); - mNotificationPanel.showTransientIndication(R.string.notification_tap_again); ActivatableNotificationView previousView = mNotificationPanel.getActivatedChild(); if (previousView != null) { previousView.makeInactive(true /* animate */); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index 96473c2a961c..51bb6434e61c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -52,6 +52,18 @@ class OngoingCallController @Inject constructor( private val mListeners: MutableList<OngoingCallListener> = mutableListOf() private val notifListener = object : NotifCollectionListener { + // Temporary workaround for b/178406514 for testing purposes. + // + // b/178406514 means that posting an incoming call notif then updating it to an ongoing call + // notif does not work (SysUI never receives the update). This workaround allows us to + // trigger the ongoing call chip when an ongoing call notif is *added* rather than + // *updated*, allowing us to test the chip. + // + // TODO(b/183229367): Remove this function override when b/178406514 is fixed. + override fun onEntryAdded(entry: NotificationEntry) { + onEntryUpdated(entry) + } + override fun onEntryUpdated(entry: NotificationEntry) { if (isOngoingCallNotification(entry)) { ongoingCallInfo = OngoingCallInfo( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java index d52ea890af7e..1be2d6e419a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java @@ -36,6 +36,7 @@ import com.android.settingslib.drawable.CircleFramedDrawable; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.AnimatableProperty; @@ -67,7 +68,9 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie private final ScreenLifecycle mScreenLifecycle; private UserSwitcherController.BaseUserAdapter mAdapter; private final KeyguardStateController mKeyguardStateController; + private final FalsingManager mFalsingManager; protected final SysuiStatusBarStateController mStatusBarStateController; + private final ConfigurationController mConfigurationController; private final KeyguardVisibilityHelper mKeyguardVisibilityHelper; private final KeyguardUserDetailAdapter mUserDetailAdapter; private NotificationPanelViewController mNotificationPanelViewController; @@ -76,7 +79,6 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie // State info for the user switch and keyguard private int mBarState; - private float mDarkAmount; private final StatusBarStateController.StateListener mStatusBarStateListener = new StatusBarStateController.StateListener() { @@ -97,6 +99,15 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie } }; + private ConfigurationController.ConfigurationListener + mConfigurationListener = new ConfigurationController.ConfigurationListener() { + + @Override + public void onUiModeChanged() { + updateView(true); + } + }; + @Inject public KeyguardQsUserSwitchController( UserAvatarView view, @@ -106,6 +117,8 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie ScreenLifecycle screenLifecycle, UserSwitcherController userSwitcherController, KeyguardStateController keyguardStateController, + FalsingManager falsingManager, + ConfigurationController configurationController, SysuiStatusBarStateController statusBarStateController, DozeParameters dozeParameters, UiEventLogger uiEventLogger) { @@ -117,6 +130,8 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie mScreenLifecycle = screenLifecycle; mUserSwitcherController = userSwitcherController; mKeyguardStateController = keyguardStateController; + mFalsingManager = falsingManager; + mConfigurationController = configurationController; mStatusBarStateController = statusBarStateController; mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController, dozeParameters); @@ -136,6 +151,10 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie }; mView.setOnClickListener(v -> { + if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + return; + } + if (isListAnimating()) { return; } @@ -153,8 +172,6 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie R.string.accessibility_quick_settings_choose_user_action))); } }); - - updateView(true /* forceUpdate */); } @Override @@ -163,6 +180,8 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie mAdapter.registerDataSetObserver(mDataSetObserver); mDataSetObserver.onChanged(); mStatusBarStateController.addCallback(mStatusBarStateListener); + mConfigurationController.addCallback(mConfigurationListener); + updateView(true /* forceUpdate */); } @Override @@ -171,6 +190,7 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie mAdapter.unregisterDataSetObserver(mDataSetObserver); mStatusBarStateController.removeCallback(mStatusBarStateListener); + mConfigurationController.removeCallback(mConfigurationListener); } public final DataSetObserver mDataSetObserver = new DataSetObserver() { 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 a3fd92f8e186..d86ef3208f7e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -112,6 +112,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private final TextView.OnEditorActionListener mEditorActionHandler; private final NotificationRemoteInputManager mRemoteInputManager; private final List<OnFocusChangeListener> mEditTextFocusChangeListeners = new ArrayList<>(); + private final List<OnSendRemoteInputListener> mOnSendListeners = new ArrayList<>(); private RemoteEditText mEditText; private ImageButton mSendButton; private GradientDrawable mContentBackground; @@ -357,6 +358,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private void sendRemoteInput(Intent intent) { if (mBouncerChecker != null && mBouncerChecker.showBouncerIfNecessary()) { mEditText.hideIme(); + for (OnSendRemoteInputListener listener : mOnSendListeners) { + listener.onSendRequestBounced(); + } return; } @@ -370,6 +374,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mController.remoteInputSent(mEntry); mEntry.setHasSentReply(); + for (OnSendRemoteInputListener listener : mOnSendListeners) { + listener.onSendRemoteInput(); + } + // Tell ShortcutManager that this package has been "activated". ShortcutManager // will reset the throttling for this package. // Strictly speaking, the intent receiver may be different from the notification publisher, @@ -754,6 +762,27 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } + /** Registers a listener for send events on this RemoteInputView */ + public void addOnSendRemoteInputListener(OnSendRemoteInputListener listener) { + mOnSendListeners.add(listener); + } + + /** Removes a previously-added listener for send events on this RemoteInputView */ + public void removeOnSendRemoteInputListener(OnSendRemoteInputListener listener) { + mOnSendListeners.remove(listener); + } + + /** Listener for send events */ + public interface OnSendRemoteInputListener { + /** Invoked when the remote input has been sent successfully. */ + void onSendRemoteInput(); + /** + * Invoked when the user had requested to send the remote input, but authentication was + * required and the bouncer was shown instead. + */ + void onSendRequestBounced(); + } + /** Handler for button click on send action in IME. */ private class EditorActionHandler implements TextView.OnEditorActionListener { diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 329cd71deda7..9bdd8c009531 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -151,6 +151,10 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { + wallpaperColors); mDeferredThemeEvaluation = true; return; + } else if (mDeferredThemeEvaluation) { + Log.i(TAG, "Wallpaper color event received, but we already were deferring eval: " + + wallpaperColors); + return; } else { if (DEBUG) { Log.i(TAG, "During user setup, but allowing first color event: had? " diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index 570202845e86..755372b4b0f5 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -299,7 +299,7 @@ public class StorageNotification extends SystemUI { // but cause crash when call notifyAsUser(). Here we return directly for USER_NULL, and // leave all notifications belong to removed user to NotificationManagerService, the latter // will remove all notifications of the removed user when handles user stopped broadcast. - if (isAutomotive() && vol.getMountUserId() == UserHandle.USER_NULL) { + if (vol.getMountUserId() == UserHandle.USER_NULL) { Log.d(TAG, "Ignore public volume state change event of removed user"); return; } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index b823534c2813..b95545576e56 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -63,11 +63,11 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.scrim.ScrimView; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeWindowController; -import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.NotificationChannelHelper; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java index 141b9f7d410d..39a8bd94bf37 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java @@ -32,8 +32,9 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ChoreographerSfVsync; import com.android.wm.shell.common.annotations.ShellMainThread; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; +import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm; +import com.android.wm.shell.startingsurface.tv.TvStartingWindowTypeAlgorithm; import com.android.wm.shell.transition.Transitions; import dagger.Module; @@ -80,4 +81,14 @@ public class TvWMShellModule { displayImeController, transactionPool, shellTaskOrganizer, syncQueue, taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler); } + + // + // Starting Windows (Splash Screen) + // + + @WMSingleton + @Provides + static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() { + return new TvStartingWindowTypeAlgorithm(); + }; } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index f96d34495fda..4c9ba66c1e39 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -72,6 +72,7 @@ import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.startingsurface.StartingSurface; import com.android.wm.shell.startingsurface.StartingWindowController; +import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm; import com.android.wm.shell.transition.ShellTransitions; import com.android.wm.shell.transition.Transitions; @@ -381,8 +382,10 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static StartingWindowController provideStartingWindowController(Context context, - @ShellSplashscreenThread ShellExecutor splashScreenExecutor, TransactionPool pool) { - return new StartingWindowController(context, splashScreenExecutor, pool); + @ShellSplashscreenThread ShellExecutor splashScreenExecutor, + StartingWindowTypeAlgorithm startingWindowTypeAlgorithm, TransactionPool pool) { + return new StartingWindowController(context, splashScreenExecutor, + startingWindowTypeAlgorithm, pool); } // diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 743dd466d95e..36fd9bee80ab 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -54,6 +54,8 @@ import com.android.wm.shell.pip.phone.PipAppOpsListener; import com.android.wm.shell.pip.phone.PipController; import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.pip.phone.PipTouchHandler; +import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm; +import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm; import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -229,4 +231,14 @@ public class WMShellModule { menuController, pipSnapAlgorithm, pipTransitionController, floatingContentCoordinator); } + + // + // Starting Windows (Splash Screen) + // + + @WMSingleton + @Provides + static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() { + return new PhoneStartingWindowTypeAlgorithm(); + } } diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index 38da21e016ec..ff5165d4e7cf 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -22,6 +22,9 @@ LOCAL_JACK_FLAGS := --multi-dex native LOCAL_DX_FLAGS := --multi-dex LOCAL_PACKAGE_NAME := SystemUITests +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_COMPATIBILITY_SUITE := device-tests diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 263a75c0928a..39ebe681841b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -41,8 +41,11 @@ import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.BcSmartspaceDataPlugin; +import com.android.systemui.plugins.BcSmartspaceDataPlugin.IntentStarter; import com.android.systemui.plugins.ClockPlugin; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.StatusBarState; @@ -107,6 +110,10 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { SmartspaceView mSmartspaceView; @Mock SystemUIFactory mSystemUIFactory; + @Mock + ActivityStarter mActivityStarter; + @Mock + FalsingManager mFalsingManager; private KeyguardClockSwitchController mController; private View mStatusArea; @@ -143,7 +150,9 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { mExecutor, mBatteryController, mConfigurationController, - mSystemUIFactory + mSystemUIFactory, + mActivityStarter, + mFalsingManager ); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); @@ -152,7 +161,6 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { mStatusArea = new View(getContext()); when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea); when(mSmartspaceDataProvider.getView(any())).thenReturn(mSmartspaceView); - } @Test @@ -264,5 +272,9 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { public void setPrimaryTextColor(int color) { } public void setDozeAmount(float amount) { } + + public void setIntentStarter(IntentStarter intentStarter) { } + + public void setFalsingManager(FalsingManager falsingManager) { } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java index f077190168be..bc2444546352 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java @@ -179,7 +179,23 @@ public class FalsingCollectorImplTest extends SysuiTestCase { mFalsingCollector.onTouchEvent(down); verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class)); - // Up event would normally flush the up event. + // Up event would normally flush the up event, but doesn't. + mFalsingCollector.onTouchEvent(up); + verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class)); + } + + @Test + public void testAvoidDozing() { + MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); + MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0); + + when(mStatusBarStateController.isDozing()).thenReturn(true); + + // Nothing passed initially + mFalsingCollector.onTouchEvent(down); + verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class)); + + // Up event would normally flush the up event, but doesn't. mFalsingCollector.onTouchEvent(up); verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java index fe2103ccc557..724f8a3adf80 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java @@ -42,6 +42,7 @@ public class DozeConfigurationUtil { when(params.getSelectivelyRegisterSensorsUsingProx()).thenReturn(false); when(params.singleTapUsesProx()).thenReturn(true); when(params.longPressUsesProx()).thenReturn(true); + when(params.getQuickPickupAodDuration()).thenReturn(500); doneHolder[0] = true; return params; diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 8eb0e9c5d0ab..4a9d66c16003 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -35,10 +35,12 @@ import android.view.Display; import androidx.test.filters.SmallTest; +import com.android.internal.logging.UiEventLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; +import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.concurrency.FakeThreadFactory; @@ -76,6 +78,8 @@ public class DozeTriggersTest extends SysuiTestCase { private ProximitySensor.ProximityCheck mProximityCheck; @Mock private AuthController mAuthController; + @Mock + private UiEventLogger mUiEventLogger; private DozeTriggers mTriggers; private FakeSensorManager mSensors; @@ -107,7 +111,7 @@ public class DozeTriggersTest extends SysuiTestCase { mTriggers = new DozeTriggers(mContext, mHost, config, dozeParameters, asyncSensorManager, wakeLock, mDockManager, mProximitySensor, mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(), - mAuthController, mExecutor); + mAuthController, mExecutor, mUiEventLogger); mTriggers.setDozeMachine(mMachine); waitForSensorManager(); } @@ -195,6 +199,40 @@ public class DozeTriggersTest extends SysuiTestCase { } @Test + public void testQuickPickup() { + // GIVEN device is in doze (screen blank, but running doze sensors) + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + + // WHEN quick pick up is triggered + mTriggers.onSensor(DozeLog.REASON_SENSOR_QUICK_PICKUP, 100, 100, null); + + // THEN device goes into aod (shows clock with black background) + verify(mMachine).requestState(DozeMachine.State.DOZE_AOD); + + // THEN a log is taken that quick pick up was triggered + verify(mUiEventLogger).log(DozingUpdateUiEvent.DOZING_UPDATE_QUICK_PICKUP); + } + + @Test + public void testQuickPickupTimeOutAfterExecutables() { + // GIVEN quick pickup is triggered when device is in DOZE + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + mTriggers.onSensor(DozeLog.REASON_SENSOR_QUICK_PICKUP, 100, 100, null); + verify(mMachine).requestState(DozeMachine.State.DOZE_AOD); + verify(mMachine, never()).requestState(DozeMachine.State.DOZE); + + // WHEN next executable is run + mExecutor.advanceClockToLast(); + mExecutor.runAllReady(); + + // THEN device goes back into DOZE + verify(mMachine).requestState(DozeMachine.State.DOZE); + + // THEN a log is taken that wake up timeout expired + verify(mUiEventLogger).log(DozingUpdateUiEvent.DOZING_UPDATE_WAKE_TIMEOUT); + } + + @Test public void testOnSensor_Fingerprint() { final int screenX = 100; final int screenY = 100; diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java index 609b8474d134..4a487be914c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java @@ -74,7 +74,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, true, - false, KEY, false, false, false); + false, KEY, false, false, false, 0L); mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt index 36b6527167f2..a9d256bf37cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.media +import android.app.smartspace.SmartspaceTarget import android.graphics.Color import androidx.test.filters.SmallTest import android.testing.AndroidTestingRunner @@ -47,6 +48,7 @@ private const val PACKAGE = "PKG" private const val ARTIST = "ARTIST" private const val TITLE = "TITLE" private const val DEVICE_NAME = "DEVICE_NAME" +private const val SMARTSPACE_KEY = "SMARTSPACE_KEY" private fun <T> eq(value: T): T = Mockito.eq(value) ?: value private fun <T> any(): T = Mockito.any() @@ -68,6 +70,8 @@ class MediaDataFilterTest : SysuiTestCase() { private lateinit var lockscreenUserManager: NotificationLockscreenUserManager @Mock private lateinit var executor: Executor + @Mock + private lateinit var smartspaceData: SmartspaceTarget private lateinit var mediaDataFilter: MediaDataFilter private lateinit var dataMain: MediaData @@ -91,6 +95,8 @@ class MediaDataFilterTest : SysuiTestCase() { dataGuest = MediaData(USER_GUEST, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, null, null, device, true, null) + + `when`(smartspaceData.smartspaceTargetId).thenReturn(SMARTSPACE_KEY) } private fun setUser(id: Int) { @@ -212,6 +218,61 @@ class MediaDataFilterTest : SysuiTestCase() { mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) mediaDataFilter.onSwipeToDismiss() - verify(mediaDataManager).setTimedOut(eq(KEY), eq(true)) + verify(mediaDataManager).setTimedOut(eq(KEY), eq(true), eq(true)) + } + + @Test + fun testOnSmartspaceMediaDataLoaded_noMedia_usesSmartspace() { + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + + verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData)) + assertThat(mediaDataFilter.hasActiveMedia()).isTrue() + } + + @Test + fun testOnSmartspaceMediaDataLoaded_noRecentMedia_usesSmartspace() { + val dataOld = dataMain.copy(active = false, lastActive = 0L) + mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld) + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + + verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData)) + assertThat(mediaDataFilter.hasActiveMedia()).isTrue() + } + + @Test + fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_usesMedia() { + // WHEN we have media that was recently played, but not currently active + val dataCurrent = dataMain.copy(active = false, lastActive = System.currentTimeMillis()) + mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent)) + + // AND we get a smartspace signal + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + + // THEN we should tell listeners to treat the media as active instead + val dataCurrentAndActive = dataCurrent.copy(active = true) + verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive)) + assertThat(mediaDataFilter.hasActiveMedia()).isTrue() + } + + @Test + fun testOnSmartspaceMediaDataRemoved_usedSmartspace_clearsMedia() { + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) + + verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) + assertThat(mediaDataFilter.hasActiveMedia()).isFalse() + } + + @Test + fun testOnSmartspaceMediaDataRemoved_usedMedia_clearsMedia() { + val dataCurrent = dataMain.copy(active = false, lastActive = System.currentTimeMillis()) + mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + + mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) + + verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrent)) + assertThat(mediaDataFilter.hasActiveMedia()).isFalse() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 96eb4b096931..678f89a06479 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -60,6 +60,7 @@ class MediaDataManagerTest : SysuiTestCase() { @JvmField @Rule val mockito = MockitoJUnit.rule() @Mock lateinit var mediaControllerFactory: MediaControllerFactory @Mock lateinit var controller: MediaController + @Mock lateinit var playbackInfo: MediaController.PlaybackInfo lateinit var session: MediaSession lateinit var metadataBuilder: MediaMetadata.Builder lateinit var backgroundExecutor: FakeExecutor @@ -118,6 +119,9 @@ class MediaDataManagerTest : SysuiTestCase() { putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) } whenever(mediaControllerFactory.create(eq(session.sessionToken))).thenReturn(controller) + whenever(controller.playbackInfo).thenReturn(playbackInfo) + whenever(playbackInfo.playbackType).thenReturn( + MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) // This is an ugly hack for now. The mediaSessionBasedFilter is one of the internal // listeners in the internal processing pipeline. It receives events, but ince it is a @@ -230,6 +234,27 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test + fun testOnNotificationRemoved_withResumption_butNotLocal() { + // GIVEN that the manager has a notification with a resume action, but is not local + whenever(controller.metadata).thenReturn(metadataBuilder.build()) + whenever(playbackInfo.playbackType).thenReturn( + MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value + val dataRemoteWithResume = data.copy(resumeAction = Runnable {}, isLocalSession = false) + mediaDataManager.onMediaDataLoaded(KEY, null, dataRemoteWithResume) + + // WHEN the notification is removed + mediaDataManager.onNotificationRemoved(KEY) + + // THEN the media data is removed + verify(listener).onMediaDataRemoved(eq(KEY)) + } + + @Test fun testAppBlockedFromResumption() { // GIVEN that the manager has a notification with a resume action whenever(controller.metadata).thenReturn(metadataBuilder.build()) @@ -280,11 +305,12 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testAddResumptionControls() { - // WHEN resumption controls are added` + // WHEN resumption controls are added val desc = MediaDescription.Builder().run { setTitle(SESSION_TITLE) build() } + val currentTimeMillis = System.currentTimeMillis() mediaDataManager.addResumptionControls(USER_ID, desc, Runnable {}, session.sessionToken, APP_NAME, pendingIntent, PACKAGE_NAME) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) @@ -296,6 +322,7 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(data.song).isEqualTo(SESSION_TITLE) assertThat(data.app).isEqualTo(APP_NAME) assertThat(data.actions).hasSize(1) + assertThat(data.lastActive).isAtLeast(currentTimeMillis) } @Test @@ -350,4 +377,52 @@ class MediaDataManagerTest : SysuiTestCase() { smartspaceMediaDataProvider.onTargetsAvailable(listOf()) verify(listener).onSmartspaceMediaDataRemoved(KEY_MEDIA_SMARTSPACE) } + + @Test + fun testOnMediaDataChanged_updatesLastActiveTime() { + val currentTimeMillis = System.currentTimeMillis() + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value!!.lastActive).isAtLeast(currentTimeMillis) + } + + @Test + fun testOnMediaDataTimedOut_doesNotUpdateLastActiveTime() { + // GIVEN that the manager has a notification + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + + // WHEN the notification times out + val currentTimeMillis = System.currentTimeMillis() + mediaDataManager.setTimedOut(KEY, true, true) + + // THEN the last active time is not changed + verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTimeMillis) + } + + @Test + fun testOnActiveMediaConverted_doesNotUpdateLastActiveTime() { + // GIVEN that the manager has a notification with a resume action + whenever(controller.metadata).thenReturn(metadataBuilder.build()) + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + val data = mediaDataCaptor.value + assertThat(data.resumption).isFalse() + mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) + + // WHEN the notification is removed + val currentTimeMillis = System.currentTimeMillis() + mediaDataManager.onNotificationRemoved(KEY) + + // THEN the last active time is not changed + verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value.resumption).isTrue() + assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTimeMillis) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt index 59c2b176f12e..96d1d94795f6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt @@ -182,6 +182,16 @@ class MediaResumeListenerTest : SysuiTestCase() { } @Test + fun testOnLoad_remotePlayback_doesNotCheck() { + // When media data is loaded that has not been checked yet, and is not local + val dataRemote = data.copy(isLocalSession = false) + resumeListener.onMediaDataLoaded(KEY, null, dataRemote) + + // Then we do not take action + verify(mediaDataManager, never()).setResumeAction(any(), any()) + } + + @Test fun testOnLoad_checksForResume_hasService() { // Set up mocks to successfully find a MBS that returns valid media val pm = mock(PackageManager::class.java) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt index f3979592c894..0a573cd6020c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt @@ -227,4 +227,41 @@ class MediaTimeoutListenerTest : SysuiTestCase() { mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData) assertThat(mediaTimeoutListener.isTimedOut(KEY)).isFalse() } + + @Test + fun testOnSessionDestroyed_clearsTimeout() { + // GIVEN media that is paused + val mediaPaused = mediaData.copy(isPlaying = false) + mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaPaused) + verify(mediaController).registerCallback(capture(mediaCallbackCaptor)) + assertThat(executor.numPending()).isEqualTo(1) + + // WHEN the session is destroyed + mediaCallbackCaptor.value.onSessionDestroyed() + + // THEN the controller is unregistered and timeout run + verify(mediaController).unregisterCallback(anyObject()) + assertThat(executor.numPending()).isEqualTo(0) + } + + @Test + fun testSessionDestroyed_thenRestarts_resetsTimeout() { + // Assuming we have previously destroyed the session + testOnSessionDestroyed_clearsTimeout() + + // WHEN we get an update with media playing + val playingState = mock(android.media.session.PlaybackState::class.java) + `when`(playingState.state).thenReturn(PlaybackState.STATE_PLAYING) + `when`(mediaController.playbackState).thenReturn(playingState) + val mediaPlaying = mediaData.copy(isPlaying = true) + mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaPlaying) + + // THEN the timeout runnable will update the state + assertThat(executor.numPending()).isEqualTo(1) + with(executor) { + advanceClockToNext() + runAllReady() + } + verify(timeoutCallback).invoke(eq(KEY), eq(false)) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/NotificationHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/NotificationHelperTest.java index 7cddc3f8e82d..1b713dd8de88 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/NotificationHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/NotificationHelperTest.java @@ -19,6 +19,7 @@ import static android.app.Notification.CATEGORY_MISSED_CALL; import static com.android.systemui.people.NotificationHelper.getHighestPriorityNotification; import static com.android.systemui.people.NotificationHelper.getMessagingStyleMessages; +import static com.android.systemui.people.NotificationHelper.getSenderIfGroupConversation; import static com.android.systemui.people.NotificationHelper.isMissedCall; import static com.android.systemui.people.NotificationHelper.isMissedCallOrHasContent; import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; @@ -32,6 +33,7 @@ import android.app.Notification; import android.app.Person; import android.content.pm.ShortcutInfo; import android.net.Uri; +import android.os.Bundle; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; @@ -202,4 +204,53 @@ public class NotificationHelperTest extends SysuiTestCase { assertThat(getHighestPriorityNotification(notifications)) .isEqualTo(mNotificationEntry1); } + + @Test + public void testGetSenderIfGroupConversation_notGroup() { + Notification.MessagingStyle.Message message = new Notification.MessagingStyle.Message( + NOTIFICATION_TEXT_3, 10, PERSON); + Notification notification = new Notification.Builder(mContext, "test") + .setContentTitle("TEST_TITLE") + .setContentText("TEST_TEXT") + .setShortcutId(SHORTCUT_ID_1) + .setStyle(new Notification.MessagingStyle(PERSON).addMessage(message)) + .build(); + assertThat(getSenderIfGroupConversation(notification, message)).isNull(); + } + + @Test + public void testGetSenderIfGroupConversation_group() { + Bundle extras = new Bundle(); + extras.putBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION, true); + Notification.MessagingStyle.Message message = new Notification.MessagingStyle.Message( + NOTIFICATION_TEXT_3, 10, PERSON); + + Notification notification = new Notification.Builder(mContext, "test") + .setContentTitle("TEST_TITLE") + .setContentText("TEST_TEXT") + .setShortcutId(SHORTCUT_ID_1) + .setStyle(new Notification.MessagingStyle(PERSON) + .setGroupConversation(true) + .addMessage(message)) + .addExtras(extras) + .build(); + assertThat(getSenderIfGroupConversation(notification, message)).isEqualTo("name"); + } + + @Test + public void testGetSenderIfGroupConversation_groupNoName() { + Bundle extras = new Bundle(); + extras.putBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION, true); + Notification.MessagingStyle.Message message = new Notification.MessagingStyle.Message( + NOTIFICATION_TEXT_3, 10, new Person.Builder().build()); + + Notification notification = new Notification.Builder(mContext, "test") + .setContentTitle("TEST_TITLE") + .setContentText("TEST_TEXT") + .setShortcutId(SHORTCUT_ID_1) + .setStyle(new Notification.MessagingStyle(PERSON).addMessage(message)) + .setExtras(extras) + .build(); + assertThat(getSenderIfGroupConversation(notification, message)).isNull(); + } } 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 c929073d9a09..cc322620eb57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -59,6 +59,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.widget.PeopleTileKey; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -94,6 +95,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { private static final Uri URI = Uri.parse("fake_uri"); private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android); private static final String NAME = "username"; + private static final UserHandle USER = new UserHandle(0); private static final Person PERSON = new Person.Builder() .setName("name") .setKey("abc") @@ -103,6 +105,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { private static final PeopleSpaceTile PERSON_TILE = new PeopleSpaceTile .Builder(SHORTCUT_ID_1, NAME, ICON, new Intent()) + .setUserHandle(USER) .setLastInteractionTimestamp(123L) .setNotificationKey(NOTIFICATION_KEY) .setNotificationContent(NOTIFICATION_CONTENT) @@ -231,10 +234,51 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setPackageName(PACKAGE_NAME) .setUserHandle(new UserHandle(0)) .build(); + PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromNotification(mContext, tile, mNotificationEntry1, 0); + .augmentTileFromNotification(mContext, tile, key, mNotificationEntry1, 0); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); + assertThat(actual.getNotificationSender()).isEqualTo(null); + } + + @Test + public void testAugmentTileFromNotificationGroupWithSender() { + Bundle extras = new Bundle(); + extras.putBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION, true); + Notification notification = new Notification.Builder(mContext, "test") + .setContentTitle("TEST_TITLE") + .setContentText("TEST_TEXT") + .setShortcutId(SHORTCUT_ID_1) + .setStyle(new Notification.MessagingStyle(PERSON) + .setGroupConversation(true) + .addMessage(new Notification.MessagingStyle.Message( + NOTIFICATION_TEXT_1, 0, PERSON)) + .addMessage(new Notification.MessagingStyle.Message( + NOTIFICATION_TEXT_2, 20, PERSON)) + .addMessage(new Notification.MessagingStyle.Message( + NOTIFICATION_TEXT_3, 10, PERSON)) + ) + .setExtras(extras) + .build(); + NotificationEntry notificationEntry = new NotificationEntryBuilder() + .setNotification(notification) + .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_1).build()) + .setUser(UserHandle.of(0)) + .setPkg(PACKAGE_NAME) + .build(); + PeopleSpaceTile tile = + new PeopleSpaceTile + .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) + .setPackageName(PACKAGE_NAME) + .setUserHandle(new UserHandle(0)) + .build(); + PeopleTileKey key = new PeopleTileKey(tile); + PeopleSpaceTile actual = PeopleSpaceUtils + .augmentTileFromNotification(mContext, tile, key, notificationEntry, 0); + + assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); + assertThat(actual.getNotificationSender().toString()).isEqualTo("name"); } @Test @@ -245,8 +289,9 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setPackageName(PACKAGE_NAME) .setUserHandle(new UserHandle(0)) .build(); + PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromNotification(mContext, tile, mNotificationEntry3, 0); + .augmentTileFromNotification(mContext, tile, key, mNotificationEntry3, 0); assertThat(actual.getNotificationContent()).isEqualTo(null); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java index 3cc55f2f9070..d353d529865b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java @@ -42,6 +42,7 @@ import android.content.res.Resources; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; +import android.os.UserHandle; import android.testing.AndroidTestingRunner; import android.util.DisplayMetrics; import android.view.View; @@ -73,10 +74,13 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { private static final String GAME_DESCRIPTION = "Playing a game!"; private static final CharSequence MISSED_CALL = "Custom missed call message"; private static final String NAME = "username"; + private static final UserHandle USER = new UserHandle(0); + private static final String SENDER = "sender"; private static final PeopleSpaceTile PERSON_TILE_WITHOUT_NOTIFICATION = new PeopleSpaceTile .Builder(SHORTCUT_ID_1, NAME, ICON, new Intent()) .setLastInteractionTimestamp(0L) + .setUserHandle(USER) .build(); private static final PeopleSpaceTile PERSON_TILE = new PeopleSpaceTile @@ -85,6 +89,16 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { .setNotificationKey(NOTIFICATION_KEY) .setNotificationContent(NOTIFICATION_CONTENT) .setNotificationDataUri(URI) + .setUserHandle(USER) + .build(); + private static final PeopleSpaceTile PERSON_TILE_WITH_SENDER = + new PeopleSpaceTile + .Builder(SHORTCUT_ID_1, NAME, ICON, new Intent()) + .setLastInteractionTimestamp(123L) + .setNotificationKey(NOTIFICATION_KEY) + .setNotificationContent(NOTIFICATION_CONTENT) + .setNotificationSender(SENDER) + .setUserHandle(USER) .build(); private static final ConversationStatus GAME_STATUS = new ConversationStatus @@ -534,6 +548,86 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { } @Test + public void testCreateRemoteViewsWithNotificationWithSenderTemplate() { + PeopleSpaceTile tileWithStatusAndNotification = PERSON_TILE_WITH_SENDER.toBuilder() + .setNotificationDataUri(null) + .setStatuses(Arrays.asList(GAME_STATUS, + NEW_STORY_WITH_AVAILABILITY)).build(); + RemoteViews views = new PeopleTileViewHelper(mContext, + tileWithStatusAndNotification, 0, mOptions).getViews(); + View result = views.apply(mContext, null); + + TextView name = (TextView) result.findViewById(R.id.name); + assertEquals(name.getText(), NAME); + TextView subtext = (TextView) result.findViewById(R.id.subtext); + assertEquals(View.VISIBLE, result.findViewById(R.id.subtext).getVisibility()); + assertEquals(subtext.getText(), SENDER); + assertEquals(View.GONE, result.findViewById(R.id.predefined_icon).getVisibility()); + // Has availability. + assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility()); + // Has person icon. + assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility()); + // Has notification content. + TextView statusContent = (TextView) result.findViewById(R.id.text_content); + assertEquals(View.VISIBLE, statusContent.getVisibility()); + assertEquals(statusContent.getText(), NOTIFICATION_CONTENT); + + // Subtract one from lines because sender is included. + assertThat(statusContent.getMaxLines()).isEqualTo(2); + + // Has a single message, no count shown. + assertEquals(View.GONE, result.findViewById(R.id.messages_count).getVisibility()); + + mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, + getSizeInDp(R.dimen.required_width_for_medium) - 1); + RemoteViews smallView = new PeopleTileViewHelper(mContext, + tileWithStatusAndNotification, 0, mOptions).getViews(); + View smallResult = smallView.apply(mContext, null); + + // Show icon instead of name. + assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility()); + assertEquals(View.VISIBLE, + smallResult.findViewById(R.id.predefined_icon).getVisibility()); + // Has person icon. + assertEquals(View.VISIBLE, + smallResult.findViewById(R.id.person_icon).getVisibility()); + + // Has a single message, no count shown. + assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility()); + + mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, + getSizeInDp(R.dimen.required_width_for_large)); + mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, + getSizeInDp(R.dimen.required_height_for_large)); + RemoteViews largeView = new PeopleTileViewHelper(mContext, + tileWithStatusAndNotification, 0, mOptions).getViews(); + View largeResult = largeView.apply(mContext, null); + + name = (TextView) largeResult.findViewById(R.id.name); + assertEquals(name.getText(), NAME); + subtext = (TextView) largeResult.findViewById(R.id.subtext); + assertEquals(View.VISIBLE, largeResult.findViewById(R.id.subtext).getVisibility()); + assertEquals(subtext.getText(), SENDER); + assertEquals(View.GONE, largeResult.findViewById(R.id.predefined_icon).getVisibility()); + // Has availability. + assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility()); + // Has person icon. + View personIcon = largeResult.findViewById(R.id.person_icon); + assertEquals(View.VISIBLE, personIcon.getVisibility()); + // Has notification content. + statusContent = (TextView) largeResult.findViewById(R.id.text_content); + assertEquals(View.VISIBLE, statusContent.getVisibility()); + assertEquals(statusContent.getText(), NOTIFICATION_CONTENT); + + // Subtract one from lines because sender is included. + assertThat(statusContent.getMaxLines()).isEqualTo(2); + + // Has a single message, no count shown. + assertEquals(View.GONE, largeResult.findViewById(R.id.messages_count).getVisibility()); + + } + + @Test public void testCreateRemoteViewsWithNotificationTemplateTwoMessages() { PeopleSpaceTile tileWithStatusAndNotification = PERSON_TILE.toBuilder() .setNotificationDataUri(null) 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 725e5d4523a7..411fb02ba87a 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 @@ -1201,7 +1201,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .setPackageName(TEST_PACKAGE_A) .setUserHandle(new UserHandle(0)) .build(); - PeopleSpaceTile actual = mManager.augmentTileFromNotifications(tile, EMPTY_STRING, + PeopleTileKey key = new PeopleTileKey(tile); + PeopleSpaceTile actual = mManager.augmentTileFromNotifications(tile, key, EMPTY_STRING, Map.of(new PeopleTileKey(mNotificationEntry), new HashSet<>(Collections.singleton(mNotificationEntry)))); @@ -1216,8 +1217,9 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { .setPackageName(TEST_PACKAGE_A) .setUserHandle(new UserHandle(0)) .build(); + PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceTile actual = mManager - .augmentTileFromNotifications(tile, EMPTY_STRING, + .augmentTileFromNotifications(tile, key, EMPTY_STRING, Map.of(new PeopleTileKey(mNotificationEntry), new HashSet<>(Collections.singleton(mNotificationEntry)))); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt index 51aeb150d4f8..6cf3434d2182 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt @@ -165,7 +165,12 @@ class DeviceControlsTileTest : SysuiTestCase() { @Test fun testLongClickIntent() { - assertThat(tile.longClickIntent.action).isEqualTo(Settings.ACTION_DEVICE_CONTROLS_SETTINGS) + assertThat(tile.longClickIntent).isNull() + } + + @Test + fun testDoesNotHandleLongClick() { + assertThat(tile.state.handlesLongClick).isFalse() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java index 8c7d762b2307..3d658ec8e811 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java @@ -16,6 +16,8 @@ package com.android.systemui.screenshot; +import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType.REGULAR_SMART_ACTIONS; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -87,6 +89,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { CompletableFuture<List<Notification.Action>> smartActionsFuture = mScreenshotSmartActions.getSmartActionsFuture( "", Uri.parse("content://authority/data"), bitmap, smartActionsProvider, + REGULAR_SMART_ACTIONS, true, UserHandle.of(UserHandle.myUserId())); assertNotNull(smartActionsFuture); List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); @@ -104,7 +107,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow( RuntimeException.class); List<Notification.Action> actions = mScreenshotSmartActions.getSmartActions( - "", smartActionsFuture, timeoutMs, mSmartActionsProvider); + "", smartActionsFuture, timeoutMs, mSmartActionsProvider, REGULAR_SMART_ACTIONS); assertEquals(Collections.emptyList(), actions); } @@ -127,6 +130,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { CompletableFuture<List<Notification.Action>> smartActionsFuture = mScreenshotSmartActions.getSmartActionsFuture( "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, + REGULAR_SMART_ACTIONS, true, UserHandle.of(UserHandle.myUserId())); verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any(), any()); assertNotNull(smartActionsFuture); @@ -140,7 +144,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { Bitmap bitmap = mock(Bitmap.class); when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE); mScreenshotSmartActions.getSmartActionsFuture( - "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, true, + "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, + REGULAR_SMART_ACTIONS, true, UserHandle.of(UserHandle.myUserId())); verify(mSmartActionsProvider, times(1)).getActions( any(), any(), any(), any(), any(), any()); @@ -157,7 +162,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { mContext, null, mHandler); CompletableFuture<List<Notification.Action>> smartActionsFuture = mScreenshotSmartActions.getSmartActionsFuture("", null, bitmap, - actionsProvider, + actionsProvider, REGULAR_SMART_ACTIONS, true, UserHandle.of(UserHandle.myUserId())); assertNotNull(smartActionsFuture); List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/scrim/ScrimViewTest.java index c2e58efe1328..a345f7804ae9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/scrim/ScrimViewTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 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. @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.scrim; import static junit.framework.Assert.assertEquals; @@ -31,7 +31,6 @@ import android.view.View; import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; -import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.utils.leaks.LeakCheckedTest; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt index c832fe481f74..bdd4a01a9766 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt @@ -21,6 +21,8 @@ import android.app.NotificationChannel import android.app.NotificationManager.IMPORTANCE_DEFAULT import android.app.NotificationManager.IMPORTANCE_HIGH import android.app.NotificationManager.IMPORTANCE_LOW +import android.app.PendingIntent +import android.app.Person import android.os.SystemClock import android.service.notification.NotificationListenerService.RankingMap import android.testing.AndroidTestingRunner @@ -408,6 +410,74 @@ class NotificationRankingManagerTest : SysuiTestCase() { assertThat(b.bucket).isEqualTo(BUCKET_FOREGROUND_SERVICE) } + @Test + fun testSort_importantCall() { + whenever(sectionsManager.isFilteringEnabled()).thenReturn(true) + + val a = NotificationEntryBuilder() + .setImportance(IMPORTANCE_HIGH) + .setPkg("pkg") + .setOpPkg("pkg") + .setTag("tag") + .setNotification( + Notification.Builder(mContext, "test") + .build()) + .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build() + + val b = NotificationEntryBuilder() + .setImportance(IMPORTANCE_DEFAULT) // high priority + .setPkg("pkg2") + .setOpPkg("pkg2") + .setTag("tag") + .setNotification(mock(Notification::class.java).also { notif -> + whenever(notif.isForegroundService).thenReturn(true) + whenever(notif.isColorized).thenReturn(true) + }) + .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build() + + val cN = Notification.Builder(mContext, "test") + .setStyle(Notification.MessagingStyle("")) + .build() + val c = NotificationEntryBuilder() + .setImportance(IMPORTANCE_HIGH) + .setPkg("pkg") + .setOpPkg("pkg") + .setTag("tag") + .setNotification(cN) + .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) + .setUser(mContext.user) + .setOverrideGroupKey("") + .build() + + val dN = Notification.Builder(mContext, "test") + .setStyle(Notification.CallStyle.forOngoingCall( + Person.Builder().setName("caller").build(), + mock(PendingIntent::class.java))) + .build() + val d = NotificationEntryBuilder() + .setImportance(IMPORTANCE_DEFAULT) // high priority + .setPkg("pkg2") + .setOpPkg("pkg2") + .setTag("tag") + .setNotification(dN) + .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) + .setUser(mContext.user) + .setOverrideGroupKey("") + .build() + whenever(personNotificationIdentifier.getPeopleNotificationType(a)) + .thenReturn(TYPE_IMPORTANT_PERSON) + + assertThat(rankingManager.updateRanking(null, listOf(a, b, c, d), "test")) + .containsExactly(b, d, c, a) + assertThat(d.bucket).isEqualTo(BUCKET_FOREGROUND_SERVICE) + } + internal class TestableNotificationRankingManager( mediaManager: Lazy<NotificationMediaManager>, groupManager: NotificationGroupManagerLegacy, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java index 41835082d706..94e273b1965a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.notification.row; -import static org.mockito.ArgumentMatchers.anyFloat; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -57,7 +55,6 @@ public class NotificationContentViewTest extends SysuiTestCase { mView = new NotificationContentView(mContext, null); ExpandableNotificationRow row = new ExpandableNotificationRow(mContext, null); ExpandableNotificationRow mockRow = spy(row); - doNothing().when(mockRow).updateBackgroundAlpha(anyFloat()); doReturn(10).when(mockRow).getIntrinsicHeight(); mView.setContainingNotification(mockRow); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index f1c8ece58fda..a63d509b0b53 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -123,6 +123,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { verify(mStatusBarKeyguardViewManager).showBouncer(eq(false)); verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(), anyFloat()); + verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean()); assertThat(mBiometricUnlockController.getMode()) .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER); } @@ -170,6 +171,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(), anyFloat()); + verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false)); assertThat(mBiometricUnlockController.getMode()) .isEqualTo(BiometricUnlockController.MODE_UNLOCK_COLLAPSING); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java index 0e3e0cc8ea97..a01e0b1c3e3a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java @@ -31,6 +31,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.view.View; import android.view.ViewPropertyAnimator; +import android.widget.FrameLayout; import androidx.test.filters.SmallTest; @@ -40,24 +41,19 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.events.PrivacyDotViewController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; -import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; import org.mockito.Mockito; -import java.util.Objects; - @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) @SmallTest public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { - private NotificationIconAreaController mMockNotificiationAreaController; + private NotificationIconAreaController mMockNotificationAreaController; private View mNotificationAreaInner; - private View mCenteredNotificationAreaView; private StatusBarStateController mStatusBarStateController; private OngoingCallController mOngoingCallController; private SystemStatusAnimationScheduler mAnimationScheduler; @@ -74,26 +70,16 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mStatusBarStateController = mDependency .injectMockDependency(StatusBarStateController.class); injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); - mMockNotificiationAreaController = mock(NotificationIconAreaController.class); - mNotificationAreaInner = mock(View.class); - mCenteredNotificationAreaView = mock(View.class); when(statusBar.getPanelController()).thenReturn( mock(NotificationPanelViewController.class)); - when(mNotificationAreaInner.animate()).thenReturn(mock(ViewPropertyAnimator.class)); - when(mMockNotificiationAreaController.getNotificationInnerAreaView()).thenReturn( - mNotificationAreaInner); - when(mCenteredNotificationAreaView.animate()).thenReturn(mock(ViewPropertyAnimator.class)); - when(mMockNotificiationAreaController.getCenteredNotificationAreaView()).thenReturn( - mCenteredNotificationAreaView); } @Test public void testDisableNone() throws Exception { mFragments.dispatchResume(); processAllMessages(); - CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; - fragment.initNotificationIconArea(mMockNotificiationAreaController); + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area) @@ -106,9 +92,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { public void testDisableSystemInfo() throws Exception { mFragments.dispatchResume(); processAllMessages(); - CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; - fragment.initNotificationIconArea(mMockNotificiationAreaController); + fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false); assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area) @@ -124,12 +109,11 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { public void testDisableNotifications() throws Exception { mFragments.dispatchResume(); processAllMessages(); - CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; - fragment.initNotificationIconArea(mMockNotificiationAreaController); + fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false); - Mockito.verify(mNotificationAreaInner).setVisibility(eq(View.INVISIBLE)); + Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE)); fragment.disable(DEFAULT_DISPLAY, 0, 0, false); @@ -140,9 +124,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { public void testDisableClock() throws Exception { mFragments.dispatchResume(); processAllMessages(); - CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; - fragment.initNotificationIconArea(mMockNotificiationAreaController); + fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false); assertEquals(View.GONE, mFragment.getView().findViewById(R.id.clock).getVisibility()); @@ -153,63 +136,87 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - public void testOnDozingChanged() throws Exception { + public void disable_noOngoingCall_chipHidden() { mFragments.dispatchResume(); processAllMessages(); - CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; - fragment.initNotificationIconArea(mMockNotificiationAreaController); - fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false); - Mockito.verify(mNotificationAreaInner).setVisibility(eq(View.INVISIBLE)); + when(mOngoingCallController.hasOngoingCall()).thenReturn(false); - reset(mStatusBarStateController); - when(mStatusBarStateController.isDozing()).thenReturn(true); - fragment.onDozingChanged(true); + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); - Mockito.verify(mStatusBarStateController).isDozing(); - Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE)); + assertEquals(View.GONE, + mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); } @Test - public void onOngoingCallStarted_notificationsHiddenAndOngoingCallChipDisplayed() { + public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() { mFragments.dispatchResume(); processAllMessages(); - CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; - fragment.initNotificationIconArea(mMockNotificiationAreaController); - - ArgumentCaptor<OngoingCallListener> ongoingCallListenerCaptor = ArgumentCaptor.forClass( - OngoingCallListener.class); - Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture()); - OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue()); when(mOngoingCallController.hasOngoingCall()).thenReturn(true); - listener.onOngoingCallStarted(/* animate= */ false); + + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE)); + } @Test - public void onOngoingCallEnded_notificationsDisplayedAndOngoingCallChipHidden() { + public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() { mFragments.dispatchResume(); processAllMessages(); + CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; + + when(mOngoingCallController.hasOngoingCall()).thenReturn(true); + fragment.disable(DEFAULT_DISPLAY, + StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false); + + assertEquals(View.GONE, + mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + } + + @Test + public void disable_ongoingCallEnded_chipHidden() { + mFragments.dispatchResume(); + processAllMessages(); CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; - fragment.initNotificationIconArea(mMockNotificiationAreaController); - ArgumentCaptor<OngoingCallListener> ongoingCallListenerCaptor = ArgumentCaptor.forClass( - OngoingCallListener.class); - Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture()); - OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue()); + when(mOngoingCallController.hasOngoingCall()).thenReturn(true); + + // Ongoing call started + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); + assertEquals(View.VISIBLE, + mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + // Ongoing call ended when(mOngoingCallController.hasOngoingCall()).thenReturn(false); - listener.onOngoingCallEnded(/* animate= */ false); + + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); assertEquals(View.GONE, mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility()); + } + + @Test + public void testOnDozingChanged() throws Exception { + mFragments.dispatchResume(); + processAllMessages(); + CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; + + fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false); + + Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE)); + + reset(mStatusBarStateController); + when(mStatusBarStateController.isDozing()).thenReturn(true); + fragment.onDozingChanged(true); + + Mockito.verify(mStatusBarStateController).isDozing(); Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE)); } @@ -218,9 +225,30 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mOngoingCallController = mock(OngoingCallController.class); mAnimationScheduler = mock(SystemStatusAnimationScheduler.class); mDotViewController = mock(PrivacyDotViewController.class); + setUpNotificationIconAreaController(); return new CollapsedStatusBarFragment( mOngoingCallController, mAnimationScheduler, - mDotViewController); + mDotViewController, + mMockNotificationAreaController); + } + + private void setUpNotificationIconAreaController() { + mMockNotificationAreaController = mock(NotificationIconAreaController.class); + + mNotificationAreaInner = mock(View.class); + View centeredNotificationAreaView = mock(View.class); + + when(mNotificationAreaInner.getLayoutParams()).thenReturn( + new FrameLayout.LayoutParams(100, 100)); + when(centeredNotificationAreaView.getLayoutParams()).thenReturn( + new FrameLayout.LayoutParams(100, 100)); + when(mNotificationAreaInner.animate()).thenReturn(mock(ViewPropertyAnimator.class)); + when(centeredNotificationAreaView.animate()).thenReturn(mock(ViewPropertyAnimator.class)); + + when(mMockNotificationAreaController.getCenteredNotificationAreaView()).thenReturn( + centeredNotificationAreaView); + when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn( + mNotificationAreaInner); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 8633eb466b6c..7dcfc6b9ebf7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -49,8 +49,8 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; +import com.android.systemui.scrim.ScrimView; import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; @@ -69,6 +69,7 @@ import org.mockito.stubbing.Answer; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -91,7 +92,7 @@ public class ScrimControllerTest extends SysuiTestCase { @Mock private AlarmManager mAlarmManager; @Mock - private DozeParameters mDozeParamenters; + private DozeParameters mDozeParameters; @Mock LightBarController mLightBarController; @Mock @@ -200,8 +201,8 @@ public class ScrimControllerTest extends SysuiTestCase { return null; }).when(mScrimBehind).postOnAnimationDelayed(any(Runnable.class), anyLong()); - when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled); - when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true); + when(mDozeParameters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled); + when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true); doAnswer((Answer<Void>) invocation -> { mScrimState = invocation.getArgument(0); @@ -220,7 +221,7 @@ public class ScrimControllerTest extends SysuiTestCase { when(mDockManager.isDocked()).thenReturn(false); mScrimController = new ScrimController(mLightBarController, - mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder, + mDozeParameters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder, new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, mDockManager, mConfigurationController, mFeatureFlags, new FakeExecutor(new FakeSystemClock())); @@ -238,6 +239,10 @@ public class ScrimControllerTest extends SysuiTestCase { @After public void tearDown() { finishAnimationsImmediately(); + Arrays.stream(ScrimState.values()).forEach((scrim) -> { + scrim.setAodFrontScrimAlpha(0f); + scrim.setClipQsScrim(false); + }); DejankUtils.setImmediate(false); } @@ -259,9 +264,30 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void transitionToShadeLocked() { mScrimController.transitionTo(ScrimState.SHADE_LOCKED); + mScrimController.setQsPosition(1f, 0); finishAnimationsImmediately(); assertScrimAlpha(Map.of( + mNotificationsScrim, OPAQUE, + mScrimInFront, TRANSPARENT, + mScrimBehind, OPAQUE)); + + assertScrimTinted(Map.of( + mScrimInFront, false, + mScrimBehind, true, + mScrimForBubble, false + )); + } + + @Test + public void transitionToShadeLocked_clippingQs() { + mScrimController.setClipsQsScrim(true); + mScrimController.transitionTo(ScrimState.SHADE_LOCKED); + mScrimController.setQsPosition(1f, 0); + finishAnimationsImmediately(); + + assertScrimAlpha(Map.of( + mNotificationsScrim, OPAQUE, mScrimInFront, TRANSPARENT, mScrimBehind, OPAQUE)); @@ -403,9 +429,6 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.setAodFrontScrimAlpha(0.3f); Assert.assertEquals(ScrimState.AOD.getFrontAlpha(), mScrimInFront.getViewAlpha(), 0.001f); Assert.assertNotEquals(0.3f, mScrimInFront.getViewAlpha(), 0.001f); - - // Reset value since enums are static. - mScrimController.setAodFrontScrimAlpha(0f); } @Test @@ -500,11 +523,34 @@ public class ScrimControllerTest extends SysuiTestCase { // Back scrim should be visible without tint assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, mScrimBehind, OPAQUE)); assertScrimTinted(Map.of( mScrimInFront, false, mScrimBehind, false, + mNotificationsScrim, false, + mScrimForBubble, false + )); + } + + @Test + public void transitionToKeyguardBouncer_clippingQs() { + mScrimController.setClipsQsScrim(true); + mScrimController.transitionTo(ScrimState.BOUNCER); + finishAnimationsImmediately(); + // Front scrim should be transparent + // Back scrim should be clipping QS + // Notif scrim should be visible without tint + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, OPAQUE, + mScrimBehind, OPAQUE)); + + assertScrimTinted(Map.of( + mScrimInFront, false, + mScrimBehind, true, + mNotificationsScrim, false, mScrimForBubble, false )); } @@ -530,9 +576,11 @@ public class ScrimControllerTest extends SysuiTestCase { finishAnimationsImmediately(); assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, mScrimBehind, TRANSPARENT)); assertScrimTinted(Map.of( + mNotificationsScrim, false, mScrimInFront, false, mScrimBehind, true, mScrimForBubble, false @@ -542,6 +590,7 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.setPanelExpansion(0.5f); assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, + mNotificationsScrim, SEMI_TRANSPARENT, mScrimBehind, SEMI_TRANSPARENT)); } @@ -553,7 +602,7 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimTinted(Map.of( mScrimInFront, false, mScrimBehind, false, - mScrimForBubble, false + mScrimForBubble, true )); // Front scrim should be transparent @@ -617,6 +666,32 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test + public void qsExpansion_clippingQs() { + reset(mScrimBehind); + mScrimController.setClipsQsScrim(true); + mScrimController.setQsPosition(1f, 999 /* value doesn't matter */); + finishAnimationsImmediately(); + + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mScrimBehind, OPAQUE, + mNotificationsScrim, OPAQUE)); + } + + @Test + public void qsExpansion_half_clippingQs() { + reset(mScrimBehind); + mScrimController.setClipsQsScrim(true); + mScrimController.setQsPosition(0.5f, 999 /* value doesn't matter */); + finishAnimationsImmediately(); + + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mScrimBehind, OPAQUE, + mNotificationsScrim, SEMI_TRANSPARENT)); + } + + @Test public void panelExpansionAffectsAlpha() { mScrimController.setPanelExpansion(0f); mScrimController.setPanelExpansion(0.5f); @@ -919,13 +994,13 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void testAnimatesTransitionToAod() { - when(mDozeParamenters.shouldControlScreenOff()).thenReturn(false); + when(mDozeParameters.shouldControlScreenOff()).thenReturn(false); ScrimState.AOD.prepare(ScrimState.KEYGUARD); Assert.assertFalse("No animation when ColorFade kicks in", ScrimState.AOD.getAnimateChange()); - reset(mDozeParamenters); - when(mDozeParamenters.shouldControlScreenOff()).thenReturn(true); + reset(mDozeParameters); + when(mDozeParameters.shouldControlScreenOff()).thenReturn(true); ScrimState.AOD.prepare(ScrimState.KEYGUARD); Assert.assertTrue("Animate scrims when ColorFade won't be triggered", ScrimState.AOD.getAnimateChange()); @@ -977,6 +1052,7 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.setPanelExpansion(0.5f); // notifications scrim alpha change require calling setQsPosition mScrimController.setQsPosition(0, 300); + finishAnimationsImmediately(); assertScrimAlpha(Map.of( mScrimBehind, SEMI_TRANSPARENT, @@ -985,6 +1061,21 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test + public void testScrimsVisible_whenShadeVisible_clippingQs() { + mScrimController.setClipsQsScrim(true); + mScrimController.transitionTo(ScrimState.UNLOCKED); + mScrimController.setPanelExpansion(0.5f); + // notifications scrim alpha change require calling setQsPosition + mScrimController.setQsPosition(0.5f, 300); + finishAnimationsImmediately(); + + assertScrimAlpha(Map.of( + mScrimBehind, OPAQUE, + mNotificationsScrim, SEMI_TRANSPARENT, + mScrimInFront, TRANSPARENT)); + } + + @Test public void testScrimsVisible_whenShadeVisibleOnLockscreen() { mScrimController.transitionTo(ScrimState.KEYGUARD); mScrimController.setQsPosition(0.5f, 300); @@ -1008,8 +1099,6 @@ public class ScrimControllerTest extends SysuiTestCase { } private void assertScrimTinted(Map<ScrimView, Boolean> scrimToTint) { - // notifications scrim should have always transparent tint - assertScrimTint(mNotificationsScrim, false); scrimToTint.forEach((scrim, hasTint) -> assertScrimTint(scrim, hasTint)); } @@ -1047,6 +1136,12 @@ public class ScrimControllerTest extends SysuiTestCase { } scrimToAlpha.forEach((scrimView, alpha) -> assertScrimAlpha(scrimView, alpha)); + // When clipping, QS scrim should not affect combined visibility. + if (mScrimController.getClipQsScrim() && scrimToAlpha.get(mScrimBehind) == OPAQUE) { + scrimToAlpha = new HashMap<>(scrimToAlpha); + scrimToAlpha.remove(mScrimBehind); + } + // Check combined scrim visibility. final int visibility; if (scrimToAlpha.values().contains(OPAQUE)) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index 60e70d01edc8..2d9d7154d229 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -228,6 +228,18 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM); verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); + + // Regression test: null events should not reset the internal state and allow colors to be + // applied again. + clearInvocations(mThemeOverlayApplier); + mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED)); + mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM); + verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(), + any()); + mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN), + null, null), WallpaperManager.FLAG_SYSTEM); + verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(), + any()); } @Test diff --git a/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp b/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp index a18ebb3eaea5..690d0a0ecda3 100644 --- a/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp @@ -26,6 +26,5 @@ package { runtime_resource_overlay { name: "IconPackVictorThemePickerOverlay", theme: "IconPackVictorThemePicker", - certificate: "platform", product_specific: true, } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 57e5bb2d521f..f1dcdffe1397 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -450,7 +450,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } mPendingInlineSuggestionsRequest = inlineSuggestionsRequest; - maybeRequestFillLocked(); + maybeRequestFillFromServiceLocked(); viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); } } : null; @@ -462,7 +462,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPendingInlineSuggestionsRequest = inlineRequest; } - void maybeRequestFillLocked() { + void maybeRequestFillFromServiceLocked() { if (mPendingFillRequest == null) { return; } @@ -472,9 +472,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), - mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(), - mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest); + if (mPendingInlineSuggestionsRequest.isServiceSupported()) { + mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), + mPendingFillRequest.getFillContexts(), + mPendingFillRequest.getClientState(), + mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest); + } } mRemoteFillService.onFillRequest(mPendingFillRequest); @@ -581,7 +584,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /*inlineSuggestionsRequest=*/null); mPendingFillRequest = request; - maybeRequestFillLocked(); + maybeRequestFillFromServiceLocked(); } if (mActivityToken != null) { @@ -3188,6 +3191,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return false; } + final InlineSuggestionsRequest request = inlineSuggestionsRequest.get(); + if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported() + || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) { + if (sDebug) { + Slog.d(TAG, "Inline suggestions not supported for " + + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service") + + ". Falling back to dropdown."); + } + return false; + } + final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); if (remoteRenderService == null) { @@ -3196,7 +3210,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } final InlineFillUi.InlineFillUiInfo inlineFillUiInfo = - new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId, + new InlineFillUi.InlineFillUiInfo(request, focusedId, filterText, remoteRenderService, userId, id); InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response, new InlineFillUi.InlineSuggestionUiCallback() { @@ -3809,6 +3823,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mContexts = new ArrayList<>(1); } + if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) { + inlineSuggestionsRequest = null; + } + mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags); } diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 08c9c2a2c97d..c5246c7073ee 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -18,16 +18,20 @@ package com.android.server; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.content.PermissionChecker.PERMISSION_HARD_DENIED; +import static android.content.PermissionChecker.PID_UNKNOWN; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.os.UserHandle.USER_SYSTEM; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHearingAid; @@ -42,6 +46,7 @@ import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; import android.content.ActivityNotFoundException; +import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -62,6 +67,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerExemptionManager; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -302,13 +308,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS); } - public boolean onFactoryReset() { + public boolean onFactoryReset(AttributionSource attributionSource) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); final long token = Binder.clearCallingIdentity(); try { - return onFactoryResetInternal(); + return onFactoryResetInternal(attributionSource); } finally { Binder.restoreCallingIdentity(token); } @@ -318,7 +324,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) - private boolean onFactoryResetInternal() { + private boolean onFactoryResetInternal(AttributionSource attributionSource) { // Wait for stable state if bluetooth is temporary state. int state = getState(); if (state == BluetoothAdapter.STATE_BLE_TURNING_ON @@ -347,7 +353,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { addActiveLog( BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, mContext.getPackageName(), false); - mBluetooth.disable(mContext.getAttributionSource()); + mBluetooth.disable(attributionSource); return true; } } catch (RemoteException e) { @@ -943,7 +949,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private boolean checkBluetoothPermissions(String packageName, boolean requireForeground) { + private boolean checkBluetoothPermissions(AttributionSource attributionSource, String message, + boolean requireForeground) { if (isBluetoothDisallowed()) { if (DBG) { Slog.d(TAG, "checkBluetoothPermissions: bluetooth disallowed"); @@ -954,22 +961,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { final int callingUid = Binder.getCallingUid(); final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; if (!isCallerSystem) { - checkPackage(callingUid, packageName); + checkPackage(callingUid, attributionSource.getPackageName()); if (requireForeground && !checkIfCallerIsForegroundUser()) { Slog.w(TAG, "Not allowed for non-active and non system user"); return false; } - if (!checkConnectPermissionForPreflight(mContext)) { + if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, message)) { return false; } } return true; } - public boolean enableBle(String packageName, IBinder token) throws RemoteException { - if (!checkBluetoothPermissions(packageName, false)) { + public boolean enableBle(AttributionSource attributionSource, IBinder token) + throws RemoteException { + final String packageName = attributionSource.getPackageName(); + if (!checkBluetoothPermissions(attributionSource, "enableBle", false)) { if (DBG) { Slog.d(TAG, "enableBle(): bluetooth disallowed"); } @@ -999,8 +1008,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean disableBle(String packageName, IBinder token) throws RemoteException { - if (!checkBluetoothPermissions(packageName, false)) { + public boolean disableBle(AttributionSource attributionSource, IBinder token) + throws RemoteException { + final String packageName = attributionSource.getPackageName(); + if (!checkBluetoothPermissions(attributionSource, "disableBle", false)) { if (DBG) { Slog.d(TAG, "disableBLE(): bluetooth disallowed"); } @@ -1099,7 +1110,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (isBleAppPresent()) { // Need to stay at BLE ON. Disconnect all Gatt connections try { - mBluetoothGatt.unregAll(); + mBluetoothGatt.unregAll(mContext.getAttributionSource()); } catch (RemoteException e) { Slog.e(TAG, "Unable to disconnect all apps.", e); } @@ -1118,8 +1129,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } - public boolean enableNoAutoConnect(String packageName) { - if (!checkBluetoothPermissions(packageName, false)) { + public boolean enableNoAutoConnect(AttributionSource attributionSource) { + final String packageName = attributionSource.getPackageName(); + if (!checkBluetoothPermissions(attributionSource, "enableNoAutoConnect", false)) { if (DBG) { Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed"); } @@ -1145,8 +1157,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean enable(String packageName) throws RemoteException { - if (!checkBluetoothPermissions(packageName, true)) { + public boolean enable(AttributionSource attributionSource) throws RemoteException { + final String packageName = attributionSource.getPackageName(); + if (!checkBluetoothPermissions(attributionSource, "enable", true)) { if (DBG) { Slog.d(TAG, "enable(): not enabling - bluetooth disallowed"); } @@ -1179,8 +1192,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean disable(String packageName, boolean persist) throws RemoteException { - if (!checkBluetoothPermissions(packageName, true)) { + public boolean disable(AttributionSource attributionSource, boolean persist) + throws RemoteException { + final String packageName = attributionSource.getPackageName(); + if (!checkBluetoothPermissions(attributionSource, "disable", true)) { if (DBG) { Slog.d(TAG, "disable(): not disabling - bluetooth disallowed"); } @@ -1696,8 +1711,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - public String getAddress() { - if (!checkConnectPermissionForPreflight(mContext)) { + public String getAddress(AttributionSource attributionSource) { + if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getAddress")) { return null; } @@ -1730,8 +1745,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return mAddress; } - public String getName() { - if (!checkConnectPermissionForPreflight(mContext)) { + public String getName(AttributionSource attributionSource) { + if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getName")) { return null; } @@ -2497,7 +2512,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, getTempAllowlistBroadcastOptions()); } @RequiresPermission(allOf = { @@ -2580,7 +2595,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, + getTempAllowlistBroadcastOptions()); } } @@ -2874,6 +2890,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + private static boolean checkPermissionForDataDelivery(Context context, String permission, + AttributionSource attributionSource, String message) { + final int result = PermissionChecker.checkPermissionForDataDeliveryFromDataSource( + context, permission, PID_UNKNOWN, + new AttributionSource(context.getAttributionSource(), attributionSource), message); + if (result == PERMISSION_GRANTED) { + return true; + } + + final String msg = "Need " + permission + " permission for " + attributionSource + ": " + + message; + if (result == PERMISSION_HARD_DENIED) { + throw new SecurityException(msg); + } else { + Log.w(TAG, msg); + return false; + } + } + /** * Returns true if the BLUETOOTH_CONNECT permission is granted for the calling app. Returns * false if the result is a soft denial. Throws SecurityException if the result is a hard @@ -2882,12 +2917,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * <p>Should be used in situations where the app op should not be noted. */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private static boolean checkConnectPermissionForPreflight(Context context) { - int permissionCheckResult = PermissionChecker.checkCallingOrSelfPermissionForPreflight( - context, BLUETOOTH_CONNECT); - if (permissionCheckResult == PERMISSION_HARD_DENIED) { - throw new SecurityException("Need BLUETOOTH_CONNECT permission"); - } - return permissionCheckResult == PERMISSION_GRANTED; + public static boolean checkConnectPermissionForDataDelivery( + Context context, AttributionSource attributionSource, String message) { + return checkPermissionForDataDelivery(context, BLUETOOTH_CONNECT, + attributionSource, message); + } + + static @NonNull Bundle getTempAllowlistBroadcastOptions() { + final long duration = 10_000; + final BroadcastOptions bOptions = BroadcastOptions.makeBasic(); + bOptions.setTemporaryAppAllowlist(duration, + TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + PowerExemptionManager.REASON_BLUETOOTH_BROADCAST, ""); + return bOptions.toBundle(); } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 667a99d50828..bfe51c2521df 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -8765,7 +8765,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (vpn == null) return VpnManager.TYPE_VPN_NONE; final TransportInfo ti = vpn.networkCapabilities.getTransportInfo(); if (!(ti instanceof VpnTransportInfo)) return VpnManager.TYPE_VPN_NONE; - return ((VpnTransportInfo) ti).type; + return ((VpnTransportInfo) ti).getType(); } /** @@ -9694,7 +9694,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // request. final ArrayList<NetworkRequest> nrs = new ArrayList<>(); nrs.add(createNetworkRequest(NetworkRequest.Type.REQUEST, pref.capabilities)); - nrs.add(createDefaultRequest()); + nrs.add(createDefaultInternetRequestForTransport( + TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT)); setNetworkRequestUids(nrs, UidRange.fromIntRanges(pref.capabilities.getUids())); final NetworkRequestInfo nri = new NetworkRequestInfo(Process.myUid(), nrs); result.add(nri); @@ -9999,7 +10000,8 @@ public class ConnectivityService extends IConnectivityManager.Stub case OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID: requests.add(createUnmeteredNetworkRequest()); requests.add(createOemPaidNetworkRequest()); - requests.add(createDefaultRequest()); + requests.add(createDefaultInternetRequestForTransport( + TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT)); break; case OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK: requests.add(createUnmeteredNetworkRequest()); diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index 08bff810b0ce..8561042a5056 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -935,9 +935,7 @@ public final class PinnerService extends SystemService { int mapSize = 0; try { - int openFlags = (OsConstants.O_RDONLY | - OsConstants.O_CLOEXEC | - OsConstants.O_NOFOLLOW); + int openFlags = (OsConstants.O_RDONLY | OsConstants.O_CLOEXEC); fd = Os.open(fileToPin, openFlags, 0); mapSize = (int) Math.min(Os.fstat(fd).st_size, Integer.MAX_VALUE); address = Os.mmap(0, mapSize, diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index ee3530a9eac8..edf832f0fc22 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -2072,7 +2072,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private void notifyCellLocationForSubscriber(int subId, CellIdentity cellIdentity, boolean hasUserSwitched) { - log("notifyCellLocationForSubscriber: subId=" + subId + " cellIdentity=" + cellIdentity); + log("notifyCellLocationForSubscriber: subId=" + subId + " cellIdentity=" + + Rlog.pii(DBG || VDBG || DBG_LOC, cellIdentity)); if (!checkNotifyPermission("notifyCellLocation()")) { return; } diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 915517a4b9ce..d8af01eb7bda 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -1779,7 +1779,7 @@ final class UiModeManagerService extends SystemService { // TODO(b/173744200) Please replace FLAG_MUTABLE_UNAUDITED below // with either FLAG_IMMUTABLE (recommended) or FLAG_MUTABLE. PendingIntent.getActivityAsUser(context, 0, - carModeOffIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED, + carModeOffIntent, PendingIntent.FLAG_MUTABLE, null, UserHandle.CURRENT)); mNotificationManager.notifyAsUser(null, SystemMessage.NOTE_CAR_MODE_DISABLE, n.build(), UserHandle.ALL); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 10cf1846da6f..206f135aef99 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -70,6 +70,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE_E import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UptimeMillisLong; @@ -1815,6 +1816,7 @@ public final class ActiveServices { notification.flags |= Notification.FLAG_FOREGROUND_SERVICE; r.foregroundNoti = notification; r.foregroundServiceType = foregroundServiceType; + boolean enterForeground = false; if (!r.isForeground) { final ServiceMap smap = getServiceMapLocked(r.userId); if (smap != null) { @@ -1840,6 +1842,7 @@ public final class ActiveServices { active.mNumActive++; } r.isForeground = true; + enterForeground = true; r.mStartForegroundCount++; r.mFgsEnterTime = SystemClock.uptimeMillis(); if (!stopProcStatsOp) { @@ -1851,16 +1854,23 @@ public final class ActiveServices { } else { stopProcStatsOp = false; } - postFgsNotificationLocked(r); + mAm.mAppOpsService.startOperation( AppOpsManager.getToken(mAm.mAppOpsService), AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null, true, false, "", false); + registerAppOpCallbackLocked(r); + mAm.updateForegroundServiceUsageStats(r.name, r.userId, true); + } + // Even if the service is already a FGS, we need to update the notification, + // so we need to call it again. + postFgsNotificationLocked(r); + if (enterForeground) { + // Because we want to log what's updated in postFgsNotificationLocked(), + // this must be called after postFgsNotificationLocked(). logForegroundServiceStateChanged(r, FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER, 0); - registerAppOpCallbackLocked(r); - mAm.updateForegroundServiceUsageStats(r.name, r.userId, true); } if (r.app != null) { updateServiceForegroundLocked(psr, true); @@ -3069,6 +3079,18 @@ public final class ActiveServices { + ", uid=" + callingUid + " requires " + r.permission); return new ServiceLookupResult(null, r.permission); + } else if (Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE.equals(r.permission) + && callingUid != Process.SYSTEM_UID) { + // Hotword detection must run in its own sandbox, and we don't even trust + // its enclosing application to bind to it - only the system. + // TODO(b/185746653) remove this special case and generalize + Slog.w(TAG, "Permission Denial: Accessing service " + r.shortInstanceName + + " from pid=" + callingPid + + ", uid=" + callingUid + + " requiring permission " + r.permission + + " can only be bound to from the system."); + return new ServiceLookupResult(null, "can only be bound to " + + "by the system."); } else if (r.permission != null && callingPackage != null) { final int opCode = AppOpsManager.permissionToOpCode(r.permission); if (opCode != AppOpsManager.OP_NONE && mAm.getAppOpsManager().checkOpNoThrow( @@ -5911,6 +5933,10 @@ public final class ActiveServices { * @param durationMs Only meaningful for EXIT event, the duration from ENTER and EXIT state. */ private void logForegroundServiceStateChanged(ServiceRecord r, int state, int durationMs) { + if (!ActivityManagerUtils.shouldSamplePackageForAtom( + r.packageName, mAm.mConstants.mDefaultFgsAtomSampleRate)) { + return; + } FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortInstanceName, diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index c8363dd59005..bf574521b895 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -97,6 +97,7 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION = "boot_time_temp_allowlist_duration"; static final String KEY_FG_TO_BG_FGS_GRACE_DURATION = "fg_to_bg_fgs_grace_duration"; static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout"; + static final String KEY_FGS_ATOM_SAMPLE_RATE = "fgs_atom_sample_rate"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000; @@ -137,6 +138,7 @@ final class ActivityManagerConstants extends ContentObserver { private static final int DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION = 10 * 1000; private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000; private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000; + private static final float DEFAULT_FGS_ATOM_SAMPLE_RATE = 1; // 100 % // Flag stored in the DeviceConfig API. /** @@ -430,6 +432,13 @@ final class ActivityManagerConstants extends ContentObserver { */ volatile long mFgsStartForegroundTimeoutMs = DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS; + /** + * Sample rate for the FGS westworld atom. + * + * If the value is 0.1, 10% of the installed packages would be sampled. + */ + volatile float mDefaultFgsAtomSampleRate = DEFAULT_FGS_ATOM_SAMPLE_RATE; + private final ActivityManagerService mService; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -629,6 +638,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_FGS_START_FOREGROUND_TIMEOUT: updateFgsStartForegroundTimeout(); break; + case KEY_FGS_ATOM_SAMPLE_RATE: + updateFgsAtomSamplePercent(); + break; default: break; } @@ -933,6 +945,13 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS); } + private void updateFgsAtomSamplePercent() { + mDefaultFgsAtomSampleRate = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FGS_ATOM_SAMPLE_RATE, + DEFAULT_FGS_ATOM_SAMPLE_RATE); + } + private void updateImperceptibleKillExemptions() { IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.clear(); IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages); @@ -1145,6 +1164,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.println(mFlagFgsStartRestrictionEnabled); pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK); pw.print("="); pw.println(mFgsStartRestrictionCheckCallerTargetSdk); + pw.print(" "); pw.print(KEY_FGS_ATOM_SAMPLE_RATE); + pw.print("="); pw.println(mDefaultFgsAtomSampleRate); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3c2702de75d5..00b13b1bb6b0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3525,7 +3525,7 @@ public class ActivityManagerService extends IActivityManager.Stub JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class); // Clearing data is a user-initiated action. js.cancelJobsForUid(appInfo.uid, JobParameters.STOP_REASON_USER, - JobParameters.DEBUG_REASON_DATA_CLEARED, "clear data"); + JobParameters.INTERNAL_STOP_REASON_DATA_CLEARED, "clear data"); // Clear its pending alarms AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class); diff --git a/services/core/java/com/android/server/am/ActivityManagerUtils.java b/services/core/java/com/android/server/am/ActivityManagerUtils.java new file mode 100644 index 000000000000..dd2414866542 --- /dev/null +++ b/services/core/java/com/android/server/am/ActivityManagerUtils.java @@ -0,0 +1,124 @@ +/* + * 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.am; + +import android.app.ActivityThread; +import android.provider.Settings; +import android.util.ArrayMap; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * To store random utility methods... + */ +public class ActivityManagerUtils { + private ActivityManagerUtils() { + } + + private static Integer sAndroidIdHash; + + @GuardedBy("sHashCache") + private static final ArrayMap<String, Integer> sHashCache = new ArrayMap<>(); + + private static String sInjectedAndroidId; + + /** Used by the unit tests to inject an android ID. Do not set in the prod code. */ + @VisibleForTesting + static void injectAndroidIdForTest(String androidId) { + sInjectedAndroidId = androidId; + sAndroidIdHash = null; + } + + /** + * Return a hash between [0, MAX_VALUE] generated from the android ID. + */ + @VisibleForTesting + static int getAndroidIdHash() { + // No synchronization is required. Double-initialization is fine here. + if (sAndroidIdHash == null) { + final String androidId = Settings.Secure.getString( + ActivityThread.currentApplication().getContentResolver(), + Settings.Secure.ANDROID_ID); + sAndroidIdHash = getUnsignedHashUnCached( + sInjectedAndroidId != null ? sInjectedAndroidId : androidId); + } + return sAndroidIdHash; + } + + /** + * Return a hash between [0, MAX_VALUE] generated from a package name, using a cache. + * + * Because all the results are cached, do not use it for dynamically generated strings. + */ + @VisibleForTesting + static int getUnsignedHashCached(String s) { + synchronized (sHashCache) { + final Integer cached = sHashCache.get(s); + if (cached != null) { + return cached; + } + final int hash = getUnsignedHashUnCached(s); + sHashCache.put(s.intern(), hash); + return hash; + } + } + + /** + * Return a hash between [0, MAX_VALUE] generated from a package name. + */ + private static int getUnsignedHashUnCached(String s) { + try { + final MessageDigest digest = MessageDigest.getInstance("SHA-1"); + digest.update(s.getBytes()); + return unsignedIntFromBytes(digest.digest()); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + @VisibleForTesting + static int unsignedIntFromBytes(byte[] longEnoughBytes) { + return (extractByte(longEnoughBytes, 0) + | extractByte(longEnoughBytes, 1) + | extractByte(longEnoughBytes, 2) + | extractByte(longEnoughBytes, 3)) + & 0x7FFF_FFFF; + } + + private static int extractByte(byte[] bytes, int index) { + return (((int) bytes[index]) & 0xFF) << (index * 8); + } + + /** + * @return whether a package should be logged, using a random value based on the ANDROID_ID, + * with a given sampling rate. + */ + public static boolean shouldSamplePackageForAtom(String packageName, float rate) { + if (rate <= 0) { + return false; + } + if (rate >= 1) { + return true; + } + final int hash = getUnsignedHashCached(packageName) ^ getAndroidIdHash(); + + return (((double) hash) / Integer.MAX_VALUE) <= rate; + } +} diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index fae941d172a2..774825ee885c 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -77,6 +77,7 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManagerInternal; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Point; import android.net.LocalSocket; @@ -4643,6 +4644,12 @@ public final class ProcessList { } }); } + + // Update the global configuration and increase the assets sequence number. + Configuration currentConfig = mService.mActivityTaskManager.getConfiguration(); + Configuration newConfig = new Configuration(); + newConfig.assetsSeq = (currentConfig != null ? currentConfig.assetsSeq : 0) + 1; + mService.mActivityTaskManager.updateConfiguration(newConfig); } @GuardedBy("mService") diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index 88e47a0efd83..7b50218cfad6 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -337,7 +337,7 @@ public class AuthService extends SystemService { } @Override - public long[] getAuthenticatorIds() throws RemoteException { + public long[] getAuthenticatorIds(int userId) throws RemoteException { // In this method, we're not checking whether the caller is permitted to use face // API because current authenticator ID is leaked (in a more contrived way) via Android // Keystore (android.security.keystore package): the user of that API can create a key @@ -355,9 +355,13 @@ public class AuthService extends SystemService { // method from inside app processes. final int callingUserId = UserHandle.getCallingUserId(); + if (userId != callingUserId) { + getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL, + "Must have " + USE_BIOMETRIC_INTERNAL + " permission."); + } final long identity = Binder.clearCallingIdentity(); try { - return mBiometricService.getAuthenticatorIds(callingUserId); + return mBiometricService.getAuthenticatorIds(userId); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java index f7443740943a..fbf249237415 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -17,6 +17,7 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricConstants; import android.media.AudioAttributes; @@ -26,6 +27,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.VibrationEffect; import android.os.Vibrator; +import android.text.TextUtils; import android.util.Slog; /** @@ -43,6 +45,15 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build(); + private final VibrationEffect mEffectTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK); + private final VibrationEffect mEffectTextureTick = + VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK); + private final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + private final VibrationEffect mEffectHeavy = + VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); + private final VibrationEffect mDoubleClick = + VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); + private final PowerManager mPowerManager; private final VibrationEffect mSuccessVibrationEffect; private final VibrationEffect mErrorVibrationEffect; @@ -61,8 +72,8 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, statsModality, statsAction, statsClient); mPowerManager = context.getSystemService(PowerManager.class); - mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); - mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); + mSuccessVibrationEffect = mEffectClick; + mErrorVibrationEffect = mDoubleClick; } @Override @@ -181,18 +192,47 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); } + protected @NonNull VibrationEffect getSuccessVibrationEffect() { + return mSuccessVibrationEffect; + } + + protected @NonNull VibrationEffect getErrorVibrationEffect() { + return mErrorVibrationEffect; + } protected final void vibrateSuccess() { Vibrator vibrator = getContext().getSystemService(Vibrator.class); if (vibrator != null) { - vibrator.vibrate(mSuccessVibrationEffect, VIBRATION_SONFICATION_ATTRIBUTES); + vibrator.vibrate(getSuccessVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES); } } protected final void vibrateError() { Vibrator vibrator = getContext().getSystemService(Vibrator.class); if (vibrator != null) { - vibrator.vibrate(mErrorVibrationEffect, VIBRATION_SONFICATION_ATTRIBUTES); + vibrator.vibrate(getErrorVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES); + } + } + + protected final @NonNull VibrationEffect getVibration(@Nullable String effect, + @NonNull VibrationEffect defaultEffect) { + if (TextUtils.isEmpty(effect)) { + return defaultEffect; + } + + switch (effect.toLowerCase()) { + case "click": + return mEffectClick; + case "heavy": + return mEffectHeavy; + case "texture_tick": + return mEffectTextureTick; + case "tick": + return mEffectTick; + case "double_click": + return mDoubleClick; + default: + return defaultEffect; } } } 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 cf545f37bd43..86688287cc9d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -22,6 +22,7 @@ import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.TaskStackListener; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.hardware.biometrics.BiometricAuthenticator; @@ -30,6 +31,8 @@ import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; import android.os.RemoteException; +import android.os.VibrationEffect; +import android.provider.Settings; import android.security.KeyStore; import android.util.EventLog; import android.util.Slog; @@ -56,12 +59,14 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> private final LockoutTracker mLockoutTracker; private final boolean mIsRestricted; private final boolean mAllowBackgroundAuthentication; + @NonNull private final ContentResolver mContentResolver; protected final long mOperationId; private long mStartTimeMs; protected boolean mAuthAttempted; + private final boolean mCustomHaptics; public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, @@ -80,6 +85,10 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> mLockoutTracker = lockoutTracker; mIsRestricted = restricted; mAllowBackgroundAuthentication = allowBackgroundAuthentication; + + mContentResolver = context.getContentResolver(); + mCustomHaptics = Settings.Global.getInt(mContentResolver, + "fp_custom_success_error", 0) == 1; } public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) { @@ -333,4 +342,25 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> public boolean interruptsPrecedingClients() { return true; } + + @Override + protected @NonNull VibrationEffect getSuccessVibrationEffect() { + if (!mCustomHaptics) { + return super.getSuccessVibrationEffect(); + } + + return getVibration(Settings.Global.getString(mContentResolver, + "fp_success_type"), super.getSuccessVibrationEffect()); + } + + @Override + protected @NonNull VibrationEffect getErrorVibrationEffect() { + if (!mCustomHaptics) { + return super.getErrorVibrationEffect(); + } + + return getVibration(Settings.Global.getString(mContentResolver, + "fp_error_type"), super.getErrorVibrationEffect()); + + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index f8a2156ce238..aa507901ab78 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -471,7 +471,7 @@ public class BiometricScheduler { * Adds a {@link BaseClientMonitor} to the pending queue * * @param clientMonitor operation to be scheduled - * @param clientCallback optional callback, invoked when the client state changes + * @param clientCallback optional callback, invoked when the client state changes. */ public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor, @Nullable BaseClientMonitor.Callback clientCallback) { diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java index 25b7add0a7d8..d82847c95dce 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java @@ -146,9 +146,10 @@ public class ClientMonitorCallbackConverter { } } - public void onFeatureGet(boolean success, int feature, boolean value) throws RemoteException { + public void onFeatureGet(boolean success, int[] features, boolean[] featureState) + throws RemoteException { if (mFaceServiceReceiver != null) { - mFaceServiceReceiver.onFeatureGet(success, feature, value); + mFaceServiceReceiver.onFeatureGet(success, features, featureState); } } 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 index 769c47a94ae3..5d713f35f925 100644 --- 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 @@ -18,52 +18,131 @@ package com.android.server.biometrics.sensors.face.aidl; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.biometrics.BiometricFaceConstants; +import android.hardware.biometrics.face.AcquiredInfo; 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.biometrics.face.Error; 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. + * Utilities for converting from hardware to framework-defined AIDL models. */ final class AidlConversionUtils { // Prevent instantiation. - private AidlConversionUtils() {} + private AidlConversionUtils() { + } - @NonNull - public static FaceAuthenticationFrame convert(@NonNull AuthenticationFrame frame) { - return new FaceAuthenticationFrame(convert(frame.data)); + public static @BiometricFaceConstants.FaceError int toFrameworkError(byte aidlError) { + if (aidlError == Error.UNKNOWN) { + // No framework constant available + return BiometricFaceConstants.FACE_ERROR_UNKNOWN; + } else if (aidlError == Error.HW_UNAVAILABLE) { + return BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE; + } else if (aidlError == Error.UNABLE_TO_PROCESS) { + return BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS; + } else if (aidlError == Error.TIMEOUT) { + return BiometricFaceConstants.FACE_ERROR_TIMEOUT; + } else if (aidlError == Error.NO_SPACE) { + return BiometricFaceConstants.FACE_ERROR_NO_SPACE; + } else if (aidlError == Error.CANCELED) { + return BiometricFaceConstants.FACE_ERROR_CANCELED; + } else if (aidlError == Error.UNABLE_TO_REMOVE) { + return BiometricFaceConstants.FACE_ERROR_UNABLE_TO_REMOVE; + } else if (aidlError == Error.VENDOR) { + return BiometricFaceConstants.FACE_ERROR_VENDOR; + } else if (aidlError == Error.REENROLL_REQUIRED) { + return BiometricFaceConstants.BIOMETRIC_ERROR_RE_ENROLL; + } else { + return BiometricFaceConstants.FACE_ERROR_UNKNOWN; + } } - @NonNull - public static AuthenticationFrame convert(@NonNull FaceAuthenticationFrame frame) { - final AuthenticationFrame convertedFrame = new AuthenticationFrame(); - convertedFrame.data = convert(frame.getData()); - return convertedFrame; + public static @BiometricFaceConstants.FaceAcquired int toFrameworkAcquiredInfo( + byte aidlAcquired) { + if (aidlAcquired == AcquiredInfo.UNKNOWN) { + return BiometricFaceConstants.FACE_ACQUIRED_UNKNOWN; + } else if (aidlAcquired == AcquiredInfo.GOOD) { + return BiometricFaceConstants.FACE_ACQUIRED_GOOD; + } else if (aidlAcquired == AcquiredInfo.INSUFFICIENT) { + return BiometricFaceConstants.FACE_ACQUIRED_INSUFFICIENT; + } else if (aidlAcquired == AcquiredInfo.TOO_BRIGHT) { + return BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT; + } else if (aidlAcquired == AcquiredInfo.TOO_DARK) { + return BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK; + } else if (aidlAcquired == AcquiredInfo.TOO_CLOSE) { + return BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE; + } else if (aidlAcquired == AcquiredInfo.TOO_FAR) { + return BiometricFaceConstants.FACE_ACQUIRED_TOO_FAR; + } else if (aidlAcquired == AcquiredInfo.FACE_TOO_HIGH) { + return BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH; + } else if (aidlAcquired == AcquiredInfo.FACE_TOO_LOW) { + return BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW; + } else if (aidlAcquired == AcquiredInfo.FACE_TOO_RIGHT) { + return BiometricFaceConstants.FACE_ACQUIRED_TOO_RIGHT; + } else if (aidlAcquired == AcquiredInfo.FACE_TOO_LEFT) { + return BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT; + } else if (aidlAcquired == AcquiredInfo.POOR_GAZE) { + return BiometricFaceConstants.FACE_ACQUIRED_POOR_GAZE; + } else if (aidlAcquired == AcquiredInfo.NOT_DETECTED) { + return BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED; + } else if (aidlAcquired == AcquiredInfo.TOO_MUCH_MOTION) { + return BiometricFaceConstants.FACE_ACQUIRED_TOO_MUCH_MOTION; + } else if (aidlAcquired == AcquiredInfo.RECALIBRATE) { + return BiometricFaceConstants.FACE_ACQUIRED_RECALIBRATE; + } else if (aidlAcquired == AcquiredInfo.TOO_DIFFERENT) { + return BiometricFaceConstants.FACE_ACQUIRED_TOO_DIFFERENT; + } else if (aidlAcquired == AcquiredInfo.TOO_SIMILAR) { + return BiometricFaceConstants.FACE_ACQUIRED_TOO_SIMILAR; + } else if (aidlAcquired == AcquiredInfo.PAN_TOO_EXTREME) { + return BiometricFaceConstants.FACE_ACQUIRED_PAN_TOO_EXTREME; + } else if (aidlAcquired == AcquiredInfo.TILT_TOO_EXTREME) { + return BiometricFaceConstants.FACE_ACQUIRED_TILT_TOO_EXTREME; + } else if (aidlAcquired == AcquiredInfo.ROLL_TOO_EXTREME) { + return BiometricFaceConstants.FACE_ACQUIRED_ROLL_TOO_EXTREME; + } else if (aidlAcquired == AcquiredInfo.FACE_OBSCURED) { + return BiometricFaceConstants.FACE_ACQUIRED_FACE_OBSCURED; + } else if (aidlAcquired == AcquiredInfo.START) { + return BiometricFaceConstants.FACE_ACQUIRED_START; + } else if (aidlAcquired == AcquiredInfo.SENSOR_DIRTY) { + return BiometricFaceConstants.FACE_ACQUIRED_SENSOR_DIRTY; + } else if (aidlAcquired == AcquiredInfo.VENDOR) { + return BiometricFaceConstants.FACE_ACQUIRED_VENDOR; + } else if (aidlAcquired == AcquiredInfo.FIRST_FRAME_RECEIVED) { + // No framework constant available + return BiometricFaceConstants.FACE_ACQUIRED_UNKNOWN; + } else if (aidlAcquired == AcquiredInfo.DARK_GLASSES_DETECTED) { + // No framework constant available + return BiometricFaceConstants.FACE_ACQUIRED_UNKNOWN; + } else if (aidlAcquired == AcquiredInfo.MOUTH_COVERING_DETECTED) { + // No framework constant available + return BiometricFaceConstants.FACE_ACQUIRED_UNKNOWN; + } else { + return BiometricFaceConstants.FACE_ACQUIRED_UNKNOWN; + } } @NonNull - public static FaceEnrollFrame convert(@NonNull EnrollmentFrame frame) { - return new FaceEnrollFrame(convert(frame.cell), frame.stage, convert(frame.data)); + public static FaceAuthenticationFrame toFrameworkAuthenticationFrame( + @NonNull AuthenticationFrame frame) { + return new FaceAuthenticationFrame(toFrameworkBaseFrame(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; + public static FaceEnrollFrame toFrameworkEnrollmentFrame(@NonNull EnrollmentFrame frame) { + return new FaceEnrollFrame(toFrameworkCell(frame.cell), frame.stage, + toFrameworkBaseFrame(frame.data)); } @NonNull - public static FaceDataFrame convert(@NonNull BaseFrame frame) { + public static FaceDataFrame toFrameworkBaseFrame(@NonNull BaseFrame frame) { return new FaceDataFrame( - frame.acquiredInfo, + toFrameworkAcquiredInfo(frame.acquiredInfo), frame.vendorCode, frame.pan, frame.tilt, @@ -71,33 +150,8 @@ final class AidlConversionUtils { 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) { + public static FaceEnrollCell toFrameworkCell(@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/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java index ca9be67914e3..87269237bc85 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java @@ -105,7 +105,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { } @Override - public void onFeatureGet(boolean success, int feature, boolean value) { + public void onFeatureGet(boolean success, int[] features, boolean[] featureState) { } 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 0c883b047fe4..d04c17cdb6d3 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 @@ -19,6 +19,7 @@ package com.android.server.biometrics.sensors.face.aidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.NotificationManager; +import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.hardware.biometrics.BiometricAuthenticator; @@ -32,6 +33,8 @@ import android.hardware.face.FaceAuthenticationFrame; import android.hardware.face.FaceManager; import android.os.IBinder; import android.os.RemoteException; +import android.os.VibrationEffect; +import android.provider.Settings; import android.util.Slog; import com.android.internal.R; @@ -57,6 +60,9 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements @Nullable private final NotificationManager mNotificationManager; @Nullable private ICancellationSignal mCancellationSignal; + @NonNull private final ContentResolver mContentResolver; + private final boolean mCustomHaptics; + private final int[] mBiometricPromptIgnoreList; private final int[] mBiometricPromptIgnoreListVendor; private final int[] mKeyguardIgnoreList; @@ -87,6 +93,10 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements R.array.config_face_acquire_keyguard_ignorelist); mKeyguardIgnoreListVendor = resources.getIntArray( R.array.config_face_acquire_vendor_keyguard_ignorelist); + + mContentResolver = context.getContentResolver(); + mCustomHaptics = Settings.Global.getInt(mContentResolver, + "face_custom_success_error", 0) == 1; } @Override @@ -243,4 +253,24 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements Slog.e(TAG, "Remote exception", e); } } + + @Override + protected @NonNull VibrationEffect getSuccessVibrationEffect() { + if (!mCustomHaptics) { + return super.getSuccessVibrationEffect(); + } + + return getVibration(Settings.Global.getString(mContentResolver, + "face_success_type"), super.getSuccessVibrationEffect()); + } + + @Override + protected @NonNull VibrationEffect getErrorVibrationEffect() { + if (!mCustomHaptics) { + return super.getErrorVibrationEffect(); + } + + return getVibration(Settings.Global.getString(mContentResolver, + "face_error_type"), super.getErrorVibrationEffect()); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java new file mode 100644 index 000000000000..12f3e87dd37e --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java @@ -0,0 +1,146 @@ +/* + * 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.content.Context; +import android.hardware.biometrics.BiometricFaceConstants; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.face.Feature; +import android.hardware.biometrics.face.IFace; +import android.hardware.biometrics.face.ISession; +import android.os.IBinder; +import android.os.RemoteException; +import android.provider.Settings; +import android.util.Slog; + +import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.ErrorConsumer; +import com.android.server.biometrics.sensors.HalClientMonitor; + +import java.util.HashMap; +import java.util.Map; + +/** + * Face-specific get feature client for the {@link IFace} AIDL HAL interface. + */ +public class FaceGetFeatureClient extends HalClientMonitor<ISession> implements ErrorConsumer { + + private static final String TAG = "FaceGetFeatureClient"; + + private final int mUserId; + + FaceGetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon, + @NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, + @NonNull String owner, int sensorId) { + super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, + BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN); + mUserId = userId; + } + + @Override + public void unableToStart() { + mCallback.onClientFinished(this, false /* success */); + } + + @Override + public void start(@NonNull Callback callback) { + super.start(callback); + startHalOperation(); + } + + @Override + protected void startHalOperation() { + try { + getFreshDaemon().getFeatures(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to getFeature", e); + mCallback.onClientFinished(this, false /* success */); + } + } + + @Override + public int getProtoEnum() { + return BiometricsProto.CM_GET_FEATURE; + } + + public void onFeatureGet(boolean success, byte[] features) { + HashMap<Integer, Boolean> featureMap = getFeatureMap(); + int[] featuresToSend = new int[featureMap.size()]; + boolean[] featureState = new boolean[featureMap.size()]; + + // The AIDL get feature api states that the presence of a feature means + // it is enabled, while the lack thereof means its disabled. + for (int i = 0; i < features.length; i++) { + Integer feature = convertAidlToFrameworkFeature(features[i]); + if (feature != null) { + featureMap.put(feature, true); + } + } + + int i = 0; + for (Map.Entry<Integer, Boolean> entry : featureMap.entrySet()) { + featuresToSend[i] = entry.getKey(); + featureState[i] = entry.getValue(); + i++; + } + + boolean attentionEnabled = featureMap.get(BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION); + Slog.d(TAG, "Updating attention value for user: " + mUserId + + " to value: " + attentionEnabled); + Settings.Secure.putIntForUser(getContext().getContentResolver(), + Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, + attentionEnabled ? 1 : 0, mUserId); + try { + getListener().onFeatureGet(success, featuresToSend, featureState); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + + mCallback.onClientFinished(this, true /* success */); + } + + private @NonNull HashMap<Integer, Boolean> getFeatureMap() { + HashMap<Integer, Boolean> featureMap = new HashMap<>(); + featureMap.put(BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION, false); + return featureMap; + } + + private Integer convertAidlToFrameworkFeature(byte feature) { + switch (feature) { + case Feature.REQUIRE_ATTENTION: + return new Integer(BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION); + default: + return null; + } + } + + @Override + public void onError(int errorCode, int vendorCode) { + try { + getListener().onFeatureGet(false /* success */, new int[0], new boolean[0]); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + + mCallback.onClientFinished(this, false /* success */); + } + +} 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 b8bac402f430..23be50e5925a 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 @@ -435,13 +435,36 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { public void scheduleSetFeature(int sensorId, @NonNull IBinder token, int userId, int feature, boolean enabled, @NonNull byte[] hardwareAuthToken, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) { - // TODO(b/171335732): implement this. + mHandler.post(() -> { + final List<Face> faces = FaceUtils.getInstance(sensorId) + .getBiometricsForUser(mContext, userId); + if (faces.isEmpty()) { + Slog.w(getTag(), "Ignoring setFeature, no templates enrolled for user: " + userId); + return; + } + final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext, + mSensors.get(sensorId).getLazySession(), token, + new ClientMonitorCallbackConverter(receiver), userId, + mContext.getOpPackageName(), sensorId, feature, enabled, hardwareAuthToken); + scheduleForSensor(sensorId, client); + }); } @Override public void scheduleGetFeature(int sensorId, @NonNull IBinder token, int userId, int feature, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName) { - // TODO(b/171335732): implement this. + mHandler.post(() -> { + final List<Face> faces = FaceUtils.getInstance(sensorId) + .getBiometricsForUser(mContext, userId); + if (faces.isEmpty()) { + Slog.w(getTag(), "Ignoring getFeature, no templates enrolled for user: " + userId); + return; + } + final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, + mSensors.get(sensorId).getLazySession(), token, callback, userId, + mContext.getOpPackageName(), sensorId); + scheduleForSensor(sensorId, client); + }); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java new file mode 100644 index 000000000000..c3abfc2ddd0b --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face.aidl; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.BiometricFaceConstants; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.face.Feature; +import android.hardware.biometrics.face.IFace; +import android.hardware.biometrics.face.ISession; +import android.hardware.keymaster.HardwareAuthToken; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.HardwareAuthTokenUtils; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.ErrorConsumer; +import com.android.server.biometrics.sensors.HalClientMonitor; + +/** + * Face-specific get feature client for the {@link IFace} AIDL HAL interface. + */ +public class FaceSetFeatureClient extends HalClientMonitor<ISession> implements ErrorConsumer { + + private static final String TAG = "FaceSetFeatureClient"; + + private final int mFeature; + private final boolean mEnabled; + private final HardwareAuthToken mHardwareAuthToken; + + FaceSetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon, + @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, + @NonNull String owner, int sensorId, int feature, boolean enabled, + byte[] hardwareAuthToken) { + super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, + BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN); + mFeature = feature; + mEnabled = enabled; + mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken); + } + + @Override + public void unableToStart() { + try { + getListener().onFeatureSet(false /* success */, mFeature); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to send error", e); + } + } + + @Override + public void start(@NonNull Callback callback) { + super.start(callback); + startHalOperation(); + } + + @Override + protected void startHalOperation() { + try { + getFreshDaemon() + .setFeature(mHardwareAuthToken, + convertFrameworkToAidlFeature(mFeature), mEnabled); + } catch (RemoteException | IllegalArgumentException e) { + Slog.e(TAG, "Unable to set feature: " + mFeature + " to enabled: " + mEnabled, e); + mCallback.onClientFinished(this, false /* success */); + } + } + + @Override + public int getProtoEnum() { + return BiometricsProto.CM_SET_FEATURE; + } + + public void onFeatureSet(boolean success) { + try { + getListener().onFeatureSet(success, mFeature); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + + mCallback.onClientFinished(this, true /* success */); + } + + private byte convertFrameworkToAidlFeature(int feature) throws IllegalArgumentException { + switch (feature) { + case BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION: + return Feature.REQUIRE_ATTENTION; + default: + Slog.e(TAG, "Unsupported feature : " + feature); + throw new IllegalArgumentException(); + } + } + + @Override + public void onError(int errorCode, int vendorCode) { + try { + getListener().onFeatureSet(false /* success */, mFeature); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + + mCallback.onClientFinished(this, false /* success */); + } + +} 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 82b1271d6102..724531ebcf42 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 @@ -186,7 +186,7 @@ public class Sensor { return; } ((FaceAuthenticationClient) client).onAuthenticationFrame( - AidlConversionUtils.convert(frame)); + AidlConversionUtils.toFrameworkAuthenticationFrame(frame)); }); } @@ -204,7 +204,8 @@ public class Sensor { + Utils.getClientName(client)); return; } - ((FaceEnrollClient) client).onEnrollmentFrame(AidlConversionUtils.convert(frame)); + ((FaceEnrollClient) client).onEnrollmentFrame( + AidlConversionUtils.toFrameworkEnrollmentFrame(frame)); }); } @@ -223,7 +224,7 @@ public class Sensor { } final ErrorConsumer errorConsumer = (ErrorConsumer) client; - errorConsumer.onError(error, vendorCode); + errorConsumer.onError(AidlConversionUtils.toFrameworkError(error), vendorCode); if (error == Error.HW_UNAVAILABLE) { mCallback.onHardwareUnavailable(); @@ -376,12 +377,32 @@ public class Sensor { @Override public void onFeaturesRetrieved(byte[] features) { + mHandler.post(() -> { + final BaseClientMonitor client = mScheduler.getCurrentClient(); + if (!(client instanceof FaceGetFeatureClient)) { + Slog.e(mTag, "onFeaturesRetrieved for non-get feature consumer: " + + Utils.getClientName(client)); + return; + } + final FaceGetFeatureClient faceGetFeatureClient = (FaceGetFeatureClient) client; + faceGetFeatureClient.onFeatureGet(true /* success */, features); + }); } @Override public void onFeatureSet(byte feature) { + mHandler.post(() -> { + final BaseClientMonitor client = mScheduler.getCurrentClient(); + if (!(client instanceof FaceSetFeatureClient)) { + Slog.e(mTag, "onFeatureSet for non-set consumer: " + + Utils.getClientName(client)); + return; + } + final FaceSetFeatureClient faceSetFeatureClient = (FaceSetFeatureClient) client; + faceSetFeatureClient.onFeatureSet(true /* success */); + }); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java index f1bfd53a5753..bf3f7b45dc4a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java @@ -17,6 +17,7 @@ package com.android.server.biometrics.sensors.face.aidl; import android.hardware.biometrics.common.ICancellationSignal; +import android.hardware.biometrics.face.EnrollmentStageConfig; import android.hardware.biometrics.face.Error; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.ISession; @@ -57,6 +58,11 @@ public class TestHal extends IFace.Stub { } @Override + public EnrollmentStageConfig[] getEnrollmentConfig(byte enrollmentType) { + return new EnrollmentStageConfig[0]; + } + + @Override public ICancellationSignal enroll(HardwareAuthToken hat, byte enrollmentType, byte[] features, NativeHandle previewSurface) { Slog.w(TAG, "enroll"); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java index e8668ed1b6c5..f8067670f61f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java @@ -94,7 +94,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { } @Override - public void onFeatureGet(boolean success, int feature, boolean value) { + public void onFeatureGet(boolean success, int[] features, boolean[] featureState) { } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java index ff06a4a30a80..c4bdb320ddef 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java @@ -17,6 +17,7 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; +import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.hardware.biometrics.BiometricAuthenticator; @@ -27,6 +28,8 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.face.FaceManager; import android.os.IBinder; import android.os.RemoteException; +import android.os.VibrationEffect; +import android.provider.Settings; import android.util.Slog; import com.android.internal.R; @@ -47,7 +50,8 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { private static final String TAG = "FaceAuthenticationClient"; - + @NonNull private final ContentResolver mContentResolver; + private final boolean mCustomHaptics; private final UsageStats mUsageStats; private final int[] mBiometricPromptIgnoreList; @@ -78,6 +82,10 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { R.array.config_face_acquire_keyguard_ignorelist); mKeyguardIgnoreListVendor = resources.getIntArray( R.array.config_face_acquire_vendor_keyguard_ignorelist); + + mContentResolver = context.getContentResolver(); + mCustomHaptics = Settings.Global.getInt(mContentResolver, + "face_custom_success_error", 0) == 1; } @Override @@ -188,4 +196,24 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { final boolean shouldSend = shouldSend(acquireInfo, vendorCode); onAcquiredInternal(acquireInfo, vendorCode, shouldSend); } + + @Override + protected @NonNull VibrationEffect getSuccessVibrationEffect() { + if (!mCustomHaptics) { + return super.getSuccessVibrationEffect(); + } + + return getVibration(Settings.Global.getString(mContentResolver, + "face_success_type"), super.getSuccessVibrationEffect()); + } + + @Override + protected @NonNull VibrationEffect getErrorVibrationEffect() { + if (!mCustomHaptics) { + return super.getErrorVibrationEffect(); + } + + return getVibration(Settings.Global.getString(mContentResolver, + "face_error_type"), super.getErrorVibrationEffect()); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java index b1083d410fec..7821601c8433 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java @@ -58,7 +58,7 @@ public class FaceGetFeatureClient extends HalClientMonitor<IBiometricsFace> { public void unableToStart() { try { if (getListener() != null) { - getListener().onFeatureGet(false /* success */, mFeature, false /* value */); + getListener().onFeatureGet(false /* success */, new int[0], new boolean[0]); } } catch (RemoteException e) { Slog.e(TAG, "Unable to send error", e); @@ -75,9 +75,14 @@ public class FaceGetFeatureClient extends HalClientMonitor<IBiometricsFace> { protected void startHalOperation() { try { final OptionalBool result = getFreshDaemon().getFeature(mFeature, mFaceId); + int[] features = new int[1]; + boolean[] featureState = new boolean[1]; + features[0] = mFeature; + featureState[0] = result.value; mValue = result.value; + if (getListener() != null) { - getListener().onFeatureGet(result.status == Status.OK, mFeature, mValue); + getListener().onFeatureGet(result.status == Status.OK, features, featureState); } mCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { 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 deb251bc4853..b780222da380 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 @@ -29,11 +29,6 @@ import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPR 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 static android.hardware.fingerprint.FingerprintStateListener.STATE_AUTH_OTHER; -import static android.hardware.fingerprint.FingerprintStateListener.STATE_BP_AUTH; -import static android.hardware.fingerprint.FingerprintStateListener.STATE_ENROLLING; -import static android.hardware.fingerprint.FingerprintStateListener.STATE_IDLE; -import static android.hardware.fingerprint.FingerprintStateListener.STATE_KEYGUARD_AUTH; import android.annotation.NonNull; import android.annotation.Nullable; @@ -55,7 +50,6 @@ import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintServiceReceiver; -import android.hardware.fingerprint.FingerprintStateListener; import android.hardware.fingerprint.IFingerprintClientActiveCallback; import android.hardware.fingerprint.IFingerprintService; import android.hardware.fingerprint.IFingerprintServiceReceiver; @@ -82,8 +76,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.biometrics.Utils; -import com.android.server.biometrics.sensors.AuthenticationClient; -import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricServiceCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutResetDispatcher; @@ -91,7 +83,6 @@ import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider; import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21; import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21UdfpsMock; -import com.android.server.biometrics.sensors.fingerprint.hidl.FingerprintEnrollClient; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -116,60 +107,13 @@ public class FingerprintService extends SystemService implements BiometricServic private final FingerprintServiceWrapper mServiceWrapper; @NonNull private List<ServiceProvider> mServiceProviders; @NonNull private final FingerprintStateCallback mFingerprintStateCallback; - @NonNull private @FingerprintStateListener.State int mFingerprintState; - - private List<IFingerprintStateListener> mFingerprintStateListeners = new ArrayList<>(); - - /** Callback to receive notifications about changes in fingerprint state. */ - public final class FingerprintStateCallback implements BaseClientMonitor.Callback { - @Override - public void onClientStarted(@NonNull BaseClientMonitor client) { - final int previousFingerprintState = mFingerprintState; - if (client instanceof AuthenticationClient) { - AuthenticationClient authClient = (AuthenticationClient) client; - if (authClient.isKeyguard()) { - mFingerprintState = STATE_KEYGUARD_AUTH; - } else if (authClient.isBiometricPrompt()) { - mFingerprintState = STATE_BP_AUTH; - } else { - mFingerprintState = STATE_AUTH_OTHER; - } - } else if (client instanceof FingerprintEnrollClient) { - mFingerprintState = STATE_ENROLLING; - } else { - Slog.w(TAG, "Other authentication client: " + Utils.getClientName(client)); - mFingerprintState = STATE_IDLE; - } - Slog.d(TAG, "Fps state updated from " + previousFingerprintState + " to " - + mFingerprintState + ", client " + client); - notifyFingerprintStateListeners(mFingerprintState); - } - - @Override - public void onClientFinished(@NonNull BaseClientMonitor client, boolean success) { - mFingerprintState = STATE_IDLE; - Slog.d(TAG, "Client finished, fps state updated to " + mFingerprintState - + ", client " + client); - notifyFingerprintStateListeners(mFingerprintState); - } - - private void notifyFingerprintStateListeners(@FingerprintStateListener.State int newState) { - for (IFingerprintStateListener listener : mFingerprintStateListeners) { - try { - listener.onStateChanged(newState); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception in fingerprint state change", e); - } - } - } - } /** * Registers FingerprintStateListener in list stored by FingerprintService * @param listener new FingerprintStateListener being added */ - public void registerFingerprintStateListener(IFingerprintStateListener listener) { - mFingerprintStateListeners.add(listener); + public void registerFingerprintStateListener(@NonNull IFingerprintStateListener listener) { + mFingerprintStateCallback.registerFingerprintStateListener(listener); } /** @@ -636,7 +580,8 @@ public class FingerprintService extends SystemService implements BiometricServic : provider.getSensorProperties()) { pw.println("Dumping for sensorId: " + props.sensorId + ", provider: " + provider.getClass().getSimpleName()); - pw.println("Fps state: " + mFingerprintState); + pw.println("Fps state: " + + mFingerprintStateCallback.getFingerprintState()); provider.dumpInternal(props.sensorId, pw); pw.println(); } @@ -898,7 +843,6 @@ public class FingerprintService extends SystemService implements BiometricServic mLockPatternUtils = new LockPatternUtils(context); mServiceProviders = new ArrayList<>(); mFingerprintStateCallback = new FingerprintStateCallback(); - mFingerprintState = STATE_IDLE; } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java new file mode 100644 index 000000000000..5f998d81fb83 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java @@ -0,0 +1,105 @@ +/* + * 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.fingerprint; + +import static android.hardware.fingerprint.FingerprintStateListener.STATE_AUTH_OTHER; +import static android.hardware.fingerprint.FingerprintStateListener.STATE_BP_AUTH; +import static android.hardware.fingerprint.FingerprintStateListener.STATE_ENROLLING; +import static android.hardware.fingerprint.FingerprintStateListener.STATE_IDLE; +import static android.hardware.fingerprint.FingerprintStateListener.STATE_KEYGUARD_AUTH; + +import android.annotation.NonNull; +import android.hardware.fingerprint.FingerprintStateListener; +import android.hardware.fingerprint.IFingerprintStateListener; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.AuthenticationClient; +import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.fingerprint.hidl.FingerprintEnrollClient; + +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * A callback for receiving notifications about changes in fingerprint state. + */ +public class FingerprintStateCallback implements BaseClientMonitor.Callback { + private @FingerprintStateListener.State int mFingerprintState; + @NonNull private final CopyOnWriteArrayList<IFingerprintStateListener> + mFingerprintStateListeners = new CopyOnWriteArrayList<>(); + + public FingerprintStateCallback() { + mFingerprintState = STATE_IDLE; + } + + public int getFingerprintState() { + return mFingerprintState; + } + + @Override + public void onClientStarted(@NonNull BaseClientMonitor client) { + final int previousFingerprintState = mFingerprintState; + if (client instanceof AuthenticationClient) { + AuthenticationClient authClient = (AuthenticationClient) client; + if (authClient.isKeyguard()) { + mFingerprintState = STATE_KEYGUARD_AUTH; + } else if (authClient.isBiometricPrompt()) { + mFingerprintState = STATE_BP_AUTH; + } else { + mFingerprintState = STATE_AUTH_OTHER; + } + } else if (client instanceof FingerprintEnrollClient) { + mFingerprintState = STATE_ENROLLING; + } else { + Slog.w(FingerprintService.TAG, + "Other authentication client: " + Utils.getClientName(client)); + mFingerprintState = STATE_IDLE; + } + Slog.d(FingerprintService.TAG, "Fps state updated from " + previousFingerprintState + + " to " + mFingerprintState + ", client " + client); + notifyFingerprintStateListeners(mFingerprintState); + } + + @Override + public void onClientFinished(@NonNull BaseClientMonitor client, boolean success) { + mFingerprintState = STATE_IDLE; + Slog.d(FingerprintService.TAG, + "Client finished, fps state updated to " + mFingerprintState + ", client " + + client); + notifyFingerprintStateListeners(mFingerprintState); + } + + private void notifyFingerprintStateListeners(@FingerprintStateListener.State int newState) { + for (IFingerprintStateListener listener : mFingerprintStateListeners) { + try { + listener.onStateChanged(newState); + } catch (RemoteException e) { + Slog.e(FingerprintService.TAG, "Remote exception in fingerprint state change", e); + } + } + } + + /** + * Enables clients to register a FingerprintStateListener. Used by FingerprintService to forward + * updates in fingerprint sensor state to the SideFpNsEventHandler + * @param listener + */ + public void registerFingerprintStateListener(@NonNull IFingerprintStateListener listener) { + mFingerprintStateListeners.add(listener); + } +} 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 e610448028e9..701b9a739e33 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 @@ -87,20 +87,20 @@ public interface ServiceProvider { void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback); + @NonNull FingerprintStateCallback fingerprintStateCallback); void cancelEnrollment(int sensorId, @NonNull IBinder token); void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, int statsClient, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback); + @NonNull FingerprintStateCallback fingerprintStateCallback); void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, boolean restricted, int statsClient, boolean allowBackgroundAuthentication, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback); + @NonNull FingerprintStateCallback fingerprintStateCallback); void startPreparedClient(int sensorId, int cookie); @@ -151,6 +151,6 @@ public interface ServiceProvider { @NonNull ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback, + @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull String opPackageName); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java new file mode 100644 index 000000000000..66142bff0453 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java @@ -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.server.biometrics.sensors.fingerprint.aidl; + +import android.hardware.biometrics.BiometricFingerprintConstants; +import android.hardware.biometrics.fingerprint.AcquiredInfo; +import android.hardware.biometrics.fingerprint.Error; + +/** + * Utilities for converting from hardware to framework-defined AIDL models. + */ +final class AidlConversionUtils { + // Prevent instantiation. + private AidlConversionUtils() { + } + + public static @BiometricFingerprintConstants.FingerprintError int toFrameworkError( + byte aidlError) { + if (aidlError == Error.UNKNOWN) { + return BiometricFingerprintConstants.FINGERPRINT_ERROR_UNKNOWN; + } else if (aidlError == Error.HW_UNAVAILABLE) { + return BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE; + } else if (aidlError == Error.UNABLE_TO_PROCESS) { + return BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS; + } else if (aidlError == Error.TIMEOUT) { + return BiometricFingerprintConstants.FINGERPRINT_ERROR_TIMEOUT; + } else if (aidlError == Error.NO_SPACE) { + return BiometricFingerprintConstants.FINGERPRINT_ERROR_NO_SPACE; + } else if (aidlError == Error.CANCELED) { + return BiometricFingerprintConstants.FINGERPRINT_ERROR_CANCELED; + } else if (aidlError == Error.UNABLE_TO_REMOVE) { + return BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_REMOVE; + } else if (aidlError == Error.VENDOR) { + return BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR; + } else { + return BiometricFingerprintConstants.FINGERPRINT_ERROR_UNKNOWN; + } + } + + public static @BiometricFingerprintConstants.FingerprintAcquired int toFrameworkAcquiredInfo( + byte aidlAcquiredInfo) { + if (aidlAcquiredInfo == AcquiredInfo.UNKNOWN) { + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN; + } else if (aidlAcquiredInfo == AcquiredInfo.GOOD) { + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD; + } else if (aidlAcquiredInfo == AcquiredInfo.PARTIAL) { + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_PARTIAL; + } else if (aidlAcquiredInfo == AcquiredInfo.INSUFFICIENT) { + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_INSUFFICIENT; + } else if (aidlAcquiredInfo == AcquiredInfo.SENSOR_DIRTY) { + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_IMAGER_DIRTY; + } else if (aidlAcquiredInfo == AcquiredInfo.TOO_SLOW) { + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_SLOW; + } else if (aidlAcquiredInfo == AcquiredInfo.TOO_FAST) { + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_FAST; + } else if (aidlAcquiredInfo == AcquiredInfo.VENDOR) { + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; + } else if (aidlAcquiredInfo == AcquiredInfo.START) { + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START; + } else if (aidlAcquiredInfo == AcquiredInfo.TOO_DARK) { + // No framework constant available + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN; + } else if (aidlAcquiredInfo == AcquiredInfo.TOO_BRIGHT) { + // No framework constant available + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN; + } else if (aidlAcquiredInfo == AcquiredInfo.IMMOBILE) { + // No framework constant available + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN; + } else if (aidlAcquiredInfo == AcquiredInfo.RETRYING_CAPTURE) { + // No framework constant available + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN; + } else { + return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN; + } + } +} 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 66bd73c51655..e34afc09eec1 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 @@ -32,7 +32,7 @@ import android.util.Slog; import com.android.server.biometrics.HardwareAuthTokenUtils; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.BaseClientMonitor; -import com.android.server.biometrics.sensors.fingerprint.FingerprintService; +import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import java.util.HashSet; @@ -51,7 +51,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub { @NonNull private final Context mContext; private final int mSensorId; @NonNull private final ITestSessionCallback mCallback; - @NonNull private final FingerprintService.FingerprintStateCallback mFingerprintStateCallback; + @NonNull private final FingerprintStateCallback mFingerprintStateCallback; @NonNull private final FingerprintProvider mProvider; @NonNull private final Sensor mSensor; @NonNull private final Set<Integer> mEnrollmentIds; @@ -117,7 +117,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub { BiometricTestSessionImpl(@NonNull Context context, int sensorId, @NonNull ITestSessionCallback callback, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback, + @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull FingerprintProvider provider, @NonNull Sensor sensor) { mContext = context; 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 f1e37e0dc6c2..c23c113b5672 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 @@ -55,7 +55,7 @@ import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.InvalidationRequesterClient; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.PerformanceTracker; -import com.android.server.biometrics.sensors.fingerprint.FingerprintService; +import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import com.android.server.biometrics.sensors.fingerprint.ServiceProvider; @@ -320,7 +320,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback) { + @NonNull FingerprintStateCallback fingerprintStateCallback) { mHandler.post(() -> { final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties() .maxEnrollmentsPerUser; @@ -358,7 +358,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, int statsClient, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback) { + @NonNull FingerprintStateCallback fingerprintStateCallback) { mHandler.post(() -> { final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); final FingerprintDetectClient client = new FingerprintDetectClient(mContext, @@ -374,7 +374,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, boolean restricted, int statsClient, boolean allowBackgroundAuthentication, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback) { + @NonNull FingerprintStateCallback fingerprintStateCallback) { mHandler.post(() -> { final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient( @@ -572,7 +572,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull @Override public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback, + @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull String opPackageName) { return mSensors.get(sensorId).createTestSession(callback, fingerprintStateCallback); } 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 49c454fcfaec..cf915ad78509 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 @@ -58,7 +58,7 @@ import com.android.server.biometrics.sensors.RemovalConsumer; import com.android.server.biometrics.sensors.StartUserClient; import com.android.server.biometrics.sensors.StopUserClient; import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; -import com.android.server.biometrics.sensors.fingerprint.FingerprintService; +import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; @@ -179,7 +179,8 @@ class Sensor { } final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client; - acquisitionClient.onAcquired(info, vendorCode); + acquisitionClient.onAcquired(AidlConversionUtils.toFrameworkAcquiredInfo(info), + vendorCode); }); } @@ -198,7 +199,7 @@ class Sensor { } final ErrorConsumer errorConsumer = (ErrorConsumer) client; - errorConsumer.onError(error, vendorCode); + errorConsumer.onError(AidlConversionUtils.toFrameworkError(error), vendorCode); if (error == Error.HW_UNAVAILABLE) { mCallback.onHardwareUnavailable(); @@ -487,7 +488,7 @@ class Sensor { } @NonNull ITestSession createTestSession(@NonNull ITestSessionCallback callback, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback) { + @NonNull FingerprintStateCallback fingerprintStateCallback) { return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, callback, fingerprintStateCallback, mProvider, this); } 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 4ff6a8fda006..ad4f679f075f 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 @@ -31,7 +31,7 @@ import android.util.Slog; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.BaseClientMonitor; -import com.android.server.biometrics.sensors.fingerprint.FingerprintService; +import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import java.util.ArrayList; @@ -52,7 +52,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { @NonNull private final Context mContext; private final int mSensorId; @NonNull private final ITestSessionCallback mCallback; - @NonNull private final FingerprintService.FingerprintStateCallback mFingerprintStateCallback; + @NonNull private final FingerprintStateCallback mFingerprintStateCallback; @NonNull private final Fingerprint21 mFingerprint21; @NonNull private final Fingerprint21.HalResultController mHalResultController; @NonNull private final Set<Integer> mEnrollmentIds; @@ -118,7 +118,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { BiometricTestSessionImpl(@NonNull Context context, int sensorId, @NonNull ITestSessionCallback callback, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback, + @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull Fingerprint21 fingerprint21, @NonNull Fingerprint21.HalResultController halResultController) { mContext = context; 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 ed681c950dd5..ebfd5347241d 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 @@ -74,7 +74,7 @@ import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.RemovalConsumer; -import com.android.server.biometrics.sensors.fingerprint.FingerprintService; +import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import com.android.server.biometrics.sensors.fingerprint.ServiceProvider; @@ -574,7 +574,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback) { + @NonNull FingerprintStateCallback fingerprintStateCallback) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); @@ -614,7 +614,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName, int statsClient, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback) { + @NonNull FingerprintStateCallback fingerprintStateCallback) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); @@ -632,7 +632,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName, boolean restricted, int statsClient, boolean allowBackgroundAuthentication, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback) { + @NonNull FingerprintStateCallback fingerprintStateCallback) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); @@ -896,7 +896,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull @Override public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, - @NonNull FingerprintService.FingerprintStateCallback fingerprintStateCallback, + @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull String opPackageName) { return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, callback, fingerprintStateCallback, this, mHalResultController); diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java index 1f4606104ab8..1da7f0c059b0 100644 --- a/services/core/java/com/android/server/content/SyncJobService.java +++ b/services/core/java/com/android/server/content/SyncJobService.java @@ -119,7 +119,7 @@ public class SyncJobService extends JobService { public boolean onStopJob(JobParameters params) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Slog.v(TAG, "onStopJob called " + params.getJobId() + ", reason: " - + params.getLegacyStopReason()); + + params.getInternalStopReasonCode()); } final SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras()); if (op == null) { @@ -161,9 +161,11 @@ public class SyncJobService extends JobService { m.obj = op; // Reschedule if this job was NOT explicitly canceled. - m.arg1 = params.getLegacyStopReason() != JobParameters.REASON_CANCELED ? 1 : 0; + m.arg1 = params.getInternalStopReasonCode() != JobParameters.INTERNAL_STOP_REASON_CANCELED + ? 1 : 0; // Apply backoff only if stop is called due to timeout. - m.arg2 = params.getLegacyStopReason() == JobParameters.REASON_TIMEOUT ? 1 : 0; + m.arg2 = params.getInternalStopReasonCode() == JobParameters.INTERNAL_STOP_REASON_TIMEOUT + ? 1 : 0; SyncManager.sendMessage(m); return false; @@ -204,7 +206,7 @@ public class SyncJobService extends JobService { return "job:null"; } else { return "job:#" + params.getJobId() + ":" - + "sr=[" + params.getLegacyStopReason() + + "sr=[" + params.getInternalStopReasonCode() + "/" + params.getDebugStopReason() + "]:" + SyncOperation.maybeCreateFromJobExtras(params.getExtras()); } diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 0071b2f558c4..2d7145fef69c 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -34,6 +34,7 @@ import com.android.server.display.config.HbmTiming; import com.android.server.display.config.HighBrightnessMode; import com.android.server.display.config.NitsMap; import com.android.server.display.config.Point; +import com.android.server.display.config.SensorDetails; import com.android.server.display.config.XmlParser; import org.xmlpull.v1.XmlPullParserException; @@ -75,6 +76,9 @@ public class DisplayDeviceConfig { private final Context mContext; + // The details of the ambient light sensor associated with this display. + private final SensorIdentifier mAmbientLightSensor = new SensorIdentifier(); + // Nits and backlight values that are loaded from either the display device config file, or // config.xml. These are the raw values and just used for the dumpsys private float[] mRawNits; @@ -249,6 +253,10 @@ public class DisplayDeviceConfig { return mBrightnessRampSlowIncrease; } + SensorIdentifier getAmbientLightSensor() { + return mAmbientLightSensor; + } + /** * @param quirkValue The quirk to test. * @return {@code true} if the specified quirk is present in this configuration, @@ -291,6 +299,7 @@ public class DisplayDeviceConfig { + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease + ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease + + ", mAmbientLightSensor=" + mAmbientLightSensor + "}"; return str; } @@ -318,7 +327,7 @@ public class DisplayDeviceConfig { private static DisplayDeviceConfig getConfigFromPmValues(Context context) { DisplayDeviceConfig config = new DisplayDeviceConfig(context); - config.initFromPmValues(); + config.initFromDefaultValues(); return config; } @@ -342,6 +351,7 @@ public class DisplayDeviceConfig { loadHighBrightnessModeData(config); loadQuirks(config); loadBrightnessRamps(config); + loadAmbientLightSensorFromDdc(config); } else { Slog.w(TAG, "DisplayDeviceConfig file is null"); } @@ -357,9 +367,10 @@ public class DisplayDeviceConfig { loadBrightnessConstraintsFromConfigXml(); loadBrightnessMapFromConfigXml(); loadBrightnessRampsFromConfigXml(); + loadAmbientLightSensorFromConfigXml(); } - private void initFromPmValues() { + private void initFromDefaultValues() { // Set all to basic values mBacklightMinimum = PowerManager.BRIGHTNESS_MIN; mBacklightMaximum = PowerManager.BRIGHTNESS_MAX; @@ -369,6 +380,7 @@ public class DisplayDeviceConfig { mBrightnessRampSlowDecrease = PowerManager.BRIGHTNESS_MAX; mBrightnessRampSlowIncrease = PowerManager.BRIGHTNESS_MAX; setSimpleMappingStrategyValues(); + loadAmbientLightSensorFromConfigXml(); } private void loadBrightnessDefaultFromDdcXml(DisplayConfiguration config) { @@ -637,6 +649,33 @@ public class DisplayDeviceConfig { mBrightnessRampSlowDecrease = mBrightnessRampSlowIncrease; } + private void loadAmbientLightSensorFromConfigXml() { + mAmbientLightSensor.name = ""; + mAmbientLightSensor.type = mContext.getResources().getString( + com.android.internal.R.string.config_displayLightSensorType); + } + + private void loadAmbientLightSensorFromDdc(DisplayConfiguration config) { + final SensorDetails sensorDetails = config.getLightSensor(); + if (sensorDetails != null) { + mAmbientLightSensor.type = sensorDetails.getType(); + mAmbientLightSensor.name = sensorDetails.getName(); + } + } + + static class SensorIdentifier { + public String type; + public String name; + + @Override + public String toString() { + return "Sensor{" + + "type: \"" + type + "\"" + + ", name: \"" + name + "\"" + + "} "; + } + } + /** * Container for high brightness mode configuration data. */ @@ -656,6 +695,17 @@ public class DisplayDeviceConfig { /** Minimum time that HBM can be on before being enabled. */ public long timeMinMillis; + HighBrightnessModeData() {} + + HighBrightnessModeData(float minimumLux, float transitionPoint, + long timeWindowMillis, long timeMaxMillis, long timeMinMillis) { + this.minimumLux = minimumLux; + this.transitionPoint = transitionPoint; + this.timeWindowMillis = timeWindowMillis; + this.timeMaxMillis = timeMaxMillis; + this.timeMinMillis = timeMinMillis; + } + /** * Copies the HBM data to the specified parameter instance. * @param other the instance to copy data to. diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 393a4eb83dd6..789f08fb3187 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -55,6 +55,7 @@ import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; import android.hardware.display.BrightnessConfiguration; +import android.hardware.display.BrightnessInfo; import android.hardware.display.Curve; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; @@ -430,8 +431,8 @@ public final class DisplayManagerService extends SystemService { mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper()); mUiHandler = UiThread.getHandler(); mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore); - mLogicalDisplayMapper = new LogicalDisplayMapper(mDisplayDeviceRepo, - new LogicalDisplayListener()); + mLogicalDisplayMapper = new LogicalDisplayMapper(mContext, mDisplayDeviceRepo, + new LogicalDisplayListener(), mSyncRoot, mHandler); mDisplayModeDirector = new DisplayModeDirector(context, mHandler); mBrightnessSynchronizer = new BrightnessSynchronizer(mContext); Resources resources = mContext.getResources(); @@ -842,8 +843,6 @@ public final class DisplayManagerService extends SystemService { return overriddenInfo; } - - return info; } @@ -1269,7 +1268,7 @@ public final class DisplayManagerService extends SystemService { DisplayPowerController dpc = mDisplayPowerControllers.get(displayId); if (dpc != null) { - dpc.onDisplayChangedLocked(); + dpc.onDisplayChanged(); } } @@ -1305,12 +1304,23 @@ public final class DisplayManagerService extends SystemService { handleLogicalDisplayChangedLocked(display); } + private void handleLogicalDisplayDeviceStateTransitionLocked(@NonNull LogicalDisplay display) { + final int displayId = display.getDisplayIdLocked(); + final DisplayPowerController dpc = mDisplayPowerControllers.get(displayId); + if (dpc != null) { + dpc.onDeviceStateTransition(); + } + } + private Runnable updateDisplayStateLocked(DisplayDevice device) { // Blank or unblank the display immediately to match the state requested // by the display power controller (if known). DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) { final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device); + if (display == null) { + return null; + } final int displayId = display.getDisplayIdLocked(); final int state = mDisplayStates.get(displayId); @@ -1454,9 +1464,12 @@ public final class DisplayManagerService extends SystemService { clearViewportsLocked(); // Configure each display device. - mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> { - configureDisplayLocked(t, device); - device.performTraversalLocked(t); + mLogicalDisplayMapper.forEachLocked((LogicalDisplay display) -> { + final DisplayDevice device = display.getPrimaryDisplayDeviceLocked(); + if (device != null) { + configureDisplayLocked(t, device); + device.performTraversalLocked(t); + } }); // Tell the input system about these new viewports. @@ -2062,10 +2075,16 @@ public final class DisplayManagerService extends SystemService { display, mContext); final DisplayPowerController displayPowerController = new DisplayPowerController( mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager, - mDisplayBlanker, display, mBrightnessTracker, brightnessSetting); + mDisplayBlanker, display, mBrightnessTracker, brightnessSetting, + () -> handleBrightnessChange(display)); mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController); } + private void handleBrightnessChange(LogicalDisplay display) { + sendDisplayEventLocked(display.getDisplayIdLocked(), + DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED); + } + private final class DisplayManagerHandler extends Handler { public DisplayManagerHandler(Looper looper) { super(looper, null, true /*async*/); @@ -2154,6 +2173,10 @@ public final class DisplayManagerService extends SystemService { case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED: handleLogicalDisplayFrameRateOverridesChangedLocked(display); break; + + case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION: + handleLogicalDisplayDeviceStateTransitionLocked(display); + break; } } @@ -2219,6 +2242,8 @@ public final class DisplayManagerService extends SystemService { return (mask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0; case DisplayManagerGlobal.EVENT_DISPLAY_CHANGED: return (mask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0; + case DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED: + return (mask & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0; case DisplayManagerGlobal.EVENT_DISPLAY_REMOVED: return (mask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0; default: @@ -2781,6 +2806,25 @@ public final class DisplayManagerService extends SystemService { } } + @Override + public BrightnessInfo getBrightnessInfo(int displayId) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS, + "Permission required to read the display's brightness info."); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + DisplayPowerController dpc = mDisplayPowerControllers.get(displayId); + if (dpc != null) { + return dpc.getBrightnessInfo(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + return null; + } + @Override // Binder call public boolean isMinimalPostProcessingRequested(int displayId) { synchronized (mSyncRoot) { @@ -3232,4 +3276,17 @@ public final class DisplayManagerService extends SystemService { } } }; + + /** + * Functional interface for providing time. + * TODO(b/184781936): merge with PowerManagerService.Clock + */ + @VisibleForTesting + public interface Clock { + /** + * Returns current time in milliseconds since boot, not counting time spent in deep sleep. + */ + long uptimeMillis(); + } + } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 5cd0534c8e93..3340e3c73fa1 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -32,6 +32,7 @@ import android.hardware.SensorManager; import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; import android.hardware.display.BrightnessConfiguration; +import android.hardware.display.BrightnessInfo; import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.metrics.LogMaker; @@ -53,6 +54,7 @@ import android.util.Slog; import android.util.TimeUtils; import android.view.Display; +import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IBatteryStats; import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.logging.MetricsLogger; @@ -146,6 +148,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int REPORTED_TO_POLICY_SCREEN_ON = 2; private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3; + private static final Uri BRIGHTNESS_FLOAT_URI = + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT); + private final Object mLock = new Object(); private final Context mContext; @@ -215,6 +220,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Whether or not the color fade on screen on / off is enabled. private final boolean mColorFadeEnabled; + @GuardedBy("mCachedBrightnessInfo") + private final CachedBrightnessInfo mCachedBrightnessInfo = new CachedBrightnessInfo(); + // True if we should fade the screen while turning it off, false if we should play // a stylish color fade animation instead. private boolean mColorFadeFadesConfig; @@ -359,6 +367,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final BrightnessSetting mBrightnessSetting; + private final Runnable mOnBrightnessChangeRunnable; + // A record of state for skipping brightness ramps. private int mSkipRampState = RAMP_STATE_SKIP_NONE; @@ -368,6 +378,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The controller for the automatic brightness level. private AutomaticBrightnessController mAutomaticBrightnessController; + private Sensor mLightSensor; + // The mapper between ambient lux, display backlight values, and display brightness. @Nullable private BrightnessMappingStrategy mBrightnessMapper; @@ -418,13 +430,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // True if this DisplayPowerController has been stopped and should no longer be running. private boolean mStopped; + private DisplayDeviceConfig mDisplayDeviceConfig; + /** * Creates the display power controller. */ public DisplayPowerController(Context context, DisplayPowerCallbacks callbacks, Handler handler, SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay, - BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting) { + BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting, + Runnable onBrightnessChangeRunnable) { mLogicalDisplay = logicalDisplay; mDisplayId = mLogicalDisplay.getDisplayIdLocked(); mHandler = new DisplayControllerHandler(handler.getLooper()); @@ -442,8 +457,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBlanker = blanker; mContext = context; mBrightnessTracker = brightnessTracker; - mBrightnessSetting = brightnessSetting; + mOnBrightnessChangeRunnable = onBrightnessChangeRunnable; + PowerManager pm = context.getSystemService(PowerManager.class); final Resources resources = context.getResources(); @@ -478,17 +494,20 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing); - DisplayDeviceConfig displayDeviceConfig = logicalDisplay + mDisplayDeviceConfig = logicalDisplay .getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig(); - mBrightnessRampRateFastDecrease = displayDeviceConfig.getBrightnessRampFastDecrease(); - mBrightnessRampRateFastIncrease = displayDeviceConfig.getBrightnessRampFastIncrease(); - mBrightnessRampRateSlowDecrease = displayDeviceConfig.getBrightnessRampSlowDecrease(); - mBrightnessRampRateSlowIncrease = displayDeviceConfig.getBrightnessRampSlowIncrease(); + mBrightnessRampRateFastDecrease = mDisplayDeviceConfig.getBrightnessRampFastDecrease(); + mBrightnessRampRateFastIncrease = mDisplayDeviceConfig.getBrightnessRampFastIncrease(); + mBrightnessRampRateSlowDecrease = mDisplayDeviceConfig.getBrightnessRampSlowDecrease(); + mBrightnessRampRateSlowIncrease = mDisplayDeviceConfig.getBrightnessRampSlowIncrease(); mSkipScreenOnBrightnessRamp = resources.getBoolean( com.android.internal.R.bool.config_skipScreenOnBrightnessRamp); mHbmController = createHbmController(); + // Seed the cached brightness + saveBrightnessInfo(getScreenBrightnessSetting()); + if (mUseSoftwareAutoBrightnessConfig) { final float dozeScaleFactor = resources.getFraction( com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor, @@ -534,16 +553,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ")."); } - String lightSensorType = resources.getString( - com.android.internal.R.string.config_displayLightSensorType); - Sensor lightSensor = findDisplayLightSensor(lightSensorType); + loadAmbientLightSensor(); - final DisplayDeviceConfig ddc = - logicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig(); - mBrightnessMapper = BrightnessMappingStrategy.create(resources, ddc); + mBrightnessMapper = BrightnessMappingStrategy.create(resources, mDisplayDeviceConfig); if (mBrightnessMapper != null) { mAutomaticBrightnessController = new AutomaticBrightnessController(this, - handler.getLooper(), sensorManager, lightSensor, mBrightnessMapper, + handler.getLooper(), sensorManager, mLightSensor, mBrightnessMapper, lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, @@ -597,8 +612,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings; mDisplayWhiteBalanceController = displayWhiteBalanceController; - if (displayDeviceConfig != null && displayDeviceConfig.getNits() != null) { - mNitsRange = displayDeviceConfig.getNits(); + if (mDisplayDeviceConfig != null && mDisplayDeviceConfig.getNits() != null) { + mNitsRange = mDisplayDeviceConfig.getNits(); } else { Slog.w(TAG, "Screen brightness nits configuration is unavailable; falling back"); mNitsRange = BrightnessMappingStrategy.getFloatArray(context.getResources() @@ -638,17 +653,19 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits); } - private Sensor findDisplayLightSensor(String sensorType) { - if (!TextUtils.isEmpty(sensorType)) { - List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); - for (int i = 0; i < sensors.size(); i++) { - Sensor sensor = sensors.get(i); - if (sensorType.equals(sensor.getStringType())) { + private Sensor findSensor(String sensorType, String sensorName, int fallbackType) { + final boolean isNameSpecified = !TextUtils.isEmpty(sensorName); + final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType); + List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); + if (isNameSpecified || isTypeSpecified) { + for (Sensor sensor : sensors) { + if ((!isNameSpecified || sensorName.equals(sensor.getName())) + && (!isTypeSpecified || sensorType.equals(sensor.getStringType()))) { return sensor; } } } - return mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); + return mSensorManager.getDefaultSensor(fallbackType); } /** @@ -763,10 +780,21 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call * when displays get swapped on foldable devices. For example, different brightness properties * of each display need to be properly reflected in AutomaticBrightnessController. */ - public void onDisplayChangedLocked() { + public void onDisplayChanged() { // TODO: b/175821789 - Support high brightness on multiple (folding) displays - mUniqueDisplayId = mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); + mDisplayDeviceConfig = mLogicalDisplay.getPrimaryDisplayDeviceLocked() + .getDisplayDeviceConfig(); + loadAmbientLightSensor(); + } + + /** + * Called when the displays are preparing to transition from one device state to another. + * This process involves turning off some displays so we need updatePowerState() to run and + * calculate the new state. + */ + public void onDeviceStateTransition() { + sendUpdatePowerState(); } /** @@ -1004,7 +1032,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mIgnoreProximityUntilChanged = false; } - if (!mLogicalDisplay.isEnabled() || mScreenOffBecauseOfProximity) { + if (!mLogicalDisplay.isEnabled() + || mLogicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION + || mScreenOffBecauseOfProximity) { state = Display.STATE_OFF; } @@ -1158,6 +1188,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL); } + // Save out the brightness info now that the brightness state for this iteration has been + // finalized and before we send out notifications about the brightness changing. + saveBrightnessInfo(brightnessState); + if (updateScreenBrightnessSetting) { // Tell the rest of the system about the new brightness in case we had to change it // for things like auto-brightness or high-brightness-mode. Note that we do this @@ -1390,13 +1424,36 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call msg.sendToTarget(); } + public BrightnessInfo getBrightnessInfo() { + synchronized (mCachedBrightnessInfo) { + return new BrightnessInfo( + mCachedBrightnessInfo.brightness, + mCachedBrightnessInfo.brightnessMin, + mCachedBrightnessInfo.brightnessMax, + mCachedBrightnessInfo.hbmMode); + } + } + + private void saveBrightnessInfo(float brightness) { + synchronized (mCachedBrightnessInfo) { + mCachedBrightnessInfo.brightness = brightness; + mCachedBrightnessInfo.brightnessMin = mHbmController.getCurrentBrightnessMin(); + mCachedBrightnessInfo.brightnessMax = mHbmController.getCurrentBrightnessMax(); + mCachedBrightnessInfo.hbmMode = mHbmController.getHighBrightnessMode(); + } + } + private HighBrightnessModeController createHbmController() { final DisplayDeviceConfig ddConfig = mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig(); final DisplayDeviceConfig.HighBrightnessModeData hbmData = ddConfig != null ? ddConfig.getHighBrightnessModeData() : null; return new HighBrightnessModeController(mHandler, PowerManager.BRIGHTNESS_MIN, - PowerManager.BRIGHTNESS_MAX, hbmData, () -> sendUpdatePowerStateLocked()); + PowerManager.BRIGHTNESS_MAX, hbmData, + () -> { + sendUpdatePowerStateLocked(); + mHandler.post(mOnBrightnessChangeRunnable); + }); } private void blockScreenOn() { @@ -1510,6 +1567,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mReportedScreenStateToPolicy = state; } + private void loadAmbientLightSensor() { + DisplayDeviceConfig.SensorIdentifier lightSensor = + mDisplayDeviceConfig.getAmbientLightSensor(); + String lightSensorName = lightSensor.name; + String lightSensorType = lightSensor.type; + mLightSensor = findSensor(lightSensorType, lightSensorName, Sensor.TYPE_LIGHT); + } + private float clampScreenBrightnessForVr(float value) { return MathUtils.constrain( value, mScreenBrightnessForVrRangeMinimum, @@ -1819,7 +1884,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mPendingScreenBrightnessSetting = getScreenBrightnessSetting(); if (userSwitch) { // Don't treat user switches as user initiated change. - mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting; + setCurrentScreenBrightness(mPendingScreenBrightnessSetting); if (mAutomaticBrightnessController != null) { mAutomaticBrightnessController.resetShortTermModel(); } @@ -1858,11 +1923,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private void putScreenBrightnessSetting(float brightnessValue, boolean updateCurrent) { if (updateCurrent) { - mCurrentScreenBrightnessSetting = brightnessValue; + setCurrentScreenBrightness(brightnessValue); } mBrightnessSetting.setBrightness(brightnessValue); } + private void setCurrentScreenBrightness(float brightnessValue) { + if (brightnessValue != mCurrentScreenBrightnessSetting) { + mCurrentScreenBrightnessSetting = brightnessValue; + mHandler.post(mOnBrightnessChangeRunnable); + } + } + private void putAutoBrightnessAdjustmentSetting(float adjustment) { if (mDisplayId == Display.DEFAULT_DISPLAY) { mAutoBrightnessAdjustment = adjustment; @@ -1895,7 +1967,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; return false; } - mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting; + setCurrentScreenBrightness(mPendingScreenBrightnessSetting); mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting; mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; @@ -1987,6 +2059,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(); pw.println("Display Power Controller:"); pw.println(" mDisplayId=" + mDisplayId); + pw.println(" mLightSensor=" + mLightSensor); pw.println(); pw.println("Display Power Controller Locked State:"); @@ -2037,10 +2110,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mPendingProximityDebounceTime=" + TimeUtils.formatUptime(mPendingProximityDebounceTime)); pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity); - pw.println(" mLastUserSetScreenBrightnessFloat=" + mLastUserSetScreenBrightness); - pw.println(" mPendingScreenBrightnessSettingFloat=" + pw.println(" mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness); + pw.println(" mPendingScreenBrightnessSetting=" + mPendingScreenBrightnessSetting); - pw.println(" mTemporaryScreenBrightnessFloat=" + mTemporaryScreenBrightness); + pw.println(" mTemporaryScreenBrightness=" + mTemporaryScreenBrightness); pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); pw.println(" mBrightnessReason=" + mBrightnessReason); pw.println(" mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment); @@ -2083,6 +2156,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.dump(pw); } + if (mHbmController != null) { + mHbmController.dump(pw); + } + pw.println(); if (mDisplayWhiteBalanceController != null) { mDisplayWhiteBalanceController.dump(pw); @@ -2411,4 +2488,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } } + + static class CachedBrightnessInfo { + public float brightness; + public float brightnessMin; + public float brightnessMax; + public int hbmMode; + } } diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index 2e5561dc0aea..e6486bd2a79a 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -16,13 +16,17 @@ package com.android.server.display; +import android.hardware.display.BrightnessInfo; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData; +import com.android.server.display.DisplayManagerService.Clock; +import java.io.PrintWriter; import java.util.Iterator; import java.util.LinkedList; @@ -45,11 +49,13 @@ class HighBrightnessModeController { private final Handler mHandler; private final Runnable mHbmChangeCallback; private final Runnable mRecalcRunnable; + private final Clock mClock; private boolean mIsInAllowedAmbientRange = false; private boolean mIsTimeAvailable = false; private boolean mIsAutoBrightnessEnabled = false; private float mAutoBrightness; + private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; /** * If HBM is currently running, this is the start time for the current HBM session. @@ -65,28 +71,25 @@ class HighBrightnessModeController { HighBrightnessModeController(Handler handler, float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData, Runnable hbmChangeCallback) { + this(SystemClock::uptimeMillis, handler, brightnessMin, brightnessMax, hbmData, + hbmChangeCallback); + } + + @VisibleForTesting + HighBrightnessModeController(Clock clock, Handler handler, float brightnessMin, + float brightnessMax, HighBrightnessModeData hbmData, Runnable hbmChangeCallback) { + mClock = clock; mHandler = handler; mBrightnessMin = brightnessMin; mBrightnessMax = brightnessMax; mHbmData = hbmData; mHbmChangeCallback = hbmChangeCallback; mAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; - - mRecalcRunnable = () -> { - boolean oldIsAllowed = isCurrentlyAllowed(); - recalculateTimeAllowance(); - if (oldIsAllowed != isCurrentlyAllowed()) { - // Our allowed state has changed; tell AutomaticBrightnessController - // to update the brightness. - if (mHbmChangeCallback != null) { - mHbmChangeCallback.run(); - } - } - }; + mRecalcRunnable = this::recalculateTimeAllowance; } void setAutoBrightnessEnabled(boolean isEnabled) { - if (isEnabled == mIsAutoBrightnessEnabled) { + if (!deviceSupportsHbm() || isEnabled == mIsAutoBrightnessEnabled) { return; } if (DEBUG) { @@ -94,6 +97,7 @@ class HighBrightnessModeController { } mIsAutoBrightnessEnabled = isEnabled; mIsInAllowedAmbientRange = false; // reset when auto-brightness switches + recalculateTimeAllowance(); } float getCurrentBrightnessMin() { @@ -137,7 +141,7 @@ class HighBrightnessModeController { final boolean wasOldBrightnessHigh = oldAutoBrightness > mHbmData.transitionPoint; final boolean isNewBrightnessHigh = mAutoBrightness > mHbmData.transitionPoint; if (wasOldBrightnessHigh != isNewBrightnessHigh) { - final long currentTime = SystemClock.uptimeMillis(); + final long currentTime = mClock.uptimeMillis(); if (isNewBrightnessHigh) { mRunningStartTimeMillis = currentTime; } else { @@ -153,6 +157,21 @@ class HighBrightnessModeController { recalculateTimeAllowance(); } + int getHighBrightnessMode() { + return mHbmMode; + } + + void dump(PrintWriter pw) { + pw.println("HighBrightnessModeController:"); + pw.println(" mBrightnessMin=" + mBrightnessMin); + pw.println(" mBrightnessMax=" + mBrightnessMax); + pw.println(" mHbmData=" + mHbmData); + pw.println(" mIsInAllowedAmbientRange=" + mIsInAllowedAmbientRange); + pw.println(" mIsTimeAvailable= " + mIsTimeAvailable); + pw.println(" mIsAutoBrightnessEnabled=" + mIsAutoBrightnessEnabled); + pw.println(" mAutoBrightness=" + mAutoBrightness); + } + private boolean isCurrentlyAllowed() { return mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange; } @@ -165,7 +184,7 @@ class HighBrightnessModeController { * Recalculates the allowable HBM time. */ private void recalculateTimeAllowance() { - final long currentTime = SystemClock.uptimeMillis(); + final long currentTime = mClock.uptimeMillis(); long timeAlreadyUsed = 0; // First, lets see how much time we've taken for any currently running @@ -247,8 +266,22 @@ class HighBrightnessModeController { if (nextTimeout != -1) { mHandler.removeCallbacks(mRecalcRunnable); - mHandler.postAtTime(mRecalcRunnable, nextTimeout); + mHandler.postAtTime(mRecalcRunnable, nextTimeout + 1); + } + + // Update the state of the world + int newHbmMode = calculateHighBrightnessMode(); + if (mHbmMode != newHbmMode) { + mHbmMode = newHbmMode; + mHbmChangeCallback.run(); + } + } + + private int calculateHighBrightnessMode() { + if (deviceSupportsHbm() && isCurrentlyAllowed()) { + return BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT; } + return BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; } /** diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 15894199f806..9acb4c8f471a 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -16,6 +16,7 @@ package com.android.server.display; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Point; @@ -65,6 +66,33 @@ import java.util.Objects; final class LogicalDisplay { private static final String TAG = "LogicalDisplay"; + /** + * Phase indicating the logical display's existence is hidden from the rest of the framework. + * This can happen if the current layout has specifically requested to keep this display + * disabled. + */ + static final int DISPLAY_PHASE_DISABLED = -1; + + /** + * Phase indicating that the logical display is going through a layout transition. + * When in this phase, other systems can choose to special case power-state handling of a + * display that might be in a transition. + */ + static final int DISPLAY_PHASE_LAYOUT_TRANSITION = 0; + + /** + * The display is exposed to the rest of the system and its power state is determined by a + * power-request from PowerManager. + */ + static final int DISPLAY_PHASE_ENABLED = 1; + + @IntDef(prefix = {"DISPLAY_PHASE" }, value = { + DISPLAY_PHASE_DISABLED, + DISPLAY_PHASE_LAYOUT_TRANSITION, + DISPLAY_PHASE_ENABLED + }) + @interface DisplayPhase {} + // The layer stack we use when the display has been blanked to prevent any // of its content from appearing. private static final int BLANK_LAYER_STACK = -1; @@ -129,10 +157,12 @@ final class LogicalDisplay { private final Rect mTempDisplayRect = new Rect(); /** - * Indicates that the Logical display is enabled (default). See {@link #setEnabled} for - * more information. + * Indicates the current phase of the display. Generally, phases supersede any + * requests from PowerManager in DPC's calculation for the display state. Only when the + * phase is ENABLED does PowerManager's request for the display take effect. */ - private boolean mIsEnabled = true; + @DisplayPhase + private int mPhase = DISPLAY_PHASE_ENABLED; /** * The UID mappings for refresh rate override @@ -721,27 +751,32 @@ final class LogicalDisplay { return old; } + public void setPhase(@DisplayPhase int phase) { + mPhase = phase; + } + /** - * Sets the LogicalDisplay to be enabled or disabled. If the display is not enabled, - * the system will always set the display to power off, regardless of the global state of the - * device. - * TODO: b/170498827 - Remove when updateDisplayStateLocked is updated. + * Returns the currently set phase for this LogicalDisplay. Phases are used when transitioning + * from one device state to another. {@see LogicalDisplayMapper}. */ - public void setEnabled(boolean isEnabled) { - mIsEnabled = isEnabled; + @DisplayPhase + public int getPhase() { + return mPhase; } /** - * @return {@code true} iff the LogicalDisplay is enabled or {@code false} - * if disabled indicating that the display has been forced to be OFF. + * @return {@code true} if the LogicalDisplay is enabled or {@code false} + * if disabled indicating that the display should be hidden from the rest of the apps and + * framework. */ public boolean isEnabled() { - return mIsEnabled; + // DISPLAY_PHASE_LAYOUT_TRANSITION is still considered an 'enabled' phase. + return mPhase == DISPLAY_PHASE_ENABLED || mPhase == DISPLAY_PHASE_LAYOUT_TRANSITION; } public void dumpLocked(PrintWriter pw) { pw.println("mDisplayId=" + mDisplayId); - pw.println("mIsEnabled=" + mIsEnabled); + pw.println("mPhase=" + mPhase); pw.println("mLayerStack=" + mLayerStack); pw.println("mHasContent=" + mHasContent); pw.println("mDesiredDisplayModeSpecs={" + mDesiredDisplayModeSpecs + "}"); diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index fcfa674dcc4e..4c9a2d702114 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -16,18 +16,24 @@ package com.android.server.display; +import android.annotation.NonNull; +import android.content.Context; import android.hardware.devicestate.DeviceStateManager; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.os.SystemProperties; import android.text.TextUtils; import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.view.Display; import android.view.DisplayAddress; import android.view.DisplayInfo; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.display.LogicalDisplay.DisplayPhase; import com.android.server.display.layout.Layout; import java.io.PrintWriter; @@ -55,11 +61,20 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 3; public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4; public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5; + public static final int LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION = 6; public static final int DISPLAY_GROUP_EVENT_ADDED = 1; public static final int DISPLAY_GROUP_EVENT_CHANGED = 2; public static final int DISPLAY_GROUP_EVENT_REMOVED = 3; + private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 500; + + private static final int MSG_TRANSITION_TO_PENDING_DEVICE_STATE = 1; + + private static final int UPDATE_STATE_NEW = 0; + private static final int UPDATE_STATE_TRANSITION = 1; + private static final int UPDATE_STATE_UPDATED = 2; + /** * Temporary display info, used for comparing display configurations. */ @@ -76,6 +91,11 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final boolean mSingleDisplayDemoMode; /** + * True if the device can have more than one internal display on at a time. + */ + private final boolean mSupportsConcurrentInternalDisplays; + + /** * Map of all logical displays indexed by logical display id. * Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache. * TODO: multi-display - Move the aforementioned comment? @@ -89,13 +109,16 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final DisplayDeviceRepository mDisplayDeviceRepo; private final DeviceStateToLayoutMap mDeviceStateToLayoutMap; private final Listener mListener; + private final DisplayManagerService.SyncRoot mSyncRoot; + private final LogicalDisplayMapperHandler mHandler; /** * Has an entry for every logical display that the rest of the system has been notified about. * Any entry in here requires us to send a {@link LOGICAL_DISPLAY_EVENT_REMOVED} event when it - * is deleted or {@link LOGICAL_DISPLAY_EVENT_CHANGED} when it is changed. + * is deleted or {@link LOGICAL_DISPLAY_EVENT_CHANGED} when it is changed. The values are any + * of the {@code UPDATE_STATE_*} constant types. */ - private final SparseBooleanArray mUpdatedLogicalDisplays = new SparseBooleanArray(); + private final SparseIntArray mUpdatedLogicalDisplays = new SparseIntArray(); /** * Keeps track of all the display groups that we already told other people about. IOW, if a @@ -119,11 +142,18 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1; private Layout mCurrentLayout = null; private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE; + private int mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE; - LogicalDisplayMapper(DisplayDeviceRepository repo, Listener listener) { + LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo, + @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, + @NonNull Handler handler) { + mSyncRoot = syncRoot; + mHandler = new LogicalDisplayMapperHandler(handler.getLooper()); mDisplayDeviceRepo = repo; mListener = listener; mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false); + mSupportsConcurrentInternalDisplays = context.getResources().getBoolean( + com.android.internal.R.bool.config_supportsConcurrentInternalDisplays); mDisplayDeviceRepo.addListener(this); mDeviceStateToLayoutMap = new DeviceStateToLayoutMap(); } @@ -142,6 +172,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { if (DEBUG) { Slog.d(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked()); } + finishStateTransitionLocked(false /*force*/); updateLogicalDisplaysLocked(); break; @@ -166,7 +197,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { public LogicalDisplay getDisplayLocked(DisplayDevice device) { final int count = mLogicalDisplays.size(); for (int i = 0; i < count; i++) { - LogicalDisplay display = mLogicalDisplays.valueAt(i); + final LogicalDisplay display = mLogicalDisplays.valueAt(i); if (display.getPrimaryDisplayDeviceLocked() == device) { return display; } @@ -198,6 +229,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } } + @VisibleForTesting public int getDisplayGroupIdFromDisplayIdLocked(int displayId) { final LogicalDisplay display = getDisplayLocked(displayId); if (display == null) { @@ -225,7 +257,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { ipw.increaseIndent(); ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); - ipw.println("mCurrentLayout=" + mCurrentLayout); final int logicalDisplayCount = mLogicalDisplays.size(); @@ -244,19 +275,78 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } void setDeviceStateLocked(int state) { - if (state != mDeviceState) { - resetLayoutLocked(); - mDeviceState = state; - applyLayoutLocked(); - updateLogicalDisplaysLocked(); + Slog.i(TAG, "Requesting Transition to state: " + state); + // As part of a state transition, we may need to turn off some displays temporarily so that + // the transition is smooth. Plus, on some devices, only one internal displays can be + // on at a time. We use DISPLAY_PHASE_LAYOUT_TRANSITION to mark a display that needs to be + // temporarily turned off. + if (mDeviceState != DeviceStateManager.INVALID_DEVICE_STATE) { + resetLayoutLocked(mDeviceState, state, LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION); + } + mPendingDeviceState = state; + if (areAllTransitioningDisplaysOffLocked()) { + // Nothing to wait on, we're good to go + transitionToPendingStateLocked(); + return; + } + + if (DEBUG) { + Slog.d(TAG, "Postponing transition to state: " + mPendingDeviceState); + } + // Send the transitioning phase updates to DisplayManager so that the displays can + // start turning OFF in preparation for the new layout. + updateLogicalDisplaysLocked(); + mHandler.sendEmptyMessageDelayed(MSG_TRANSITION_TO_PENDING_DEVICE_STATE, + TIMEOUT_STATE_TRANSITION_MILLIS); + } + + private boolean areAllTransitioningDisplaysOffLocked() { + final int count = mLogicalDisplays.size(); + for (int i = 0; i < count; i++) { + final LogicalDisplay display = mLogicalDisplays.valueAt(i); + if (display.getPhase() != LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) { + continue; + } + + final DisplayDevice device = display.getPrimaryDisplayDeviceLocked(); + if (device != null) { + final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); + if (info.state != Display.STATE_OFF) { + return false; + } + } + } + return true; + } + + private void transitionToPendingStateLocked() { + resetLayoutLocked(mDeviceState, mPendingDeviceState, LogicalDisplay.DISPLAY_PHASE_ENABLED); + mDeviceState = mPendingDeviceState; + mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE; + applyLayoutLocked(); + updateLogicalDisplaysLocked(); + } + + private void finishStateTransitionLocked(boolean force) { + if (mPendingDeviceState == DeviceStateManager.INVALID_DEVICE_STATE) { + return; + } + + final boolean displaysOff = areAllTransitioningDisplaysOffLocked(); + if (displaysOff || force) { + transitionToPendingStateLocked(); + mHandler.removeMessages(MSG_TRANSITION_TO_PENDING_DEVICE_STATE); + } else if (DEBUG) { + Slog.d(TAG, "Not yet ready to transition to state=" + mPendingDeviceState + + " with displays-off=" + displaysOff + " and force=" + force); } } private void handleDisplayDeviceAddedLocked(DisplayDevice device) { DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked(); // Internal Displays need to have additional initialization. - // TODO: b/168208162 - This initializes a default dynamic display layout for INTERNAL - // devices, which will eventually just be a fallback in case no static layout definitions + // This initializes a default dynamic display layout for INTERNAL + // devices, which is used as a fallback in case no static layout definitions // exist or cannot be loaded. if (deviceInfo.type == Display.TYPE_INTERNAL) { initializeInternalDisplayDeviceLocked(device); @@ -289,7 +379,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { display.updateLocked(mDisplayDeviceRepo); final DisplayInfo newDisplayInfo = display.getDisplayInfoLocked(); - final boolean wasPreviouslyUpdated = mUpdatedLogicalDisplays.get(displayId); + final int updateState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW); + final boolean wasPreviouslyUpdated = updateState != UPDATE_STATE_NEW; // The display is no longer valid and needs to be removed. if (!display.isValidLocked()) { @@ -331,6 +422,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { assignDisplayGroupLocked(display); mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED); + } else if (updateState == UPDATE_STATE_TRANSITION) { + mLogicalDisplaysToUpdate.put(displayId, + LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION); + // Display frame rate overrides changed. } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) { mLogicalDisplaysToUpdate.put( @@ -347,7 +442,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } } - mUpdatedLogicalDisplays.put(displayId, true); + mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_UPDATED); } // Go through the groups and do the same thing. We do this after displays since group @@ -376,12 +471,13 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { // Send the display and display group updates in order by message type. This is important // to ensure that addition and removal notifications happen in the right order. + sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION); sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_ADDED); sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REMOVED); sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CHANGED); sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED); - sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_ADDED); sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_SWAPPED); + sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_ADDED); sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_CHANGED); sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_REMOVED); @@ -400,7 +496,14 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } final int id = mLogicalDisplaysToUpdate.keyAt(i); - mListener.onLogicalDisplayEventLocked(getDisplayLocked(id), msg); + final LogicalDisplay display = getDisplayLocked(id); + if (DEBUG) { + final DisplayDevice device = display.getPrimaryDisplayDeviceLocked(); + final String uniqueId = device == null ? "null" : device.getUniqueId(); + Slog.d(TAG, "Sending " + displayEventToString(msg) + " for display=" + id + + " with device=" + uniqueId); + } + mListener.onLogicalDisplayEventLocked(display, msg); if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) { // We wait until we sent the EVENT_REMOVED event before actually removing the // display. @@ -464,36 +567,81 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } /** - * Resets the current layout in preparation for a new layout. Layouts can specify if some - * displays should be disabled (OFF). When switching from one layout to another, we go - * through each of the displays and make sure any displays we might have disabled are - * enabled again. + * Goes through all the displays used in the layouts for the specified {@code fromState} and + * {@code toState} and applies the specified {@code phase}. When a new layout is requested, we + * put the displays that will change into a transitional phase so that they can all be turned + * OFF. Once all are confirmed OFF, then this method gets called again to reset the phase to + * normal operation. This helps to ensure that all display-OFF requests are made before + * display-ON which in turn hides any resizing-jank windows might incur when switching displays. + * + * @param fromState The state we are switching from. + * @param toState The state we are switching to. + * @param phase The new phase to apply to the displays. */ - private void resetLayoutLocked() { - final Layout layout = mDeviceStateToLayoutMap.get(mDeviceState); - for (int i = layout.size() - 1; i >= 0; i--) { - final Layout.Display displayLayout = layout.getAt(i); - final LogicalDisplay display = getDisplayLocked(displayLayout.getLogicalDisplayId()); - if (display != null) { - enableDisplayLocked(display, true); // Reset all displays back to enabled + private void resetLayoutLocked(int fromState, int toState, @DisplayPhase int phase) { + final Layout fromLayout = mDeviceStateToLayoutMap.get(fromState); + final Layout toLayout = mDeviceStateToLayoutMap.get(toState); + + final int count = mLogicalDisplays.size(); + for (int i = 0; i < count; i++) { + final LogicalDisplay logicalDisplay = mLogicalDisplays.valueAt(i); + final int displayId = logicalDisplay.getDisplayIdLocked(); + final DisplayDevice device = logicalDisplay.getPrimaryDisplayDeviceLocked(); + if (device == null) { + // If there's no device, then the logical display is due to be removed. Ignore it. + continue; + } + + // Grab the display associations this display-device has in the old layout and the + // new layout. + final DisplayAddress address = device.getDisplayDeviceInfoLocked().address; + + // Virtual displays do not have addresses. + final Layout.Display fromDisplay = + address != null ? fromLayout.getByAddress(address) : null; + final Layout.Display toDisplay = + address != null ? toLayout.getByAddress(address) : null; + + // If a layout doesn't mention a display-device at all, then the display-device defaults + // to enabled. This is why we treat null as "enabled" in the code below. + final boolean wasEnabled = fromDisplay == null || fromDisplay.isEnabled(); + final boolean willBeEnabled = toDisplay == null || toDisplay.isEnabled(); + + final boolean deviceHasNewLogicalDisplayId = fromDisplay != null && toDisplay != null + && fromDisplay.getLogicalDisplayId() != toDisplay.getLogicalDisplayId(); + + // We consider a display-device as changing/transition if + // 1) It's already marked as transitioning + // 2) It's going from enabled to disabled + // 3) It's enabled, but it's mapped to a new logical display ID. To the user this + // would look like apps moving from one screen to another since task-stacks stay + // with the logical display [ID]. + final boolean isTransitioning = + (logicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) + || (wasEnabled && !willBeEnabled) + || (wasEnabled && deviceHasNewLogicalDisplayId); + + if (isTransitioning) { + setDisplayPhase(logicalDisplay, phase); + if (phase == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) { + mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION); + } } } } - /** * Apply (or reapply) the currently selected display layout. */ private void applyLayoutLocked() { - final Layout layout = mDeviceStateToLayoutMap.get(mDeviceState); - mCurrentLayout = layout; - Slog.i(TAG, "Applying the display layout for device state(" + mDeviceState - + "): " + layout); + final Layout oldLayout = mCurrentLayout; + mCurrentLayout = mDeviceStateToLayoutMap.get(mDeviceState); + Slog.i(TAG, "Applying layout: " + mCurrentLayout + ", Previous layout: " + oldLayout); // Go through each of the displays in the current layout set. - final int size = layout.size(); + final int size = mCurrentLayout.size(); for (int i = 0; i < size; i++) { - final Layout.Display displayLayout = layout.getAt(i); + final Layout.Display displayLayout = mCurrentLayout.getAt(i); // If the underlying display-device we want to use for this display // doesn't exist, then skip it. This can happen at startup as display-devices @@ -521,8 +669,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { if (newDisplay != oldDisplay) { newDisplay.swapDisplaysLocked(oldDisplay); } - enableDisplayLocked(newDisplay, displayLayout.isEnabled()); + + if (!displayLayout.isEnabled()) { + setDisplayPhase(newDisplay, LogicalDisplay.DISPLAY_PHASE_DISABLED); + } } + } @@ -540,23 +692,23 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device); display.updateLocked(mDisplayDeviceRepo); mLogicalDisplays.put(displayId, display); - enableDisplayLocked(display, device != null); + setDisplayPhase(display, LogicalDisplay.DISPLAY_PHASE_ENABLED); return display; } - private void enableDisplayLocked(LogicalDisplay display, boolean isEnabled) { + private void setDisplayPhase(LogicalDisplay display, @DisplayPhase int phase) { final int displayId = display.getDisplayIdLocked(); final DisplayInfo info = display.getDisplayInfoLocked(); final boolean disallowSecondaryDisplay = mSingleDisplayDemoMode && (info.type != Display.TYPE_INTERNAL); - if (isEnabled && disallowSecondaryDisplay) { + if (phase != LogicalDisplay.DISPLAY_PHASE_DISABLED && disallowSecondaryDisplay) { Slog.i(TAG, "Not creating a logical display for a secondary display because single" + " display demo mode is enabled: " + display.getDisplayInfoLocked()); - isEnabled = false; + phase = LogicalDisplay.DISPLAY_PHASE_DISABLED; } - display.setEnabled(isEnabled); + display.setPhase(phase); } private int assignDisplayGroupIdLocked(boolean isOwnDisplayGroup) { @@ -564,14 +716,15 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } private void initializeInternalDisplayDeviceLocked(DisplayDevice device) { - // We always want to make sure that our default display layout creates a logical + // We always want to make sure that our default layout creates a logical // display for every internal display device that is found. // To that end, when we are notified of a new internal display, we add it to - // the default definition if it is not already there. - final Layout layoutSet = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT); + // the default layout definition if it is not already there. + final Layout layout = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT); final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); final boolean isDefault = (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0; - layoutSet.createDisplayLocked(info.address, isDefault, true /* isEnabled */); + final boolean isEnabled = isDefault || mSupportsConcurrentInternalDisplays; + layout.createDisplayLocked(info.address, isDefault, isEnabled); } private int assignLayerStackLocked(int displayId) { @@ -580,9 +733,45 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { return displayId; } + private String displayEventToString(int msg) { + switch(msg) { + case LOGICAL_DISPLAY_EVENT_ADDED: + return "added"; + case LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION: + return "transition"; + case LOGICAL_DISPLAY_EVENT_CHANGED: + return "changed"; + case LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED: + return "framerate_override"; + case LOGICAL_DISPLAY_EVENT_SWAPPED: + return "swapped"; + case LOGICAL_DISPLAY_EVENT_REMOVED: + return "removed"; + } + return null; + } + public interface Listener { void onLogicalDisplayEventLocked(LogicalDisplay display, int event); void onDisplayGroupEventLocked(int groupId, int event); void onTraversalRequested(); } + + private class LogicalDisplayMapperHandler extends Handler { + LogicalDisplayMapperHandler(Looper looper) { + super(looper, null, true /*async*/); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_TRANSITION_TO_PENDING_DEVICE_STATE: + synchronized (mSyncRoot) { + finishStateTransitionLocked(true /*force*/); + } + break; + } + } + } + } diff --git a/services/core/java/com/android/server/display/layout/Layout.java b/services/core/java/com/android/server/display/layout/Layout.java index ef336674df5d..e53aec12f186 100644 --- a/services/core/java/com/android/server/display/layout/Layout.java +++ b/services/core/java/com/android/server/display/layout/Layout.java @@ -19,6 +19,7 @@ package com.android.server.display.layout; import static android.view.Display.DEFAULT_DISPLAY; import android.annotation.NonNull; +import android.annotation.Nullable; import android.util.Slog; import android.view.DisplayAddress; @@ -100,11 +101,28 @@ public class Layout { * * @return The display corresponding to the specified display ID. */ + @Nullable public Display getById(int id) { for (int i = 0; i < mDisplays.size(); i++) { - Display layout = mDisplays.get(i); - if (id == layout.getLogicalDisplayId()) { - return layout; + Display display = mDisplays.get(i); + if (id == display.getLogicalDisplayId()) { + return display; + } + } + return null; + } + + /** + * @param address The display address to check. + * + * @return The display corresponding to the specified address. + */ + @Nullable + public Display getByAddress(@NonNull DisplayAddress address) { + for (int i = 0; i < mDisplays.size(); i++) { + Display display = mDisplays.get(i); + if (address.equals(display.getAddress())) { + return display; } } return null; 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 91581997849e..7c013e0bf330 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -52,7 +52,6 @@ import java.io.PrintWriter; import java.nio.ByteBuffer; import java.nio.NioUtils; import java.nio.channels.FileChannel; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -249,8 +248,6 @@ public final class FontManagerService extends IFontManager.Stub { // If apk verity is supported, fs-verity should be available. if (!VerityUtils.isFsVeritySupported()) return null; return new UpdatableFontDir(new File(FONT_FILES_DIR), - Arrays.asList(new File(SystemFonts.SYSTEM_FONT_DIR), - new File(SystemFonts.OEM_FONT_DIR)), new OtfFontFileParser(), new FsverityUtilImpl()); } @@ -323,7 +320,7 @@ public final class FontManagerService extends IFontManager.Stub { return Collections.emptyMap(); } synchronized (mUpdatableFontDirLock) { - return mUpdatableFontDir.getFontFileMap(); + return mUpdatableFontDir.getPostScriptMap(); } } 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 b39bd7db797d..38d1aab70550 100644 --- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java @@ -43,6 +43,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.function.Supplier; /** @@ -117,12 +118,12 @@ final class UpdatableFontDir { * randomized dir. The font file path would be {@code mFilesDir/~~{randomStr}/{fontFileName}}. */ private final File mFilesDir; - private final List<File> mPreinstalledFontDirs; private final FontFileParser mParser; private final FsverityUtil mFsverityUtil; private final File mConfigFile; private final File mTmpConfigFile; private final Supplier<Long> mCurrentTimeSupplier; + private final Function<Map<String, File>, FontConfig> mConfigSupplier; private long mLastModifiedMillis; private int mConfigVersion; @@ -134,22 +135,24 @@ final class UpdatableFontDir { */ private final ArrayMap<String, FontFileInfo> mFontFileInfoMap = new ArrayMap<>(); - UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser, - FsverityUtil fsverityUtil) { - this(filesDir, preinstalledFontDirs, parser, fsverityUtil, new File(CONFIG_XML_FILE), - () -> System.currentTimeMillis()); + UpdatableFontDir(File filesDir, FontFileParser parser, FsverityUtil fsverityUtil) { + this(filesDir, parser, fsverityUtil, new File(CONFIG_XML_FILE), + () -> System.currentTimeMillis(), + (map) -> SystemFonts.getSystemFontConfig(map, 0, 0) + ); } // For unit testing - UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser, - FsverityUtil fsverityUtil, File configFile, Supplier<Long> currentTimeSupplier) { + UpdatableFontDir(File filesDir, FontFileParser parser, FsverityUtil fsverityUtil, + File configFile, Supplier<Long> currentTimeSupplier, + Function<Map<String, File>, FontConfig> configSupplier) { mFilesDir = filesDir; - mPreinstalledFontDirs = preinstalledFontDirs; mParser = parser; mFsverityUtil = fsverityUtil; mConfigFile = configFile; mTmpConfigFile = new File(configFile.getAbsoluteFile() + ".tmp"); mCurrentTimeSupplier = currentTimeSupplier; + mConfigSupplier = configSupplier; } /** @@ -174,6 +177,7 @@ final class UpdatableFontDir { File[] dirs = mFilesDir.listFiles(); if (dirs == null) return; + FontConfig fontConfig = getSystemFontConfig(); for (File dir : dirs) { if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) { Slog.e(TAG, "Unexpected dir found: " + dir); @@ -190,7 +194,7 @@ final class UpdatableFontDir { return; } FontFileInfo fontFileInfo = validateFontFile(files[0]); - addFileToMapIfSameOrNewer(fontFileInfo, true /* deleteOldFile */); + addFileToMapIfSameOrNewer(fontFileInfo, fontConfig, true /* deleteOldFile */); } success = true; } catch (Throwable t) { @@ -302,7 +306,7 @@ final class UpdatableFontDir { * Installs a new font file, or updates an existing font file. * * <p>The new font will be immediately available for new Zygote-forked processes through - * {@link #getFontFileMap()}. Old font files will be kept until next system server reboot, + * {@link #getPostScriptMap()}. Old font files will be kept until next system server reboot, * because existing Zygote-forked processes have paths to old font files. * * @param fd A file descriptor to the font file. @@ -373,7 +377,8 @@ final class UpdatableFontDir { "Failed to change mode to 711", e); } FontFileInfo fontFileInfo = validateFontFile(newFontFile); - if (!addFileToMapIfSameOrNewer(fontFileInfo, false)) { + FontConfig fontConfig = getSystemFontConfig(); + if (!addFileToMapIfSameOrNewer(fontFileInfo, fontConfig, false)) { throw new SystemFontException( FontManager.RESULT_ERROR_DOWNGRADING, "Downgrading font file is forbidden."); @@ -417,14 +422,15 @@ final class UpdatableFontDir { * equal to or higher than the revision of currently used font file (either in * {@link #mFontFileInfoMap} or {@link #mPreinstalledFontDirs}). */ - private boolean addFileToMapIfSameOrNewer(FontFileInfo fontFileInfo, boolean deleteOldFile) { + private boolean addFileToMapIfSameOrNewer(FontFileInfo fontFileInfo, FontConfig fontConfig, + boolean deleteOldFile) { FontFileInfo existingInfo = lookupFontFileInfo(fontFileInfo.getPostScriptName()); final boolean shouldAddToMap; if (existingInfo == null) { // We got a new updatable font. We need to check if it's newer than preinstalled fonts. // Note that getPreinstalledFontRevision() returns -1 if there is no preinstalled font // with 'name'. - long preInstalledRev = getPreinstalledFontRevision(fontFileInfo.getFile().getName()); + long preInstalledRev = getPreinstalledFontRevision(fontFileInfo, fontConfig); shouldAddToMap = preInstalledRev <= fontFileInfo.getRevision(); } else { shouldAddToMap = existingInfo.getRevision() <= fontFileInfo.getRevision(); @@ -442,21 +448,33 @@ final class UpdatableFontDir { return shouldAddToMap; } - private long getPreinstalledFontRevision(String name) { - long maxRevision = -1; - for (File dir : mPreinstalledFontDirs) { - File preinstalledFontFile = new File(dir, name); - if (!preinstalledFontFile.exists()) continue; - long revision = getFontRevision(preinstalledFontFile); - if (revision == -1) { - Slog.w(TAG, "Invalid preinstalled font file"); - continue; - } - if (revision > maxRevision) { - maxRevision = revision; + private long getPreinstalledFontRevision(FontFileInfo info, FontConfig fontConfig) { + String psName = info.getPostScriptName(); + FontConfig.Font targetFont = null; + for (int i = 0; i < fontConfig.getFontFamilies().size(); i++) { + FontConfig.FontFamily family = fontConfig.getFontFamilies().get(i); + for (int j = 0; j < family.getFontList().size(); ++j) { + FontConfig.Font font = family.getFontList().get(j); + if (font.getPostScriptName().equals(psName)) { + targetFont = font; + break; + } } } - return maxRevision; + if (targetFont == null) { + return -1; + } + + File preinstalledFontFile = targetFont.getOriginalFile() != null + ? targetFont.getOriginalFile() : targetFont.getFile(); + if (!preinstalledFontFile.exists()) { + return -1; + } + long revision = getFontRevision(preinstalledFontFile); + if (revision == -1) { + Slog.w(TAG, "Invalid preinstalled font file"); + } + return revision; } /** @@ -515,17 +533,17 @@ final class UpdatableFontDir { null, FontConfig.FontFamily.VARIANT_DEFAULT); } - Map<String, File> getFontFileMap() { + Map<String, File> getPostScriptMap() { Map<String, File> map = new ArrayMap<>(); for (int i = 0; i < mFontFileInfoMap.size(); ++i) { - File file = mFontFileInfoMap.valueAt(i).getFile(); - map.put(file.getName(), file); + FontFileInfo info = mFontFileInfoMap.valueAt(i); + map.put(info.getPostScriptName(), info.getFile()); } return map; } /* package */ FontConfig getSystemFontConfig() { - FontConfig config = SystemFonts.getSystemFontConfig(getFontFileMap(), 0, 0); + FontConfig config = mConfigSupplier.apply(getPostScriptMap()); PersistentSystemFontConfig.Config persistentConfig = readPersistentConfig(); List<FontUpdateRequest.Family> families = persistentConfig.fontFamilies; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java index 2bf74c95bcce..5802e53c67f1 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java @@ -232,7 +232,8 @@ public class HdmiCecMessageValidator { DEST_DIRECT); // Messages for Feature Discovery. - addValidationInfo(Constants.MESSAGE_GIVE_FEATURES, noneValidator, DEST_DIRECT); + addValidationInfo(Constants.MESSAGE_GIVE_FEATURES, noneValidator, + DEST_DIRECT | SRC_UNREGISTERED); addValidationInfo(Constants.MESSAGE_REPORT_FEATURES, new VariableLengthValidator(4, 14), DEST_BROADCAST); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 754fa25191b0..77de187e57ca 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -422,6 +422,9 @@ public class HdmiControlService extends SystemService { // Set to true if the logical address allocation is completed. private boolean mAddressAllocated = false; + // Whether a CEC-enabled sink is connected to the playback device + private boolean mIsCecAvailable = false; + // Object that handles logging statsd atoms. // Use getAtomWriter() instead of accessing directly, to allow dependency injection for testing. private HdmiCecAtomWriter mAtomWriter = new HdmiCecAtomWriter(); @@ -2229,6 +2232,7 @@ public class HdmiControlService extends SystemService { pw.println("mProhibitMode: " + mProhibitMode); pw.println("mPowerStatus: " + mPowerStatusController.getPowerStatus()); + pw.println("mIsCecAvailable: " + mIsCecAvailable); pw.println("mCecVersion: " + mCecVersion); // System settings @@ -2450,7 +2454,7 @@ public class HdmiControlService extends SystemService { if (hdmiCecEnabled != HdmiControlManager.HDMI_CEC_CONTROL_ENABLED) { return false; } - return true; + return mIsCecAvailable; } @ServiceThreadOnly @@ -2835,24 +2839,24 @@ public class HdmiControlService extends SystemService { private void invokeHdmiControlStatusChangeListenerLocked( Collection<IHdmiControlStatusChangeListener> listeners, @HdmiControlManager.HdmiCecControl int isEnabled) { - if (listeners.isEmpty()) { - return; - } if (isEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED) { queryDisplayStatus(new IHdmiControlCallback.Stub() { public void onComplete(int status) { - boolean isAvailable = true; if (status == HdmiControlManager.POWER_STATUS_UNKNOWN || status == HdmiControlManager.RESULT_EXCEPTION || status == HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE) { - isAvailable = false; + mIsCecAvailable = false; + } else { + mIsCecAvailable = true; } - invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled, isAvailable); } }); - return; + } else { + mIsCecAvailable = false; + } + if (!listeners.isEmpty()) { + invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled, mIsCecAvailable); } - invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled, false); } private void invokeHdmiControlStatusChangeListenerLocked( diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 0a800e9fc3a1..c51571a20bb1 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -270,6 +270,8 @@ public class InputManagerService extends IInputManager.Stub private final Object mAssociationsLock = new Object(); @GuardedBy("mAssociationLock") private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<String, Integer>(); + @GuardedBy("mAssociationLock") + private final Map<String, String> mUniqueIdAssociations = new ArrayMap<>(); private static native long nativeInit(InputManagerService service, Context context, MessageQueue messageQueue); @@ -340,6 +342,7 @@ public class InputManagerService extends IInputManager.Stub boolean enabled); private static native boolean nativeCanDispatchToDisplay(long ptr, int deviceId, int displayId); private static native void nativeNotifyPortAssociationsChanged(long ptr); + private static native void nativeChangeUniqueIdAssociation(long ptr); private static native void nativeSetMotionClassifierEnabled(long ptr, boolean enabled); private static native InputSensorInfo[] nativeGetSensorList(long ptr, int deviceId); private static native boolean nativeFlushSensor(long ptr, int deviceId, int sensorType); @@ -2222,10 +2225,10 @@ public class InputManagerService extends IInputManager.Stub @Override // Binder call public void addPortAssociation(@NonNull String inputPort, int displayPort) { if (!checkCallingPermission( - android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT, + android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY, "addPortAssociation()")) { throw new SecurityException( - "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT permission"); + "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission"); } Objects.requireNonNull(inputPort); @@ -2243,10 +2246,10 @@ public class InputManagerService extends IInputManager.Stub @Override // Binder call public void removePortAssociation(@NonNull String inputPort) { if (!checkCallingPermission( - android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT, + android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY, "clearPortAssociations()")) { throw new SecurityException( - "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY_BY_PORT permission"); + "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission"); } Objects.requireNonNull(inputPort); @@ -2256,6 +2259,49 @@ public class InputManagerService extends IInputManager.Stub nativeNotifyPortAssociationsChanged(mPtr); } + /** + * Add a runtime association between the input device name and the display unique id. + * @param inputDeviceName The name of the input device. + * @param displayUniqueId The unique id of the associated display. + */ + @Override // Binder call + public void addUniqueIdAssociation(@NonNull String inputDeviceName, + @NonNull String displayUniqueId) { + if (!checkCallingPermission( + android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY, + "addNameAssociation()")) { + throw new SecurityException( + "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission"); + } + + Objects.requireNonNull(inputDeviceName); + Objects.requireNonNull(displayUniqueId); + synchronized (mAssociationsLock) { + mUniqueIdAssociations.put(inputDeviceName, displayUniqueId); + } + nativeChangeUniqueIdAssociation(mPtr); + } + + /** + * Remove the runtime association between the input device and the display. + * @param inputDeviceName The port of the input device to be cleared. + */ + @Override // Binder call + public void removeUniqueIdAssociation(@NonNull String inputDeviceName) { + if (!checkCallingPermission( + android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY, + "removeUniqueIdAssociation()")) { + throw new SecurityException( + "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission"); + } + + Objects.requireNonNull(inputDeviceName); + synchronized (mAssociationsLock) { + mUniqueIdAssociations.remove(inputDeviceName); + } + nativeChangeUniqueIdAssociation(mPtr); + } + @Override // Binder call public InputSensorInfo[] getSensorList(int deviceId) { return nativeGetSensorList(mPtr, deviceId); @@ -2790,13 +2836,13 @@ public class InputManagerService extends IInputManager.Stub * key. * @return Flattened list */ - private static List<String> flatten(@NonNull Map<String, Integer> map) { + private static <T> String[] flatten(@NonNull Map<String, T> map) { final List<String> list = new ArrayList<>(map.size() * 2); map.forEach((k, v)-> { list.add(k); list.add(v.toString()); }); - return list; + return list.toArray(new String[0]); } /** @@ -2828,8 +2874,17 @@ public class InputManagerService extends IInputManager.Stub associations.putAll(mRuntimeAssociations); } - final List<String> associationList = flatten(associations); - return associationList.toArray(new String[0]); + return flatten(associations); + } + + // Native callback + private String[] getInputUniqueIdAssociations() { + final Map<String, String> associations; + synchronized (mAssociationsLock) { + associations = new HashMap<>(mUniqueIdAssociations); + } + + return flatten(associations); } /** diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index 3ac95d71de3d..bfb9db828131 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -125,6 +125,11 @@ public abstract class InputMethodManagerInternal { public abstract void removeImeSurface(); /** + * Updates the IME visibility, back disposition and show IME picker status for SystemUI. + */ + public abstract void updateImeWindowStatus(); + + /** * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing. */ private static final InputMethodManagerInternal NOP = @@ -175,6 +180,10 @@ public abstract class InputMethodManagerInternal { @Override public void removeImeSurface() { } + + @Override + public void updateImeWindowStatus() { + } }; /** diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 3ba17f509c94..c7c681b78d2e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -248,6 +248,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_CREATE_SESSION = 1050; static final int MSG_REMOVE_IME_SURFACE = 1060; static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061; + static final int MSG_UPDATE_IME_WINDOW_STATUS = 1070; static final int MSG_START_INPUT = 2000; @@ -2940,6 +2941,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + private void updateImeWindowStatus() { + synchronized (mMethodMap) { + updateSystemUiLocked(); + } + } + void updateSystemUiLocked() { updateSystemUiLocked(mImeWindowVis, mBackDisposition); } @@ -3348,68 +3355,100 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @NonNull @Override + public void reportWindowGainedFocusAsync( + boolean nextFocusHasConnection, IInputMethodClient client, IBinder windowToken, + @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, + int windowFlags, int unverifiedTargetSdkVersion) { + final int startInputReason = nextFocusHasConnection + ? StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION + : StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION; + try { + startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken, + startInputFlags, softInputMode, windowFlags, null /* attribute */, + null /* inputContext */, 0 /* missingMethods */, unverifiedTargetSdkVersion); + } catch (Throwable t) { + if (client != null) { + try { + client.throwExceptionFromSystem(t.getMessage()); + } catch (RemoteException ignore) { } + } + } + } + + @NonNull + @Override public void startInputOrWindowGainedFocus( @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext, @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion, IInputBindResultResultCallback resultCallback) { - CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () -> { - if (windowToken == null) { - Slog.e(TAG, "windowToken cannot be null."); - return InputBindResult.NULL; - } - try { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, - "IMMS.startInputOrWindowGainedFocus"); - ImeTracing.getInstance().triggerManagerServiceDump( - "InputMethodManagerService#startInputOrWindowGainedFocus"); - final int callingUserId = UserHandle.getCallingUserId(); - final int userId; - if (attribute != null && attribute.targetInputMethodUser != null - && attribute.targetInputMethodUser.getIdentifier() != callingUserId) { - mContext.enforceCallingPermission( - Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "Using EditorInfo.targetInputMethodUser requires" - + " INTERACT_ACROSS_USERS_FULL."); - userId = attribute.targetInputMethodUser.getIdentifier(); - if (!mUserManagerInternal.isUserRunning(userId)) { - // There is a chance that we hit here because of race condition. Let's just - // return an error code instead of crashing the caller process, which at - // least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an - // important process. - Slog.e(TAG, "User #" + userId + " is not running."); - return InputBindResult.INVALID_USER; - } - } else { - userId = callingUserId; - } - final InputBindResult result; - synchronized (mMethodMap) { - final long ident = Binder.clearCallingIdentity(); - try { - result = startInputOrWindowGainedFocusInternalLocked(startInputReason, - client, windowToken, startInputFlags, softInputMode, windowFlags, - attribute, inputContext, missingMethods, unverifiedTargetSdkVersion, - userId); - } finally { - Binder.restoreCallingIdentity(ident); - } + CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () -> + startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken, + startInputFlags, softInputMode, windowFlags, attribute, inputContext, + missingMethods, unverifiedTargetSdkVersion)); + } + + @NonNull + private InputBindResult startInputOrWindowGainedFocusInternal( + @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken, + @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, + int windowFlags, @Nullable EditorInfo attribute, @Nullable IInputContext inputContext, + @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) { + if (windowToken == null) { + Slog.e(TAG, "windowToken cannot be null."); + return InputBindResult.NULL; + } + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "IMMS.startInputOrWindowGainedFocus"); + ImeTracing.getInstance().triggerManagerServiceDump( + "InputMethodManagerService#startInputOrWindowGainedFocus"); + final int callingUserId = UserHandle.getCallingUserId(); + final int userId; + if (attribute != null && attribute.targetInputMethodUser != null + && attribute.targetInputMethodUser.getIdentifier() != callingUserId) { + mContext.enforceCallingPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "Using EditorInfo.targetInputMethodUser requires" + + " INTERACT_ACROSS_USERS_FULL."); + userId = attribute.targetInputMethodUser.getIdentifier(); + if (!mUserManagerInternal.isUserRunning(userId)) { + // There is a chance that we hit here because of race condition. Let's just + // return an error code instead of crashing the caller process, which at + // least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an + // important process. + Slog.e(TAG, "User #" + userId + " is not running."); + return InputBindResult.INVALID_USER; } - if (result == null) { - // This must never happen, but just in case. - Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason=" - + InputMethodDebug.startInputReasonToString(startInputReason) - + " windowFlags=#" + Integer.toHexString(windowFlags) - + " editorInfo=" + attribute); - return InputBindResult.NULL; + } else { + userId = callingUserId; + } + final InputBindResult result; + synchronized (mMethodMap) { + final long ident = Binder.clearCallingIdentity(); + try { + result = startInputOrWindowGainedFocusInternalLocked(startInputReason, + client, windowToken, startInputFlags, softInputMode, windowFlags, + attribute, inputContext, missingMethods, unverifiedTargetSdkVersion, + userId); + } finally { + Binder.restoreCallingIdentity(ident); } - - return result; - } finally { - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } - }); + if (result == null) { + // This must never happen, but just in case. + Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason=" + + InputMethodDebug.startInputReasonToString(startInputReason) + + " windowFlags=#" + Integer.toHexString(windowFlags) + + " editorInfo=" + attribute); + return InputBindResult.NULL; + } + + return result; + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } } @NonNull @@ -4537,6 +4576,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } return true; } + case MSG_UPDATE_IME_WINDOW_STATUS: { + synchronized (mMethodMap) { + updateSystemUiLocked(); + } + return true; + } // --------------------------------------------------------- case MSG_START_INPUT: { @@ -5202,6 +5247,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void removeImeSurface() { mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE)); } + + @Override + public void updateImeWindowStatus() { + mService.mHandler.sendMessage( + mService.mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS)); + } } @BinderThread diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java index e25b03481934..403187bb53ff 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java @@ -221,7 +221,6 @@ public class InputMethodMenuController { */ @VisibleForTesting public Context getSettingsContext(int displayId) { - // TODO(b/178462039): Cover the case when IME is moved to another ImeContainer. if (mSettingsContext == null || mSettingsContext.getDisplayId() != displayId) { final Context systemUiContext = ActivityThread.currentActivityThread() .createSystemUiContext(displayId); diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 885093d61486..0fc91bac59f5 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -241,6 +241,10 @@ public final class MultiClientInputMethodManagerService { public void removeImeSurface() { reportNotSupported(); } + + @Override + public void updateImeWindowStatus() { + } }); } @@ -1623,6 +1627,33 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override + public void reportWindowGainedFocusAsync( + boolean nextFocusHasConnection, + @Nullable IInputMethodClient client, + @Nullable IBinder windowToken, + @StartInputFlags int startInputFlags, + @SoftInputModeFlags int softInputMode, + int windowFlags, + int unverifiedTargetSdkVersion) { + final int startInputReason = nextFocusHasConnection + ? StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION + : StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION; + try { + startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken, + startInputFlags, softInputMode, windowFlags, null /* editorInfo */, + null /* inputContext */, 0 /* missingMethods */, + unverifiedTargetSdkVersion); + } catch (Throwable t) { + if (client != null) { + try { + client.throwExceptionFromSystem(t.getMessage()); + } catch (RemoteException ignore) { } + } + } + } + + @BinderThread + @Override public void startInputOrWindowGainedFocus( @StartInputReason int startInputReason, @Nullable IInputMethodClient client, @@ -1637,8 +1668,8 @@ public final class MultiClientInputMethodManagerService { IInputBindResultResultCallback resultCallback) { CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () -> startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken, - startInputFlags, softInputMode, windowFlags, editorInfo, inputContext, - missingMethods, unverifiedTargetSdkVersion)); + startInputFlags, softInputMode, windowFlags, editorInfo, inputContext, + missingMethods, unverifiedTargetSdkVersion)); } @BinderThread diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 6cded508f5b0..d110839a2204 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -3637,11 +3637,12 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public void prepareRebootEscrow() { + public boolean prepareRebootEscrow() { if (!mRebootEscrowManager.prepareRebootEscrow()) { - return; + return false; } mStrongAuth.requireStrongAuth(STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE, USER_ALL); + return true; } @Override @@ -3650,12 +3651,13 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public void clearRebootEscrow() { + public boolean clearRebootEscrow() { if (!mRebootEscrowManager.clearRebootEscrow()) { - return; + return false; } mStrongAuth.noLongerRequireStrongAuth(STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE, USER_ALL); + return true; } @Override diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java index 6a5c2d89a4e2..a73c8e0c914a 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java @@ -311,7 +311,7 @@ class LockSettingsShellCommand extends ShellCommand { PasswordMetrics metrics = new PasswordMetrics( credential.isPattern() ? CREDENTIAL_TYPE_PATTERN : CREDENTIAL_TYPE_NONE); errors = PasswordMetrics.validatePasswordMetrics( - requiredMetrics, requiredComplexity, false /* isPin */, metrics); + requiredMetrics, requiredComplexity, metrics); } if (!errors.isEmpty()) { getOutPrintWriter().println( diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java index 29b5e81447f8..78219bcb9011 100644 --- a/services/core/java/com/android/server/notification/NotificationComparator.java +++ b/services/core/java/com/android/server/notification/NotificationComparator.java @@ -147,15 +147,16 @@ public class NotificationComparator } private boolean isImportantOngoing(NotificationRecord record) { - if (!isOngoing(record)) { + if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) { return false; } - - if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) { + if (isCallStyle(record)) { + return true; + } + if (!isOngoing(record)) { return false; } - - return isCall(record) || isMediaNotification(record); + return isCallCategory(record) || isMediaNotification(record); } protected boolean isImportantPeople(NotificationRecord record) { @@ -181,11 +182,16 @@ public class NotificationComparator return record.getNotification().hasMediaSession(); } - private boolean isCall(NotificationRecord record) { + private boolean isCallCategory(NotificationRecord record) { return record.isCategory(Notification.CATEGORY_CALL) && isDefaultPhoneApp(record.getSbn().getPackageName()); } + private boolean isCallStyle(NotificationRecord record) { + return "android.app.Notification$CallStyle".equals( + record.getNotification().extras.getString(Notification.EXTRA_TEMPLATE)); + } + private boolean isDefaultPhoneApp(String pkg) { if (mDefaultPhoneApp == null) { final TelecomManager telecomm = diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 1c50d41e45d3..a71722c93d48 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -6570,6 +6570,17 @@ public class NotificationManagerService extends SystemService { } } + if ("android.app.Notification$CallStyle".equals( + n.extras.getString(Notification.EXTRA_TEMPLATE))) { + boolean isForegroundService = (n.flags & FLAG_FOREGROUND_SERVICE) != 0; + boolean hasFullScreenIntent = n.fullScreenIntent != null; + if (!isForegroundService && !hasFullScreenIntent) { + throw new IllegalArgumentException(r.getKey() + " Not posted." + + " CallStyle notifications must either be for a foreground Service or" + + " use a fullScreenIntent."); + } + } + // snoozed apps if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) { MetricsLogger.action(r.getLogMaker() diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 4f527f26cbb3..cd352b5fb1be 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -35,6 +35,7 @@ import android.content.pm.parsing.component.ParsedInstrumentation; import android.content.pm.parsing.component.ParsedIntentInfo; import android.content.pm.parsing.component.ParsedMainComponent; import android.content.pm.parsing.component.ParsedProvider; +import android.os.Binder; import android.os.Process; import android.os.Trace; import android.os.UserHandle; @@ -51,6 +52,7 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.function.QuadFunction; import com.android.server.FgThread; import com.android.server.compat.CompatChange; import com.android.server.om.OverlayReferenceMapper; @@ -69,7 +71,6 @@ import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; import java.util.concurrent.Executor; -import java.util.function.Function; /** * The entity responsible for filtering visibility between apps based on declarations in their @@ -1447,14 +1448,20 @@ public class AppsFilter implements Watchable, Snappable { public void dumpQueries( PrintWriter pw, @Nullable Integer filteringAppId, DumpState dumpState, int[] users, - Function<Integer, String[]> getPackagesForUid) { + QuadFunction<Integer, Integer, Integer, Boolean, String[]> getPackagesForUid) { final SparseArray<String> cache = new SparseArray<>(); ToString<Integer> expandPackages = input -> { String cachedValue = cache.get(input); if (cachedValue == null) { - final String[] packagesForUid = getPackagesForUid.apply(input); + final int callingUid = Binder.getCallingUid(); + final int appId = UserHandle.getAppId(input); + String[] packagesForUid = null; + for (int i = 0, size = users.length; packagesForUid == null && i < size; i++) { + packagesForUid = getPackagesForUid.apply(callingUid, users[i], appId, + false /*isCallerInstantApp*/); + } if (packagesForUid == null) { - cachedValue = "[unknown app id " + input + "]"; + cachedValue = "[app id " + input + " not installed]"; } else { cachedValue = packagesForUid.length == 1 ? packagesForUid[0] : "[" + TextUtils.join(",", packagesForUid) + "]"; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 6b7e729671c5..bf114d8f7f34 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -883,6 +883,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return isDataLoaderInstallation() && params.dataLoaderParams.getType() == INCREMENTAL; } + private boolean isSystemDataLoaderInstallation() { + if (!isDataLoaderInstallation()) { + return false; + } + return SYSTEM_DATA_LOADER_PACKAGE.equals( + this.params.dataLoaderParams.getComponentName().getPackageName()); + } + /** * @return {@code true} iff the installing is app an device owner or affiliated profile owner. */ @@ -1058,9 +1066,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "DataLoader installation of APEX modules is not allowed."); } - boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals( - this.params.dataLoaderParams.getComponentName().getPackageName()); - if (systemDataLoader && mContext.checkCallingOrSelfPermission( + if (isSystemDataLoaderInstallation() && mContext.checkCallingOrSelfPermission( Manifest.permission.USE_SYSTEM_DATA_LOADERS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("You need the " @@ -2107,6 +2113,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { dispatchSessionFinished(error, detailedMessage, null); } + private void onSystemDataLoaderUnrecoverable() { + final PackageManagerService packageManagerService = mPm; + final String packageName = mPackageName; + if (TextUtils.isEmpty(packageName)) { + // The package has not been installed. + return; + } + mHandler.post(() -> { + if (packageManagerService.deletePackageX(packageName, + PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM, + PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/) + != PackageManager.DELETE_SUCCEEDED) { + Slog.e(TAG, "Failed to uninstall package with failed dataloader: " + packageName); + } + }); + } + /** * If session should be sealed, then it's sealed to prevent further modification. * If the session can't be sealed then it's destroyed. @@ -3740,6 +3763,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final DataLoaderParams params = this.params.dataLoaderParams; final boolean manualStartAndDestroy = !isIncrementalInstallation(); + final boolean systemDataLoader = isSystemDataLoaderInstallation(); final IDataLoaderStatusListener statusListener = new IDataLoaderStatusListener.Stub() { @Override public void onStatusChanged(int dataLoaderId, int status) { @@ -3751,10 +3775,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } if (mDestroyed || mDataLoaderFinished) { - // No need to worry about post installation + switch (status) { + case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE: + if (systemDataLoader) { + onSystemDataLoaderUnrecoverable(); + } + return; + } return; } - try { IDataLoader dataLoader = dataLoaderManager.getDataLoader(dataLoaderId); if (dataLoader == null) { @@ -3848,14 +3877,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS; healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS; - final boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals( - params.getComponentName().getPackageName()); - final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() { @Override public void onHealthStatus(int storageId, int status) { if (mDestroyed || mDataLoaderFinished) { - // No need to worry about post installation + // App's installed. + switch (status) { + case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY: + if (systemDataLoader) { + onSystemDataLoaderUnrecoverable(); + } + return; + } return; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index d2cbcf0598fc..8d6c14545351 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -4619,7 +4619,7 @@ public class PackageManagerService extends IPackageManager.Stub Integer filteringAppId = setting == null ? null : setting.appId; mAppsFilter.dumpQueries( pw, filteringAppId, dumpState, mUserManager.getUserIds(), - this::getPackagesForUid); + this::getPackagesForUidInternalBody); break; } @@ -21627,7 +21627,8 @@ public class PackageManagerService extends IPackageManager.Stub null /*disabledComponents*/, PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.UNINSTALL_REASON_UNKNOWN, - null /*harmfulAppWarning*/); + null /*harmfulAppWarning*/, + null /*splashScreenTheme*/); } mSettings.writeKernelMappingLPr(ps); } @@ -27701,6 +27702,23 @@ public class PackageManagerService extends IPackageManager.Stub return mSettings.getPackageLPr(packageName).getMimeGroup(mimeGroup); } + @Override + public void setSplashScreenTheme(@NonNull String packageName, @Nullable String themeId, + int userId) { + int callingUid = Binder.getCallingUid(); + PackageSetting packageSetting = getPackageSettingForUser(packageName, callingUid, userId); + if (packageSetting != null) { + packageSetting.setSplashScreenTheme(userId, themeId); + } + } + + @Override + public String getSplashScreenTheme(@NonNull String packageName, int userId) { + int callingUid = Binder.getCallingUid(); + PackageSetting packageSetting = getPackageSettingForUser(packageName, callingUid, userId); + return packageSetting != null ? packageSetting.getSplashScreenTheme(userId) : null; + } + /** * Temporary method that wraps mSettings.writeLPr() and calls mPermissionManager's * writeLegacyPermissionsTEMP() beforehand. diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 19b56b75d712..731d41c38f79 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -493,7 +493,8 @@ public abstract class PackageSettingBase extends SettingBase { ArrayMap<String, PackageUserState.SuspendParams> suspendParams, boolean instantApp, boolean virtualPreload, String lastDisableAppCaller, ArraySet<String> enabledComponents, ArraySet<String> disabledComponents, - int installReason, int uninstallReason, String harmfulAppWarning) { + int installReason, int uninstallReason, String harmfulAppWarning, + String splashScreenTheme) { PackageUserState state = modifyUserState(userId); state.ceDataInode = ceDataInode; state.enabled = enabled; @@ -512,6 +513,7 @@ public abstract class PackageSettingBase extends SettingBase { state.instantApp = instantApp; state.virtualPreload = virtualPreload; state.harmfulAppWarning = harmfulAppWarning; + state.splashScreenTheme = splashScreenTheme; onChanged(); } @@ -522,7 +524,8 @@ public abstract class PackageSettingBase extends SettingBase { otherState.instantApp, otherState.virtualPreload, otherState.lastDisableAppCaller, otherState.enabledComponents, otherState.disabledComponents, - otherState.installReason, otherState.uninstallReason, otherState.harmfulAppWarning); + otherState.installReason, otherState.uninstallReason, otherState.harmfulAppWarning, + otherState.splashScreenTheme); } ArraySet<String> getEnabledComponents(int userId) { @@ -723,6 +726,26 @@ public abstract class PackageSettingBase extends SettingBase { } /** + * @param userId the specified user to modify the theme for + * @param themeName the theme name to persist + * @see android.window.SplashScreen#setSplashScreenTheme(int) + */ + public void setSplashScreenTheme(@UserIdInt int userId, @Nullable String themeName) { + modifyUserState(userId).splashScreenTheme = themeName; + } + + /** + * @param userId the specified user to get the theme setting from + * @return the theme name previously persisted for the user or null + * if no splashscreen theme is persisted. + * @see android.window.SplashScreen#setSplashScreenTheme(int) + */ + @Nullable + public String getSplashScreenTheme(@UserIdInt int userId) { + return readUserState(userId).splashScreenTheme; + } + + /** * @return True if package is still being loaded, false if the package is fully loaded. */ public boolean isPackageLoading() { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index e409019e0da6..b6d4a5b88f8a 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -340,6 +340,7 @@ public final class Settings implements Watchable, Snappable { private static final String ATTR_INSTANT_APP = "instant-app"; private static final String ATTR_VIRTUAL_PRELOAD = "virtual-preload"; private static final String ATTR_HARMFUL_APP_WARNING = "harmful-app-warning"; + private static final String ATTR_SPLASH_SCREEN_THEME = "splash-screen-theme"; private static final String ATTR_PACKAGE_NAME = "packageName"; private static final String ATTR_FINGERPRINT = "fingerprint"; @@ -939,7 +940,9 @@ public final class Settings implements Watchable, Snappable { null /*disabledComponents*/, PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.UNINSTALL_REASON_UNKNOWN, - null /*harmfulAppWarning*/); + null, /*harmfulAppWarning*/ + null /*splashscreenTheme*/ + ); } } } @@ -1578,7 +1581,8 @@ public final class Settings implements Watchable, Snappable { null /*disabledComponents*/, PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.UNINSTALL_REASON_UNKNOWN, - null /*harmfulAppWarning*/); + null /*harmfulAppWarning*/, + null /* splashScreenTheme*/); } return; } @@ -1666,6 +1670,8 @@ public final class Settings implements Watchable, Snappable { PackageManager.INSTALL_REASON_UNKNOWN); final int uninstallReason = parser.getAttributeInt(null, ATTR_UNINSTALL_REASON, PackageManager.UNINSTALL_REASON_UNKNOWN); + final String splashScreenTheme = parser.getAttributeValue(null, + ATTR_SPLASH_SCREEN_THEME); ArraySet<String> enabledComponents = null; ArraySet<String> disabledComponents = null; @@ -1738,7 +1744,8 @@ public final class Settings implements Watchable, Snappable { ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched, hidden, distractionFlags, suspended, suspendParamsMap, instantApp, virtualPreload, enabledCaller, enabledComponents, - disabledComponents, installReason, uninstallReason, harmfulAppWarning); + disabledComponents, installReason, uninstallReason, harmfulAppWarning, + splashScreenTheme); mDomainVerificationManager.setLegacyUserState(name, userId, verifState); } else if (tagName.equals("preferred-activities")) { @@ -1995,6 +2002,10 @@ public final class Settings implements Watchable, Snappable { serializer.attribute(null, ATTR_HARMFUL_APP_WARNING, ustate.harmfulAppWarning); } + if (ustate.splashScreenTheme != null) { + serializer.attribute(null, ATTR_SPLASH_SCREEN_THEME, + ustate.splashScreenTheme); + } if (ustate.suspended) { for (int i = 0; i < ustate.suspendParams.size(); i++) { final String suspendingPackage = ustate.suspendParams.keyAt(i); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 007393ab80fb..678f0466af92 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -1992,10 +1992,11 @@ public class ShortcutService extends IShortcutService.Stub { packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts); verifyStates(); + + ret.complete(true); } catch (Exception e) { ret.completeExceptionally(e); } - ret.complete(true); }); return ret; } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 1e92ca60ce2f..50f958f558f7 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -211,6 +211,7 @@ final class DefaultPermissionGrantPolicy { NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_ADVERTISE); NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_CONNECT); NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_SCAN); + NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.UWB_RANGING); } private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1; diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 2d1178a3f116..0147790559e3 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -70,7 +70,9 @@ import android.app.admin.DevicePolicyManagerInternal; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.AttributionSource; +import android.content.AttributionSourceState; import android.content.Context; +import android.content.PermissionChecker; import android.content.pm.ApplicationInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; @@ -104,6 +106,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.permission.IOnPermissionsChangeListener; +import android.permission.IPermissionChecker; import android.permission.IPermissionManager; import android.permission.PermissionControllerManager; import android.permission.PermissionManager; @@ -164,6 +167,7 @@ import java.util.Objects; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -451,6 +455,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (permissionService == null) { permissionService = new PermissionManagerService(context, availableFeatures); ServiceManager.addService("permissionmgr", permissionService); + ServiceManager.addService("permission_checker", new PermissionCheckerService(context)); } return LocalServices.getService(PermissionManagerServiceInternal.class); } @@ -5414,4 +5419,419 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } } + + /** + * TODO: We need to consolidate these APIs either on PermissionManager or an extension + * object or a separate PermissionChecker service in context. The impartant part is to + * keep a single impl that is exposed to Java and native. We are not sure about the + * API shape so let is soak a bit. + */ + private static final class PermissionCheckerService extends IPermissionChecker.Stub { + // Cache for platform defined runtime permissions to avoid multi lookup (name -> info) + private static final ConcurrentHashMap<String, PermissionInfo> sPlatformPermissions + = new ConcurrentHashMap<>(); + + private final @NonNull Context mContext; + private final @NonNull AppOpsManager mAppOpsManager; + + PermissionCheckerService(@NonNull Context context) { + mContext = context; + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + } + + @Override + @PermissionChecker.PermissionResult + public int checkPermission(@NonNull String permission, + @NonNull AttributionSourceState attributionSourceState, @Nullable String message, + boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) { + Objects.requireNonNull(permission); + Objects.requireNonNull(attributionSourceState); + final AttributionSource attributionSource = new AttributionSource( + attributionSourceState); + final int result = checkPermission(mContext, permission, attributionSource, message, + forDataDelivery, startDataDelivery, fromDatasource); + // Finish any started op if some step in the attribution chain failed. + if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED) { + finishDataDelivery(AppOpsManager.permissionToOp(permission), + attributionSource.asState()); + } + return result; + } + + @Override + public void finishDataDelivery(@NonNull String op, + @NonNull AttributionSourceState attributionSourceState) { + if (op == null || attributionSourceState.packageName == null) { + return; + } + mAppOpsManager.finishProxyOp(op, new AttributionSource(attributionSourceState)); + if (attributionSourceState.next != null) { + finishDataDelivery(op, attributionSourceState.next[0]); + } + } + + @Override + @PermissionChecker.PermissionResult + public int checkOp(int op, AttributionSourceState attributionSource, + String message, boolean forDataDelivery, boolean startDataDelivery) { + int result = checkOp(mContext, op, new AttributionSource(attributionSource), message, + forDataDelivery, startDataDelivery); + if (result != PermissionChecker.PERMISSION_GRANTED && startDataDelivery) { + // Finish any started op if some step in the attribution chain failed. + finishDataDelivery(AppOpsManager.opToName(op), attributionSource); + } + return result; + } + + @PermissionChecker.PermissionResult + private static int checkPermission(@NonNull Context context, @NonNull String permission, + @NonNull AttributionSource attributionSource, @Nullable String message, + boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) { + PermissionInfo permissionInfo = sPlatformPermissions.get(permission); + + if (permissionInfo == null) { + try { + permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0); + if (PLATFORM_PACKAGE_NAME.equals(permissionInfo.packageName)) { + // Double addition due to concurrency is fine - the backing + // store is concurrent. + sPlatformPermissions.put(permission, permissionInfo); + } + } catch (PackageManager.NameNotFoundException ignored) { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + } + + if (permissionInfo.isAppOp()) { + return checkAppOpPermission(context, permission, attributionSource, message, + forDataDelivery, fromDatasource); + } + if (permissionInfo.isRuntime()) { + return checkRuntimePermission(context, permission, attributionSource, message, + forDataDelivery, startDataDelivery, fromDatasource); + } + + if (!fromDatasource && !checkPermission(context, permission, attributionSource.getUid(), + attributionSource.getRenouncedPermissions())) { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + + if (attributionSource.getNext() != null) { + return checkPermission(context, permission, + attributionSource.getNext(), message, forDataDelivery, + startDataDelivery, /*fromDatasource*/ false); + } + + return PermissionChecker.PERMISSION_GRANTED; + } + + @PermissionChecker.PermissionResult + private static int checkAppOpPermission(@NonNull Context context, + @NonNull String permission, @NonNull AttributionSource attributionSource, + @Nullable String message, boolean forDataDelivery, boolean fromDatasource) { + final int op = AppOpsManager.permissionToOpCode(permission); + if (op < 0) { + Slog.wtf(LOG_TAG, "Appop permission " + permission + " with no app op defined!"); + return PermissionChecker.PERMISSION_HARD_DENIED; + } + + AttributionSource current = attributionSource; + AttributionSource next = null; + + while (true) { + final boolean skipCurrentChecks = (fromDatasource || next != null); + + next = current.getNext(); + + // If the call is from a datasource we need to vet only the chain before it. This + // way we can avoid the datasource creating an attribution context for every call. + if (!(fromDatasource && current == attributionSource) + && next != null && !current.isTrusted(context)) { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + + // The access is for oneself if this is the single receiver of data + // after the data source or if this is the single attribution source + // in the chain if not from a datasource. + final boolean singleReceiverFromDatasource = (fromDatasource + && current == attributionSource && next != null && next.getNext() == null); + final boolean selfAccess = singleReceiverFromDatasource || next == null; + + final int opMode = performOpTransaction(context, op, current, message, + forDataDelivery, /*startDataDelivery*/ false, skipCurrentChecks, + selfAccess, singleReceiverFromDatasource); + + switch (opMode) { + case AppOpsManager.MODE_IGNORED: + case AppOpsManager.MODE_ERRORED: { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + case AppOpsManager.MODE_DEFAULT: { + if (!skipCurrentChecks && !checkPermission(context, permission, + attributionSource.getUid(), attributionSource + .getRenouncedPermissions())) { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + if (next != null && !checkPermission(context, permission, + next.getUid(), next.getRenouncedPermissions())) { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + } + } + + if (next == null || next.getNext() == null) { + return PermissionChecker.PERMISSION_GRANTED; + } + + current = next; + } + } + + private static int checkRuntimePermission(@NonNull Context context, + @NonNull String permission, @NonNull AttributionSource attributionSource, + @Nullable String message, boolean forDataDelivery, boolean startDataDelivery, + boolean fromDatasource) { + // Now let's check the identity chain... + final int op = AppOpsManager.permissionToOpCode(permission); + + AttributionSource current = attributionSource; + AttributionSource next = null; + + while (true) { + final boolean skipCurrentChecks = (fromDatasource || next != null); + next = current.getNext(); + + // If the call is from a datasource we need to vet only the chain before it. This + // way we can avoid the datasource creating an attribution context for every call. + if (!(fromDatasource && current == attributionSource) + && next != null && !current.isTrusted(context)) { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + + // If we already checked the permission for this one, skip the work + if (!skipCurrentChecks && !checkPermission(context, permission, + current.getUid(), current.getRenouncedPermissions())) { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + + if (next != null && !checkPermission(context, permission, + next.getUid(), next.getRenouncedPermissions())) { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + + if (op < 0) { + // Bg location is one-off runtime modifier permission and has no app op + if (sPlatformPermissions.contains(permission) + && !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission)) { + Slog.wtf(LOG_TAG, "Platform runtime permission " + permission + + " with no app op defined!"); + } + if (next == null) { + return PermissionChecker.PERMISSION_GRANTED; + } + current = next; + continue; + } + + // The access is for oneself if this is the single receiver of data + // after the data source or if this is the single attribution source + // in the chain if not from a datasource. + final boolean singleReceiverFromDatasource = (fromDatasource + && current == attributionSource && next != null && next.getNext() == null); + final boolean selfAccess = singleReceiverFromDatasource || next == null; + + final int opMode = performOpTransaction(context, op, current, message, + forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess, + singleReceiverFromDatasource); + + switch (opMode) { + case AppOpsManager.MODE_ERRORED: { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + case AppOpsManager.MODE_IGNORED: { + return PermissionChecker.PERMISSION_SOFT_DENIED; + } + } + + if (next == null || next.getNext() == null) { + return PermissionChecker.PERMISSION_GRANTED; + } + + current = next; + } + } + + private static boolean checkPermission(@NonNull Context context, @NonNull String permission, + int uid, @NonNull Set<String> renouncedPermissions) { + final boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1, + uid) == PackageManager.PERMISSION_GRANTED; + if (permissionGranted && renouncedPermissions.contains(permission) + && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS, + /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) { + return false; + } + return permissionGranted; + } + + private static int checkOp(@NonNull Context context, @NonNull int op, + @NonNull AttributionSource attributionSource, @Nullable String message, + boolean forDataDelivery, boolean startDataDelivery) { + if (op < 0 || attributionSource.getPackageName() == null) { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + + AttributionSource current = attributionSource; + AttributionSource next = null; + + while (true) { + final boolean skipCurrentChecks = (next != null); + next = current.getNext(); + + // If the call is from a datasource we need to vet only the chain before it. This + // way we can avoid the datasource creating an attribution context for every call. + if (next != null && !current.isTrusted(context)) { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + + // The access is for oneself if this is the single attribution source in the chain. + final boolean selfAccess = (next == null); + + final int opMode = performOpTransaction(context, op, current, message, + forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess, + /*fromDatasource*/ false); + + switch (opMode) { + case AppOpsManager.MODE_ERRORED: { + return PermissionChecker.PERMISSION_HARD_DENIED; + } + case AppOpsManager.MODE_IGNORED: { + return PermissionChecker.PERMISSION_SOFT_DENIED; + } + } + + if (next == null || next.getNext() == null) { + return PermissionChecker.PERMISSION_GRANTED; + } + + current = next; + } + } + + private static int performOpTransaction(@NonNull Context context, int op, + @NonNull AttributionSource attributionSource, @Nullable String message, + boolean forDataDelivery, boolean startDataDelivery, boolean skipProxyOperation, + boolean selfAccess, boolean singleReceiverFromDatasource) { + // We cannot perform app ops transactions without a package name. In all relevant + // places we pass the package name but just in case there is a bug somewhere we + // do a best effort to resolve the package from the UID (pick first without a loss + // of generality - they are in the same security sandbox). + final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); + final AttributionSource accessorSource = (!singleReceiverFromDatasource) + ? attributionSource : attributionSource.getNext(); + if (!forDataDelivery) { + final String resolvedAccessorPackageName = resolvePackageName(context, + accessorSource); + if (resolvedAccessorPackageName == null) { + return AppOpsManager.MODE_ERRORED; + } + final int opMode = appOpsManager.unsafeCheckOpRawNoThrow(op, + accessorSource.getUid(), resolvedAccessorPackageName); + final AttributionSource next = accessorSource.getNext(); + if (!selfAccess && opMode == AppOpsManager.MODE_ALLOWED && next != null) { + final String resolvedNextPackageName = resolvePackageName(context, next); + if (resolvedNextPackageName == null) { + return AppOpsManager.MODE_ERRORED; + } + return appOpsManager.unsafeCheckOpRawNoThrow(op, next.getUid(), + resolvedNextPackageName); + } + return opMode; + } else if (startDataDelivery) { + final AttributionSource resolvedAttributionSource = resolveAttributionSource( + context, accessorSource); + if (resolvedAttributionSource.getPackageName() == null) { + return AppOpsManager.MODE_ERRORED; + } + if (selfAccess) { + // If the datasource is not in a trusted platform component then in would not + // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that + // an app is exposing runtime permission protected data but cannot blame others + // in a trusted way which would not properly show in permission usage UIs. + // As a fallback we note a proxy op that blames the app and the datasource. + try { + return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(), + resolvedAttributionSource.getPackageName(), + /*startIfModeDefault*/ false, + resolvedAttributionSource.getAttributionTag(), + message); + } catch (SecurityException e) { + Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with" + + " platform defined runtime permission " + + AppOpsManager.opToPermission(op) + " while not having " + + Manifest.permission.UPDATE_APP_OPS_STATS); + return appOpsManager.startProxyOpNoThrow(op, attributionSource, message, + skipProxyOperation); + } + } else { + return appOpsManager.startProxyOpNoThrow(op, resolvedAttributionSource, message, + skipProxyOperation); + } + } else { + final AttributionSource resolvedAttributionSource = resolveAttributionSource( + context, accessorSource); + if (resolvedAttributionSource.getPackageName() == null) { + return AppOpsManager.MODE_ERRORED; + } + if (selfAccess) { + // If the datasource is not in a trusted platform component then in would not + // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that + // an app is exposing runtime permission protected data but cannot blame others + // in a trusted way which would not properly show in permission usage UIs. + // As a fallback we note a proxy op that blames the app and the datasource. + try { + return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(), + resolvedAttributionSource.getPackageName(), + resolvedAttributionSource.getAttributionTag(), + message); + } catch (SecurityException e) { + Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with" + + " platform defined runtime permission " + + AppOpsManager.opToPermission(op) + " while not having " + + Manifest.permission.UPDATE_APP_OPS_STATS); + return appOpsManager.noteProxyOpNoThrow(op, attributionSource, message, + skipProxyOperation); + } + } else { + return appOpsManager.noteProxyOpNoThrow(op, resolvedAttributionSource, message, + skipProxyOperation); + } + } + } + + private static @Nullable String resolvePackageName(@NonNull Context context, + @NonNull AttributionSource attributionSource) { + if (attributionSource.getPackageName() != null) { + return attributionSource.getPackageName(); + } + final String[] packageNames = context.getPackageManager().getPackagesForUid( + attributionSource.getUid()); + if (packageNames != null) { + // This is best effort if the caller doesn't pass a package. The security + // sandbox is UID, therefore we pick an arbitrary package. + return packageNames[0]; + } + // Last resort to handle special UIDs like root, etc. + return AppOpsManager.resolvePackageName(attributionSource.getUid(), + attributionSource.getPackageName()); + } + + private static @NonNull AttributionSource resolveAttributionSource( + @NonNull Context context, @NonNull AttributionSource attributionSource) { + if (attributionSource.getPackageName() != null) { + return attributionSource; + } + return attributionSource.withPackageName(resolvePackageName(context, + attributionSource)); + } + } } diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java index a8a6a723ce74..a5ba82fe162a 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java @@ -17,6 +17,7 @@ package com.android.server.pm.verify.domain; import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.Intent; @@ -33,6 +34,8 @@ import com.android.server.compat.PlatformCompat; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.List; +import java.util.Objects; +import java.util.function.BiFunction; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -44,6 +47,12 @@ public class DomainVerificationCollector { private static final int MAX_DOMAINS_BYTE_SIZE = 1024 * 1024; + private static final BiFunction<ArraySet<String>, String, Boolean> ARRAY_SET_COLLECTOR = + (set, domain) -> { + set.add(domain); + return null; + }; + @NonNull private final PlatformCompat mPlatformCompat; @@ -105,27 +114,62 @@ public class DomainVerificationCollector { return collectDomains(pkg, true /* checkAutoVerify */, false /* valid */); } + public boolean containsWebDomain(@NonNull AndroidPackage pkg, @NonNull String targetDomain) { + return collectDomains(pkg, false /* checkAutoVerify */, true /* valid */, null, + (BiFunction<Void, String, Boolean>) (unused, domain) -> { + if (Objects.equals(targetDomain, domain)) { + return true; + } + return null; + }) != null; + } + + public boolean containsAutoVerifyDomain(@NonNull AndroidPackage pkg, + @NonNull String targetDomain) { + return collectDomains(pkg, true /* checkAutoVerify */, true /* valid */, null, + (BiFunction<Void, String, Boolean>) (unused, domain) -> { + if (Objects.equals(targetDomain, domain)) { + return true; + } + return null; + }) != null; + } + @NonNull private ArraySet<String> collectDomains(@NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid) { + ArraySet<String> domains = new ArraySet<>(); + collectDomains(pkg, checkAutoVerify, valid, domains, ARRAY_SET_COLLECTOR); + return domains; + } + + @NonNull + private <InitialValue, ReturnValue> ReturnValue collectDomains(@NonNull AndroidPackage pkg, + boolean checkAutoVerify, boolean valid, @Nullable InitialValue initialValue, + @NonNull BiFunction<InitialValue, String, ReturnValue> domainCollector) { boolean restrictDomains = DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, RESTRICT_DOMAINS); if (restrictDomains) { - return collectDomainsInternal(pkg, checkAutoVerify, valid); + return collectDomainsInternal(pkg, checkAutoVerify, valid, initialValue, + domainCollector); } else { - return collectDomainsLegacy(pkg, checkAutoVerify, valid); + return collectDomainsLegacy(pkg, checkAutoVerify, valid, initialValue, domainCollector); } } /** * @see #RESTRICT_DOMAINS */ - private ArraySet<String> collectDomainsLegacy(@NonNull AndroidPackage pkg, - boolean checkAutoVerify, boolean valid) { + @Nullable + private <InitialValue, ReturnValue> ReturnValue collectDomainsLegacy( + @NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid, + @Nullable InitialValue initialValue, + @NonNull BiFunction<InitialValue, String, ReturnValue> domainCollector) { if (!checkAutoVerify) { // Per-domain user selection state doesn't have a V1 equivalent on S, so just use V2 - return collectDomainsInternal(pkg, false /* checkAutoVerify */, true /* valid */); + return collectDomainsInternal(pkg, false /* checkAutoVerify */, true /* valid */, + initialValue, domainCollector); } List<ParsedActivity> activities = pkg.getActivities(); @@ -148,11 +192,10 @@ public class DomainVerificationCollector { } if (!needsAutoVerify) { - return new ArraySet<>(); + return null; } } - ArraySet<String> domains = new ArraySet<>(); int totalSize = 0; boolean underMaxSize = true; for (int activityIndex = 0; activityIndex < activitiesSize && underMaxSize; @@ -169,22 +212,30 @@ public class DomainVerificationCollector { if (isValidHost(host) == valid) { totalSize += byteSizeOf(host); underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE; - domains.add(host); + ReturnValue returnValue = domainCollector.apply(initialValue, host); + if (returnValue != null) { + return returnValue; + } } } } } } - return domains; + return null; } /** * @see #RESTRICT_DOMAINS + * @param domainCollector Function to call with initialValue and a valid host. Should return + * a non-null value if the function should return immediately + * after the currently processed host. */ - private ArraySet<String> collectDomainsInternal(@NonNull AndroidPackage pkg, - boolean checkAutoVerify, boolean valid) { - ArraySet<String> domains = new ArraySet<>(); + @Nullable + private <InitialValue, ReturnValue> ReturnValue collectDomainsInternal( + @NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid, + @Nullable InitialValue initialValue, + @NonNull BiFunction<InitialValue, String, ReturnValue> domainCollector) { int totalSize = 0; boolean underMaxSize = true; @@ -226,13 +277,16 @@ public class DomainVerificationCollector { if (isValidHost(host) == valid) { totalSize += byteSizeOf(host); underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE; - domains.add(host); + ReturnValue returnValue = domainCollector.apply(initialValue, host); + if (returnValue != null) { + return returnValue; + } } } } } - return domains; + return null; } /** 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 39ed4882c69c..adf8f0d3e486 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 @@ -37,6 +37,7 @@ import com.android.server.pm.verify.domain.models.DomainVerificationPkgState; import com.android.server.pm.verify.domain.models.DomainVerificationStateMap; import java.util.Arrays; +import java.util.List; import java.util.function.Function; @SuppressWarnings("PointlessBooleanExpression") @@ -100,6 +101,70 @@ public class DomainVerificationDebug { } } + /** + * @param userIdToApprovalLevelToOwners Mapping of user ID to approval level to domain owners. + */ + public void printOwners(@NonNull IndentingPrintWriter writer, @NonNull String domain, + SparseArray<SparseArray<List<String>>> userIdToApprovalLevelToOwners) { + writer.println(domain + ":"); + writer.increaseIndent(); + + if (userIdToApprovalLevelToOwners.size() == 0) { + writer.println("none"); + writer.decreaseIndent(); + return; + } + + int usersSize = userIdToApprovalLevelToOwners.size(); + for (int userIndex = 0; userIndex < usersSize; userIndex++) { + int userId = userIdToApprovalLevelToOwners.keyAt(userIndex); + SparseArray<List<String>> approvalLevelToOwners = + userIdToApprovalLevelToOwners.valueAt(userIndex); + + if (approvalLevelToOwners.size() == 0) { + continue; + } + + boolean printedUserHeader = false; + int approvalsSize = approvalLevelToOwners.size(); + for (int approvalIndex = 0; approvalIndex < approvalsSize; approvalIndex++) { + int approvalLevel = approvalLevelToOwners.keyAt(approvalIndex); + if (approvalLevel < DomainVerificationManagerInternal.APPROVAL_LEVEL_UNVERIFIED) { + continue; + } + + if (!printedUserHeader) { + writer.println("User " + userId + ":"); + writer.increaseIndent(); + printedUserHeader = true; + } + + String approvalString = + DomainVerificationManagerInternal.approvalLevelToDebugString(approvalLevel); + List<String> owners = approvalLevelToOwners.valueAt(approvalIndex); + writer.println(approvalString + "[" + approvalLevel + "]" + ":"); + writer.increaseIndent(); + + if (owners.size() == 0) { + writer.println("none"); + writer.decreaseIndent(); + continue; + } + + int ownersSize = owners.size(); + for (int ownersIndex = 0; ownersIndex < ownersSize; ownersIndex++) { + writer.println(owners.get(ownersIndex)); + } + writer.decreaseIndent(); + } + + if (printedUserHeader) { + writer.decreaseIndent(); + } + } + writer.decreaseIndent(); + } + boolean printState(@NonNull IndentingPrintWriter writer, @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg, @NonNull ArrayMap<String, Integer> reusedMap, boolean wasHeaderPrinted) { @@ -120,7 +185,7 @@ public class DomainVerificationDebug { Signature[] signatures = pkg.getSigningDetails().signatures; String signaturesDigest = signatures == null ? null : Arrays.toString( PackageUtils.computeSignaturesSha256Digests( - pkg.getSigningDetails().signatures)); + pkg.getSigningDetails().signatures, ":")); writer.println(pkgState.getPackageName() + ":"); writer.increaseIndent(); 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 0f99e1963f28..5aed3670e5c7 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 @@ -57,6 +57,28 @@ public interface DomainVerificationManagerInternal { UUID DISABLED_ID = new UUID(0, 0); /** + * The app was not installed for the user. + */ + int APPROVAL_LEVEL_NOT_INSTALLED = -4; + + /** + * The app was not enabled for the user. + */ + int APPROVAL_LEVEL_DISABLED = -3; + + /** + * The app has not declared this domain in a valid web intent-filter in their manifest, and so + * would never be able to be approved for this domain. + */ + int APPROVAL_LEVEL_UNDECLARED = -2; + + /** + * The app has declared this domain as a valid autoVerify domain, but it failed or has not + * succeeded verification. + */ + int APPROVAL_LEVEL_UNVERIFIED = -1; + + /** * The app has not been approved for this domain and should never be able to open it through * an implicit web intent. */ @@ -117,10 +139,14 @@ public interface DomainVerificationManagerInternal { * by approval priority. A higher numerical value means the package should override all lower * values. This means that comparison using less/greater than IS valid. * - * Negative values are possible, although not implemented, reserved if explicit disable of a - * package for a domain needs to be tracked. + * Negative values are possible, used for tracking specific reasons for why an app doesn't have + * approval. */ @IntDef({ + APPROVAL_LEVEL_NOT_INSTALLED, + APPROVAL_LEVEL_DISABLED, + APPROVAL_LEVEL_UNDECLARED, + APPROVAL_LEVEL_UNVERIFIED, APPROVAL_LEVEL_NONE, APPROVAL_LEVEL_LEGACY_ASK, APPROVAL_LEVEL_LEGACY_ALWAYS, @@ -131,6 +157,33 @@ public interface DomainVerificationManagerInternal { @interface ApprovalLevel { } + static String approvalLevelToDebugString(@ApprovalLevel int level) { + switch (level) { + case APPROVAL_LEVEL_NOT_INSTALLED: + return "NOT_INSTALLED"; + case APPROVAL_LEVEL_DISABLED: + return "DISABLED"; + case APPROVAL_LEVEL_UNDECLARED: + return "UNDECLARED"; + case APPROVAL_LEVEL_UNVERIFIED: + return "UNVERIFIED"; + case APPROVAL_LEVEL_NONE: + return "NONE"; + case APPROVAL_LEVEL_LEGACY_ASK: + return "LEGACY_ASK"; + case APPROVAL_LEVEL_LEGACY_ALWAYS: + return "LEGACY_ALWAYS"; + case APPROVAL_LEVEL_SELECTION: + return "USER_SELECTION"; + case APPROVAL_LEVEL_VERIFIED: + return "VERIFIED"; + case APPROVAL_LEVEL_INSTANT_APP: + return "INSTANT_APP"; + default: + return "UNKNOWN"; + } + } + /** @see DomainVerificationManager#getDomainVerificationInfo(String) */ @Nullable @RequiresPermission(anyOf = { 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 a3e1a9cdc524..3a4b849bac3c 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 @@ -741,62 +741,21 @@ public class DomainVerificationService extends SystemService }); } + @NonNull public List<DomainOwner> getOwnersForDomain(@NonNull String domain, @UserIdInt int userId) { Objects.requireNonNull(domain); mEnforcer.assertOwnerQuerent(mConnection.getCallingUid(), mConnection.getCallingUserId(), userId); - SparseArray<List<String>> levelToPackages = new SparseArray<>(); return mConnection.withPackageSettingsReturningThrowing(pkgSettings -> { - // First, collect the raw approval level values - synchronized (mLock) { - final int size = mAttachedPkgStates.size(); - for (int index = 0; index < size; index++) { - DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); - String packageName = pkgState.getPackageName(); - PackageSetting pkgSetting = pkgSettings.apply(packageName); - if (pkgSetting == null) { - continue; - } - - int level = approvalLevelForDomain(pkgSetting, domain, userId, domain); - if (level <= APPROVAL_LEVEL_NONE) { - continue; - } - List<String> list = levelToPackages.get(level); - if (list == null) { - list = new ArrayList<>(); - levelToPackages.put(level, list); - } - list.add(packageName); - } - } - - final int size = levelToPackages.size(); - if (size == 0) { + SparseArray<List<String>> levelToPackages = getOwnersForDomainInternal(domain, false, + userId, pkgSettings); + if (levelToPackages.size() == 0) { return emptyList(); } - // Then sort them ascending by first installed time, with package name as tie breaker - for (int index = 0; index < size; index++) { - levelToPackages.valueAt(index).sort((first, second) -> { - PackageSetting firstPkgSetting = pkgSettings.apply(first); - PackageSetting secondPkgSetting = pkgSettings.apply(second); - - long firstInstallTime = - firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime(); - long secondInstallTime = - secondPkgSetting == null ? -1L : secondPkgSetting.getFirstInstallTime(); - - if (firstInstallTime != secondInstallTime) { - return (int) (firstInstallTime - secondInstallTime); - } - - return first.compareToIgnoreCase(second); - }); - } - List<DomainOwner> owners = new ArrayList<>(); + int size = levelToPackages.size(); for (int index = 0; index < size; index++) { int level = levelToPackages.keyAt(index); boolean overrideable = level <= APPROVAL_LEVEL_SELECTION; @@ -811,6 +770,69 @@ public class DomainVerificationService extends SystemService }); } + /** + * @param includeNegative See {@link #approvalLevelForDomain(PackageSetting, String, boolean, + * int, Object)}. + * @return Mapping of approval level to packages; packages are sorted by firstInstallTime. Null + * if no owners were found. + */ + @NonNull + private SparseArray<List<String>> getOwnersForDomainInternal(@NonNull String domain, + boolean includeNegative, @UserIdInt int userId, + @NonNull Function<String, PackageSetting> pkgSettingFunction) { + SparseArray<List<String>> levelToPackages = new SparseArray<>(); + // First, collect the raw approval level values + synchronized (mLock) { + final int size = mAttachedPkgStates.size(); + for (int index = 0; index < size; index++) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); + String packageName = pkgState.getPackageName(); + PackageSetting pkgSetting = pkgSettingFunction.apply(packageName); + if (pkgSetting == null) { + continue; + } + + int level = approvalLevelForDomain(pkgSetting, domain, includeNegative, userId, + domain); + if (!includeNegative && level <= APPROVAL_LEVEL_NONE) { + continue; + } + List<String> list = levelToPackages.get(level); + if (list == null) { + list = new ArrayList<>(); + levelToPackages.put(level, list); + } + list.add(packageName); + } + } + + final int size = levelToPackages.size(); + if (size == 0) { + return levelToPackages; + } + + // Then sort them ascending by first installed time, with package name as tie breaker + for (int index = 0; index < size; index++) { + levelToPackages.valueAt(index).sort((first, second) -> { + PackageSetting firstPkgSetting = pkgSettingFunction.apply(first); + PackageSetting secondPkgSetting = pkgSettingFunction.apply(second); + + long firstInstallTime = + firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime(); + long secondInstallTime = + secondPkgSetting == null ? -1L : secondPkgSetting.getFirstInstallTime(); + + if (firstInstallTime != secondInstallTime) { + return (int) (firstInstallTime - secondInstallTime); + } + + return first.compareToIgnoreCase(second); + }); + } + + return levelToPackages; + } + @NonNull @Override public UUID generateNewId() { @@ -1153,6 +1175,88 @@ public class DomainVerificationService extends SystemService } } + @Override + public void printOwnersForPackage(@NonNull IndentingPrintWriter writer, + @Nullable String packageName, @Nullable @UserIdInt Integer userId) + throws NameNotFoundException { + mConnection.withPackageSettingsThrowing(pkgSettings -> { + synchronized (mLock) { + if (packageName == null) { + int size = mAttachedPkgStates.size(); + for (int index = 0; index < size; index++) { + try { + printOwnersForPackage(writer, + mAttachedPkgStates.valueAt(index).getPackageName(), userId, + pkgSettings); + } catch (NameNotFoundException ignored) { + // When iterating packages, if one doesn't exist somehow, ignore + } + } + } else { + printOwnersForPackage(writer, packageName, userId, pkgSettings); + } + } + }); + } + + private void printOwnersForPackage(@NonNull IndentingPrintWriter writer, + @NonNull String packageName, @Nullable @UserIdInt Integer userId, + @NonNull Function<String, PackageSetting> pkgSettingFunction) + throws NameNotFoundException { + PackageSetting pkgSetting = pkgSettingFunction.apply(packageName); + AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg(); + if (pkg == null) { + throw DomainVerificationUtils.throwPackageUnavailable(packageName); + } + + ArraySet<String> domains = mCollector.collectAllWebDomains(pkg); + int size = domains.size(); + if (size == 0) { + return; + } + + writer.println(packageName + ":"); + writer.increaseIndent(); + + for (int index = 0; index < size; index++) { + printOwnersForDomain(writer, domains.valueAt(index), userId, pkgSettingFunction); + } + + writer.decreaseIndent(); + } + + @Override + public void printOwnersForDomains(@NonNull IndentingPrintWriter writer, + @NonNull List<String> domains, @Nullable @UserIdInt Integer userId) { + mConnection.withPackageSettings(pkgSettings -> { + synchronized (mLock) { + int size = domains.size(); + for (int index = 0; index < size; index++) { + printOwnersForDomain(writer, domains.get(index), userId, pkgSettings); + } + } + }); + } + + private void printOwnersForDomain(@NonNull IndentingPrintWriter writer, @NonNull String domain, + @Nullable @UserIdInt Integer userId, + @NonNull Function<String, PackageSetting> pkgSettingFunction) { + SparseArray<SparseArray<List<String>>> userIdToApprovalLevelToOwners = + new SparseArray<>(); + + if (userId == null || userId == UserHandle.USER_ALL) { + for (int aUserId : mConnection.getAllUserIds()) { + userIdToApprovalLevelToOwners.put(aUserId, + getOwnersForDomainInternal(domain, true, aUserId, pkgSettingFunction)); + } + } else { + userIdToApprovalLevelToOwners.put(userId, + getOwnersForDomainInternal(domain, true, userId, pkgSettingFunction)); + } + + mDebug.printOwners(writer, domain, userIdToApprovalLevelToOwners); + } + @NonNull @Override public DomainVerificationShell getShell() { @@ -1427,7 +1531,7 @@ public class DomainVerificationService extends SystemService // Find all approval levels int highestApproval = fillMapWithApprovalLevels(infoApprovals, domain, userId, pkgSettingFunction); - if (highestApproval == APPROVAL_LEVEL_NONE) { + if (highestApproval <= APPROVAL_LEVEL_NONE) { return Pair.create(emptyList(), highestApproval); } @@ -1484,7 +1588,7 @@ public class DomainVerificationService extends SystemService fillInfoMapForSamePackage(inputMap, packageName, APPROVAL_LEVEL_NONE); continue; } - int approval = approvalLevelForDomain(pkgSetting, domain, userId, domain); + int approval = approvalLevelForDomain(pkgSetting, domain, false, userId, domain); highestApproval = Math.max(highestApproval, approval); fillInfoMapForSamePackage(inputMap, packageName, approval); } @@ -1600,18 +1704,53 @@ public class DomainVerificationService extends SystemService return APPROVAL_LEVEL_NONE; } - return approvalLevelForDomain(pkgSetting, intent.getData().getHost(), userId, intent); + return approvalLevelForDomain(pkgSetting, intent.getData().getHost(), false, userId, + intent); } /** - * @param debugObject Should be an {@link Intent} if checking for resolution or a {@link String} - * otherwise. + * @param includeNegative Whether to include negative values, which requires an expensive + * domain comparison operation. + * @param debugObject Should be an {@link Intent} if checking for resolution or a + * {@link String} otherwise. */ private int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull String host, - @UserIdInt int userId, @NonNull Object debugObject) { + boolean includeNegative, @UserIdInt int userId, @NonNull Object debugObject) { + int approvalLevel = approvalLevelForDomainInternal(pkgSetting, host, includeNegative, + userId, debugObject); + if (includeNegative && approvalLevel == APPROVAL_LEVEL_NONE) { + PackageUserState pkgUserState = pkgSetting.readUserState(userId); + if (!pkgUserState.installed) { + return APPROVAL_LEVEL_NOT_INSTALLED; + } + + AndroidPackage pkg = pkgSetting.getPkg(); + if (pkg != null) { + if (!pkgUserState.isPackageEnabled(pkg)) { + return APPROVAL_LEVEL_DISABLED; + } else if (mCollector.containsAutoVerifyDomain(pkgSetting.getPkg(), host)) { + return APPROVAL_LEVEL_UNVERIFIED; + } + } + } + + return approvalLevel; + } + + private int approvalLevelForDomainInternal(@NonNull PackageSetting pkgSetting, + @NonNull String host, boolean includeNegative, @UserIdInt int userId, + @NonNull Object debugObject) { String packageName = pkgSetting.getName(); final AndroidPackage pkg = pkgSetting.getPkg(); + if (pkg != null && includeNegative && !mCollector.containsWebDomain(pkg, host)) { + if (DEBUG_APPROVAL) { + debugApproval(packageName, debugObject, userId, false, + "domain not declared"); + } + return APPROVAL_LEVEL_UNDECLARED; + } + final PackageUserState pkgUserState = pkgSetting.readUserState(userId); if (pkgUserState == null) { if (DEBUG_APPROVAL) { @@ -1749,6 +1888,7 @@ public class DomainVerificationService extends SystemService private Pair<List<String>, Integer> getApprovedPackagesLocked(@NonNull String domain, @UserIdInt int userId, int minimumApproval, @NonNull Function<String, PackageSetting> pkgSettingFunction) { + boolean includeNegative = minimumApproval < APPROVAL_LEVEL_NONE; int highestApproval = minimumApproval; List<String> approvedPackages = emptyList(); @@ -1762,7 +1902,8 @@ public class DomainVerificationService extends SystemService continue; } - int level = approvalLevelForDomain(pkgSetting, domain, userId, domain); + int level = approvalLevelForDomain(pkgSetting, domain, includeNegative, userId, + domain); if (level < minimumApproval) { continue; } diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java index ea71b28c6acd..da2d162d46f9 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java @@ -110,6 +110,13 @@ public class DomainVerificationShell { pw.println(" packages will be reset if no one package is specified."); pw.println(" <ALLOWED>: true to allow the package to open auto verified links, false"); pw.println(" to disable"); + pw.println(" get-app-link-owners [--user <USER_ID>] [--package <PACKAGE>] [<DOMAINS>]"); + pw.println(" Print the owners for a specific domain for a given user in low to high"); + pw.println(" priority order."); + pw.println(" --user <USER_ID>: the user to query for"); + pw.println(" --package <PACKAGE>: optionally also print for all web domains declared"); + pw.println(" by a package, or \"all\" to print all packages"); + pw.println(" --<DOMAINS>: space separated list of domains to query for"); } /** @@ -132,6 +139,8 @@ public class DomainVerificationShell { return runSetAppLinksUserState(commandHandler); case "set-app-links-allowed": return runSetAppLinksAllowed(commandHandler); + case "get-app-link-owners": + return runGetAppLinkOwners(commandHandler); } return null; @@ -420,6 +429,67 @@ public class DomainVerificationShell { return true; } + // pm get-app-link-owners [--user <USER_ID>] [--package <PACKAGE>] [<DOMAINS>] + private boolean runGetAppLinkOwners(@NonNull BasicShellCommandHandler commandHandler) { + String packageName = null; + Integer userId = null; + String option; + while ((option = commandHandler.getNextOption()) != null) { + switch (option) { + case "--user": + userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired()); + break; + case "--package": + packageName = commandHandler.getNextArgRequired(); + if (TextUtils.isEmpty(packageName)) { + commandHandler.getErrPrintWriter().println("Error: no package specified"); + return false; + } + break; + default: + commandHandler.getErrPrintWriter().println( + "Error: unexpected option: " + option); + return false; + } + } + + ArrayList<String> domains = getRemainingArgs(commandHandler); + if (domains.isEmpty() && TextUtils.isEmpty(packageName)) { + commandHandler.getErrPrintWriter() + .println("Error: no package name or domain specified"); + return false; + } + + if (userId != null) { + userId = translateUserId(userId, "runSetAppLinksAllowed"); + } + + try (IndentingPrintWriter writer = new IndentingPrintWriter( + commandHandler.getOutPrintWriter(), /* singleIndent */ " ", /* wrapLength */ + 120)) { + writer.increaseIndent(); + if (packageName != null) { + if (packageName.equals("all")) { + packageName = null; + } + + try { + mCallback.printOwnersForPackage(writer, packageName, userId); + } catch (NameNotFoundException e) { + commandHandler.getErrPrintWriter() + .println("Error: package not found: " + packageName); + return false; + } + } + if (!domains.isEmpty()) { + mCallback.printOwnersForDomains(writer, domains, userId); + } + writer.decreaseIndent(); + return true; + } + } + + @NonNull private ArrayList<String> getRemainingArgs(@NonNull BasicShellCommandHandler commandHandler) { ArrayList<String> args = new ArrayList<>(); String arg; @@ -534,5 +604,18 @@ public class DomainVerificationShell { */ void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName, @Nullable @UserIdInt Integer userId) throws NameNotFoundException; + + /** + * Print the owners for all domains in a given package. + */ + void printOwnersForPackage(@NonNull IndentingPrintWriter writer, + @Nullable String packageName, @Nullable @UserIdInt Integer userId) + throws NameNotFoundException; + + /** + * Print the owners for the given domains. + */ + void printOwnersForDomains(@NonNull IndentingPrintWriter writer, + @NonNull List<String> domains, @Nullable @UserIdInt Integer userId); } } diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index 81a51e290664..24337f3ad346 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java @@ -554,11 +554,15 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo case ROR_NEED_PREPARATION: final long origId = Binder.clearCallingIdentity(); try { - mInjector.getLockSettingsService().prepareRebootEscrow(); + boolean result = mInjector.getLockSettingsService().prepareRebootEscrow(); + // Clear the RoR preparation state if lock settings reports an failure. + if (!result) { + clearRoRPreparationState(); + } + return result; } finally { Binder.restoreCallingIdentity(origId); } - return true; default: throw new IllegalStateException("Unsupported action type on new request " + action); } @@ -670,11 +674,10 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo case ROR_REQUESTED_NEED_CLEAR: final long origId = Binder.clearCallingIdentity(); try { - mInjector.getLockSettingsService().clearRebootEscrow(); + return mInjector.getLockSettingsService().clearRebootEscrow(); } finally { Binder.restoreCallingIdentity(origId); } - return true; default: throw new IllegalStateException("Unsupported action type on clear " + action); } @@ -820,6 +823,11 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo lskfCapturedCount); } + private synchronized void clearRoRPreparationState() { + mCallerPendingRequest.clear(); + mCallerPreparedForReboot.clear(); + } + private void clearRoRPreparationStateOnRebootFailure(RebootPreparationError escrowError) { if (!FATAL_ARM_ESCROW_ERRORS.contains(escrowError.mProviderErrorCode)) { return; @@ -827,10 +835,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo Slog.w(TAG, "Clearing resume on reboot states for all clients on arm escrow error: " + escrowError.mProviderErrorCode); - synchronized (this) { - mCallerPendingRequest.clear(); - mCallerPreparedForReboot.clear(); - } + clearRoRPreparationState(); } private @ResumeOnRebootRebootErrorCode int rebootWithLskfImpl(String packageName, String reason, diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java index 52236a81c607..8b2b8b1cfbac 100644 --- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java +++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java @@ -284,6 +284,9 @@ public class TestHarnessModeService extends SystemService { private class TestHarnessModeShellCommand extends ShellCommand { @Override public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } switch (cmd) { case "enable": case "restore": diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 9acbdcc177cc..c888e545b24e 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2384,17 +2384,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub public void notifyWakingUp(int x, int y, @NonNull Bundle extras) { synchronized (mLock) { final WallpaperData data = mWallpaperMap.get(mCurrentUserId); - data.connection.forEachDisplayConnector( - displayConnector -> { - if (displayConnector.mEngine != null) { - try { - displayConnector.mEngine.dispatchWallpaperCommand( - WallpaperManager.COMMAND_WAKING_UP, x, y, -1, extras); - } catch (RemoteException e) { - e.printStackTrace(); + if (data != null && data.connection != null) { + data.connection.forEachDisplayConnector( + displayConnector -> { + if (displayConnector.mEngine != null) { + try { + displayConnector.mEngine.dispatchWallpaperCommand( + WallpaperManager.COMMAND_WAKING_UP, x, y, -1, extras); + } catch (RemoteException e) { + e.printStackTrace(); + } } - } - }); + }); + } } } @@ -2404,17 +2406,20 @@ public class WallpaperManagerService extends IWallpaperManager.Stub public void notifyGoingToSleep(int x, int y, @NonNull Bundle extras) { synchronized (mLock) { final WallpaperData data = mWallpaperMap.get(mCurrentUserId); - data.connection.forEachDisplayConnector( - displayConnector -> { - if (displayConnector.mEngine != null) { - try { - displayConnector.mEngine.dispatchWallpaperCommand( - WallpaperManager.COMMAND_GOING_TO_SLEEP, x, y, -1, extras); - } catch (RemoteException e) { - e.printStackTrace(); + if (data != null && data.connection != null) { + data.connection.forEachDisplayConnector( + displayConnector -> { + if (displayConnector.mEngine != null) { + try { + displayConnector.mEngine.dispatchWallpaperCommand( + WallpaperManager.COMMAND_GOING_TO_SLEEP, x, y, -1, + extras); + } catch (RemoteException e) { + e.printStackTrace(); + } } - } - }); + }); + } } } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index e6d49e406803..a97c0804edce 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CO 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 android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY; import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER; @@ -47,6 +48,7 @@ import android.annotation.NonNull; import android.app.Application; import android.content.Context; import android.content.pm.PackageManagerInternal; +import android.graphics.BLASTBufferQueue; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; @@ -938,8 +940,7 @@ final class AccessibilityController { for (int i = visibleWindowCount - 1; i >= 0; i--) { WindowState windowState = visibleWindows.valueAt(i); final int windowType = windowState.mAttrs.type; - if ((windowType == TYPE_MAGNIFICATION_OVERLAY - || windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) + if (isExcludedWindowType(windowType) || ((windowState.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) { continue; @@ -1040,6 +1041,17 @@ final class AccessibilityController { } } + private boolean isExcludedWindowType(int windowType) { + return windowType == TYPE_MAGNIFICATION_OVERLAY + // Omit the touch region to avoid the cut out of the magnification + // bounds because nav bar panel is unmagnifiable. + || windowType == TYPE_NAVIGATION_BAR_PANEL + // Omit the touch region of window magnification to avoid the cut out of the + // magnification and the magnified center of window magnification could be + // in the bounds + || windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; + } + 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 @@ -1112,7 +1124,8 @@ final class AccessibilityController { private final Paint mPaint = new Paint(); private final SurfaceControl mSurfaceControl; - private final Surface mSurface = mService.mSurfaceFactory.get(); + private final BLASTBufferQueue mBlastBufferQueue; + private final Surface mSurface; private final AnimationController mAnimationController; @@ -1124,11 +1137,10 @@ final class AccessibilityController { ViewportWindow(Context context) { SurfaceControl surfaceControl = null; try { - mDisplay.getRealSize(mTempPoint); surfaceControl = mDisplayContent .makeOverlay() .setName(SURFACE_TITLE) - .setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo + .setBLASTLayer() .setFormat(PixelFormat.TRANSLUCENT) .setCallsite("ViewportWindow") .build(); @@ -1136,6 +1148,9 @@ final class AccessibilityController { /* ignore */ } mSurfaceControl = surfaceControl; + mDisplay.getRealSize(mTempPoint); + mBlastBufferQueue = new BLASTBufferQueue(SURFACE_TITLE, mSurfaceControl, + mTempPoint.x, mTempPoint.y, PixelFormat.RGBA_8888); final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); final int layer = @@ -1145,8 +1160,7 @@ final class AccessibilityController { InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t, mDisplayContent.getDisplayId(), "Magnification Overlay"); t.apply(); - - mSurface.copyFrom(mSurfaceControl); + mSurface = mBlastBufferQueue.createSurface(); mAnimationController = new AnimationController(context, mService.mH.getLooper()); @@ -1271,6 +1285,9 @@ final class AccessibilityController { } void releaseSurface() { + if (mBlastBufferQueue != null) { + mBlastBufferQueue.destroy(); + } mService.mTransactionFactory.get().remove(mSurfaceControl).apply(); mSurface.release(); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 4c392f0906aa..3e8bc5d31af0 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -972,6 +972,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (lastLaunchTime == 0) pw.print("0"); else TimeUtils.formatDuration(lastLaunchTime, now, pw); pw.println(); + if (mLaunchCookie != null) { + pw.print(prefix); + pw.print("launchCookie="); + pw.println(mLaunchCookie); + } pw.print(prefix); pw.print("mHaveState="); pw.print(mHaveState); pw.print(" mIcicle="); pw.println(mIcicle); pw.print(prefix); pw.print("state="); pw.print(mState); @@ -3518,7 +3523,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // Reset the last saved PiP snap fraction on removal. - mDisplayContent.mPinnedTaskControllerLocked.onActivityHidden(mActivityComponent); + mDisplayContent.mPinnedTaskController.onActivityHidden(mActivityComponent); mWmService.mEmbeddedWindowController.onActivityRemoved(this); mRemovingFromDisplay = false; } @@ -4877,7 +4882,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppStopped: %s", this); mAppStopped = true; // Reset the last saved PiP snap fraction on app stop. - mDisplayContent.mPinnedTaskControllerLocked.onActivityHidden(mActivityComponent); + mDisplayContent.mPinnedTaskController.onActivityHidden(mActivityComponent); destroySurfaces(); // Remove any starting window that was added for this app if they are still around. removeStartingWindow(); @@ -6400,8 +6405,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } clearThumbnail(); final Transaction transaction = getAnimatingContainer().getPendingTransaction(); - mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory, - transaction, getAnimatingContainer(), thumbnailHeader); + mThumbnail = new WindowContainerThumbnail(transaction, getAnimatingContainer(), + thumbnailHeader); mThumbnail.startAnimation(transaction, loadThumbnailAnimation(thumbnailHeader)); } @@ -6430,8 +6435,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } final Transaction transaction = getPendingTransaction(); - mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory, - transaction, getTask(), thumbnail); + mThumbnail = new WindowContainerThumbnail(transaction, getTask(), thumbnail); final Animation animation = getDisplayContent().mAppTransition.createCrossProfileAppsThumbnailAnimationLocked( frame); @@ -6600,6 +6604,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } + mDisplayContent.mPinnedTaskController.onCancelFixedRotationTransform(task); // Perform rotation animation according to the rotation of this activity. startFreezingScreen(originalDisplayRotation); // This activity may relaunch or perform configuration change so once it has reported drawn, @@ -6991,6 +6996,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // fixed-orientation requests. return; } + if (newParentConfig.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED) { + // PiP bounds have higher priority than the requested orientation. Otherwise the + // activity may be squeezed into a small piece. + return; + } final Rect resolvedBounds = getResolvedOverrideConfiguration().windowConfiguration.getBounds(); @@ -7382,8 +7392,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + final boolean wasInPictureInPicture = inPinnedWindowingMode(); final DisplayContent display = mDisplayContent; - if (inPinnedWindowingMode() && attachedToProcess() && display != null) { + if (wasInPictureInPicture && attachedToProcess() && display != null) { // If the PIP activity is changing to fullscreen with display orientation change, the // fixed rotation will take effect that requires to send fixed rotation adjustments // before the process configuration (if the process is a configuration listener of the @@ -7415,6 +7426,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A onMergedOverrideConfigurationChanged(); } + // Before PiP animation is done, th windowing mode of the activity is still the previous + // mode (see RootWindowContainer#moveActivityToPinnedRootTask). So once the windowing mode + // of activity is changed, it is the signal of the last step to update the PiP states. + if (!wasInPictureInPicture && inPinnedWindowingMode() && task != null) { + mTaskSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, task.getBounds()); + } + if (display == null) { return; } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 1158a9c70158..08a9f0928b8b 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1275,7 +1275,11 @@ class ActivityStarter { || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP; final boolean isCallingUidPersistentSystemProcess = callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; - if ((appSwitchAllowed && callingUidHasAnyVisibleWindow) + + // Normal apps with visible app window will be allowed to start activity if app switching + // is allowed, or apps like live wallpaper with non app visible window will be allowed. + if (((appSwitchAllowed || mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) + && callingUidHasAnyVisibleWindow) || isCallingUidPersistentSystemProcess) { if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "Activity start allowed: callingUidHasAnyVisibleWindow = " + callingUid @@ -2639,6 +2643,11 @@ class ActivityStarter { intentTask.setWindowingMode(mPreferredWindowingMode); } + // Update the target's launch cookie to those specified in the options if set + if (mStartActivity.mLaunchCookie != null) { + intentActivity.mLaunchCookie = mStartActivity.mLaunchCookie; + } + // Need to update mTargetRootTask because if task was moved out of it, the original root // task may be destroyed. mTargetRootTask = intentActivity.getRootTask(); @@ -2752,8 +2761,8 @@ class ActivityStarter { final boolean onTop = (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind; - return mRootWindowContainer.getLaunchRootTask(r, aOptions, task, onTop, mLaunchParams, - mRequest.realCallingPid, mRequest.realCallingUid); + return mRootWindowContainer.getLaunchRootTask(r, aOptions, task, mSourceRootTask, onTop, + mLaunchParams, launchFlags, mRequest.realCallingPid, mRequest.realCallingUid); } private boolean isLaunchModeOneOf(int mode1, int mode2) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 8de4ee62561d..0570f6cdfa77 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -5620,10 +5620,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mTaskSupervisor.endDeferResume(); } - if (wpc.isInstrumenting()) { - finishInstrumentationCallback.run(); - } - if (!restarting && hasVisibleActivities) { deferWindowLayout(); try { @@ -5640,6 +5636,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } } + if (wpc.isInstrumenting()) { + finishInstrumentationCallback.run(); + } } @Override @@ -6139,6 +6138,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public boolean handleAppCrashInActivityController(String processName, int pid, String shortMsg, String longMsg, long timeMillis, String stackTrace, Runnable killCrashingAppCallback) { + Runnable targetRunnable = null; synchronized (mGlobalLock) { if (mController == null) { return false; @@ -6147,15 +6147,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { if (!mController.appCrashed(processName, pid, shortMsg, longMsg, timeMillis, stackTrace)) { - killCrashingAppCallback.run(); - return true; + targetRunnable = killCrashingAppCallback; } } catch (RemoteException e) { mController = null; Watchdog.getInstance().setActivityController(null); } - return false; } + if (targetRunnable != null) { + targetRunnable.run(); + return true; + } + return false; } @Override diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 9d225e1ab1f6..df1fec9ad0da 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2254,7 +2254,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { scheduleUpdatePictureInPictureModeIfNeeded(task, rootTask.getRequestedOverrideBounds()); } - private void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Rect targetRootTaskBounds) { + void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Rect targetRootTaskBounds) { final PooledConsumer c = PooledLambda.obtainConsumer( ActivityTaskSupervisor::addToPipModeChangedList, this, PooledLambda.__(ActivityRecord.class)); @@ -2278,16 +2278,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mMultiWindowModeChangedActivities.remove(r); } - void updatePictureInPictureMode(Task task, Rect targetRootTaskBounds, boolean forceUpdate) { - mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG); - final PooledConsumer c = PooledLambda.obtainConsumer( - ActivityRecord::updatePictureInPictureMode, - PooledLambda.__(ActivityRecord.class), targetRootTaskBounds, forceUpdate); - task.getRootTask().setBounds(targetRootTaskBounds); - task.forAllActivities(c); - c.recycle(); - } - void wakeUp(String reason) { mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION, "android.server.am:TURN_ON:" + reason); diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 394ae7e94b72..43326df1a143 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -986,24 +986,25 @@ public class AppTransition implements Dump { Animation a; if (isKeyguardGoingAwayTransitOld(transit) && enter) { - a = mTransitionAnimation.loadKeyguardExitAnimation(transit, mNextAppTransitionFlags); + a = mTransitionAnimation.loadKeyguardExitAnimation(mNextAppTransitionFlags, + transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER); } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) { a = null; } else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE && !enter) { - a = mTransitionAnimation.loadKeyguardUnoccludeAnimation(lp); + a = mTransitionAnimation.loadKeyguardUnoccludeAnimation(); } else if (transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) { a = null; } else if (isVoiceInteraction && (transit == TRANSIT_OLD_ACTIVITY_OPEN || transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_TO_FRONT)) { - a = mTransitionAnimation.loadVoiceActivityOpenAnimation(lp, enter); + a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", a, appTransitionOldToString(transit), enter, Debug.getCallers(3)); } else if (isVoiceInteraction && (transit == TRANSIT_OLD_ACTIVITY_CLOSE || transit == TRANSIT_OLD_TASK_CLOSE || transit == TRANSIT_OLD_TASK_TO_BACK)) { - a = mTransitionAnimation.loadVoiceActivityExitAnimation(lp, enter); + a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", a, appTransitionOldToString(transit), enter, Debug.getCallers(3)); diff --git a/services/core/java/com/android/server/wm/BlurController.java b/services/core/java/com/android/server/wm/BlurController.java index d920267bb44d..ff1016855287 100644 --- a/services/core/java/com/android/server/wm/BlurController.java +++ b/services/core/java/com/android/server/wm/BlurController.java @@ -22,37 +22,32 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.database.ContentObserver; import android.net.ConnectivityManager; import android.os.PowerManager; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.provider.Settings; import android.view.ICrossWindowBlurEnabledListener; -import com.android.internal.annotations.GuardedBy; - /** * Keeps track of the different factors that determine whether cross-window blur is enabled * or disabled. Also keeps a list of all interested listeners and notifies them when the * blur enabled state changes. */ final class BlurController { - private final PowerManager mPowerManager; + private final Context mContext; private final RemoteCallbackList<ICrossWindowBlurEnabledListener> mBlurEnabledListeners = new RemoteCallbackList<>(); // We don't use the WM global lock, because the BlurController is not involved in window // drawing and only receives binder calls that don't need synchronization with the rest of WM private final Object mLock = new Object(); - @GuardedBy("mLock") - boolean mBlurEnabled; - @GuardedBy("mLock") - boolean mBlurForceDisabled; - @GuardedBy("mLock") - boolean mInBatterySaverMode; + private volatile boolean mBlurEnabled; + private boolean mInPowerSaveMode; + private boolean mBlurDisabledSetting; BlurController(Context context, PowerManager powerManager) { - mPowerManager = powerManager; - mInBatterySaverMode = mPowerManager.isPowerSaveMode(); - updateBlurEnabledLocked(); + mContext = context; IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); @@ -60,18 +55,36 @@ final class BlurController { @Override public void onReceive(Context context, Intent intent) { if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) { - setBatterySaverEnabled(mPowerManager.isPowerSaveMode()); + // onReceive always gets called on the same thread, so there is no + // multi-threaded execution here. Thus, we don't have to hold mLock here. + mInPowerSaveMode = powerManager.isPowerSaveMode(); + updateBlurEnabled(); } } }, filter, null, null); + mInPowerSaveMode = powerManager.isPowerSaveMode(); + + context.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.DISABLE_WINDOW_BLURS), false, + new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + // onChange always gets called on the same thread, so there is no + // multi-threaded execution here. Thus, we don't have to hold mLock here. + mBlurDisabledSetting = getBlurDisabledSetting(); + updateBlurEnabled(); + } + }); + mBlurDisabledSetting = getBlurDisabledSetting(); + + updateBlurEnabled(); } boolean registerCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener) { if (listener == null) return false; mBlurEnabledListeners.register(listener); - synchronized (mLock) { - return mBlurEnabled; - } + return getBlurEnabled(); } void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener) { @@ -79,29 +92,20 @@ final class BlurController { mBlurEnabledListeners.unregister(listener); } - void setForceCrossWindowBlurDisabled(boolean disable) { - synchronized (mLock) { - mBlurForceDisabled = disable; - updateBlurEnabledLocked(); - } - + boolean getBlurEnabled() { + return mBlurEnabled; } - void setBatterySaverEnabled(boolean enabled) { + private void updateBlurEnabled() { synchronized (mLock) { - mInBatterySaverMode = enabled; - updateBlurEnabledLocked(); - } - } - - private void updateBlurEnabledLocked() { - final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurForceDisabled - && !mInBatterySaverMode; - if (mBlurEnabled == newEnabled) { - return; + final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurDisabledSetting + && !mInPowerSaveMode; + if (mBlurEnabled == newEnabled) { + return; + } + mBlurEnabled = newEnabled; + notifyBlurEnabledChangedLocked(newEnabled); } - mBlurEnabled = newEnabled; - notifyBlurEnabledChangedLocked(newEnabled); } private void notifyBlurEnabledChangedLocked(boolean enabled) { @@ -117,4 +121,9 @@ final class BlurController { } mBlurEnabledListeners.finishBroadcast(); } + + private boolean getBlurDisabledSetting() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.DISABLE_WINDOW_BLURS, 0) == 1; + } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c44f4e3cbd71..0c4e1a2c0aae 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -154,6 +154,8 @@ import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.ColorSpace; +import android.graphics.GraphicBuffer; import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; @@ -464,7 +466,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp private boolean mDeferredRemoval; final DockedTaskDividerController mDividerControllerLocked; - final PinnedTaskController mPinnedTaskControllerLocked; + final PinnedTaskController mPinnedTaskController; final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>(); /** A collection of windows that provide tap exclude regions inside of them. */ @@ -1017,7 +1019,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius(); mDividerControllerLocked = new DockedTaskDividerController(this); - mPinnedTaskControllerLocked = new PinnedTaskController(mWmService, this); + mPinnedTaskController = new PinnedTaskController(mWmService, this); final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession) .setOpaque(true) @@ -1578,10 +1580,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // has it own policy for bounds, the activity bounds based on parent is unknown. return false; } - if (mPinnedTaskControllerLocked.isPipActiveOrWindowingModeChanging()) { - // Use normal rotation animation because seamless PiP rotation is not supported yet. - return false; - } setFixedRotationLaunchingApp(r, rotation); return true; @@ -1667,6 +1665,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (mFixedRotationLaunchingApp == null) { return; } + if (mPinnedTaskController.shouldDeferOrientationChange()) { + // Wait for the PiP animation to finish. + return; + } // Update directly because the app which will change the orientation of display is ready. if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) { sendNewConfiguration(); @@ -1837,6 +1839,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp forAllWindows(w -> { w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly); }, true /* traverseTopToBottom */); + mPinnedTaskController.startSeamlessRotationIfNeeded(transaction); } mWmService.mDisplayManagerInternal.performTraversal(transaction); @@ -2333,7 +2336,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } PinnedTaskController getPinnedTaskController() { - return mPinnedTaskControllerLocked; + return mPinnedTaskController; } /** @@ -2392,12 +2395,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Override public void onConfigurationChanged(Configuration newParentConfig) { - // update resources before cascade so that root docked/pinned tasks use the correct info - preOnConfigurationChanged(); final int lastOrientation = getConfiguration().orientation; super.onConfigurationChanged(newParentConfig); if (mDisplayPolicy != null) { mDisplayPolicy.onConfigurationChanged(); + mPinnedTaskController.onPostDisplayConfigurationChanged(); } if (lastOrientation != getConfiguration().orientation) { @@ -2408,19 +2410,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } - /** - * Updates the resources used by docked/pinned controllers. This needs to be called at the - * beginning of a configuration update cascade since the metrics from these resources are used - * for bounds calculations. - */ - void preOnConfigurationChanged() { - final PinnedTaskController pinnedTaskController = getPinnedTaskController(); - - if (pinnedTaskController != null) { - getPinnedTaskController().onConfigurationChanged(); - } - } - @Override boolean fillsParent() { return true; @@ -2962,7 +2951,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final boolean imeVisible = imeWin != null && imeWin.isVisible() && imeWin.isDisplayed(); final int imeHeight = getInputMethodWindowVisibleHeight(); - mPinnedTaskControllerLocked.setAdjustedForIme(imeVisible, imeHeight); + mPinnedTaskController.setAdjustedForIme(imeVisible, imeHeight); } int getInputMethodWindowVisibleHeight() { @@ -3190,7 +3179,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } pw.println(); - mPinnedTaskControllerLocked.dump(prefix, pw); + mPinnedTaskController.dump(prefix, pw); pw.println(); mDisplayFrames.dump(prefix, pw); @@ -3821,7 +3810,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final ActivityRecord activity = mImeLayeringTarget.mActivityRecord; final SurfaceControl imeSurface = mWmService.mSurfaceControlFactory.apply(null) .setName("IME-snapshot-surface") - .setBufferSize(buffer.getWidth(), buffer.getHeight()) + .setBLASTLayer() .setFormat(buffer.getFormat()) .setParent(activity.getSurfaceControl()) .setCallsite("DisplayContent.attachAndShowImeScreenshotOnTarget") @@ -3829,10 +3818,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Make IME snapshot as trusted overlay InputMonitor.setTrustedOverlayInputInfo(imeSurface, t, getDisplayId(), "IME-snapshot-surface"); - Surface surface = mWmService.mSurfaceFactory.get(); - surface.copyFrom(imeSurface); - surface.attachAndQueueBufferWithColorSpace(buffer, null); - surface.release(); + GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(buffer); + t.setBuffer(imeSurface, graphicBuffer); + t.setColorSpace(mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB)); t.setRelativeLayer(imeSurface, activity.getSurfaceControl(), 1); t.setPosition(imeSurface, mInputMethodWindow.getDisplayFrame().left, mInputMethodWindow.getDisplayFrame().top); diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index e1fc75e6fd9f..0e73d7956748 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -254,7 +254,7 @@ public class DisplayRotation { if (isDefaultDisplay) { final Handler uiHandler = UiThread.getHandler(); - mOrientationListener = new OrientationListener(mContext, uiHandler, mService); + mOrientationListener = new OrientationListener(mContext, uiHandler); mOrientationListener.setCurrentRotation(mRotation); mSettingsObserver = new SettingsObserver(uiHandler); mSettingsObserver.observe(); @@ -1514,8 +1514,8 @@ public class DisplayRotation { final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5); boolean mEnabled; - OrientationListener(Context context, Handler handler, WindowManagerService service) { - super(context, handler, service); + OrientationListener(Context context, Handler handler) { + super(context, handler); } private class UpdateRunnable implements Runnable { diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java index 724747daaa5b..02a7db19f405 100644 --- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java +++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java @@ -20,38 +20,39 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.content.Context; +import android.graphics.BLASTBufferQueue; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PorterDuff; -import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.view.Display; import android.view.Surface; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; -import java.util.function.Supplier; - class EmulatorDisplayOverlay { private static final String TAG = TAG_WITH_CLASS_NAME ? "EmulatorDisplayOverlay" : TAG_WM; + private static final String TITLE = "EmulatorDisplayOverlay"; + // Display dimensions private Point mScreenSize; private final SurfaceControl mSurfaceControl; private final Surface mSurface; + private final BLASTBufferQueue mBlastBufferQueue; + private int mLastDW; private int mLastDH; private boolean mDrawNeeded; - private Drawable mOverlay; + private final Drawable mOverlay; private int mRotation; private boolean mVisible; - EmulatorDisplayOverlay(Supplier<Surface> surfaceFactory, Context context, DisplayContent dc, - int zOrder, SurfaceControl.Transaction t) { - mSurface = surfaceFactory.get(); + EmulatorDisplayOverlay(Context context, DisplayContent dc, int zOrder, + SurfaceControl.Transaction t) { final Display display = dc.getDisplay(); mScreenSize = new Point(); display.getSize(mScreenSize); @@ -59,24 +60,26 @@ class EmulatorDisplayOverlay { SurfaceControl ctrl = null; try { ctrl = dc.makeOverlay() - .setName("EmulatorDisplayOverlay") - .setBufferSize(mScreenSize.x, mScreenSize.y) + .setName(TITLE) + .setBLASTLayer() .setFormat(PixelFormat.TRANSLUCENT) - .setCallsite("EmulatorDisplayOverlay") + .setCallsite(TITLE) .build(); t.setLayer(ctrl, zOrder); t.setPosition(ctrl, 0, 0); t.show(ctrl); // Ensure we aren't considered as obscuring for Input purposes. - InputMonitor.setTrustedOverlayInputInfo(ctrl, t, - dc.getDisplayId(), "EmulatorDisplayOverlay"); - mSurface.copyFrom(ctrl); + InputMonitor.setTrustedOverlayInputInfo(ctrl, t, dc.getDisplayId(), TITLE); } catch (OutOfResourcesException e) { } mSurfaceControl = ctrl; mDrawNeeded = true; mOverlay = context.getDrawable( com.android.internal.R.drawable.emulator_circular_window_overlay); + + mBlastBufferQueue = new BLASTBufferQueue(TITLE, mSurfaceControl, mScreenSize.x, + mScreenSize.y, PixelFormat.RGBA_8888); + mSurface = mBlastBufferQueue.createSurface(); } private void drawIfNeeded(SurfaceControl.Transaction t) { @@ -85,12 +88,10 @@ class EmulatorDisplayOverlay { } mDrawNeeded = false; - Rect dirty = new Rect(0, 0, mScreenSize.x, mScreenSize.y); Canvas c = null; try { - c = mSurface.lockCanvas(dirty); - } catch (IllegalArgumentException e) { - } catch (OutOfResourcesException e) { + c = mSurface.lockCanvas(null); + } catch (IllegalArgumentException | OutOfResourcesException e) { } if (c == null) { return; diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 8ff9fbaa3995..df4f2a91a8a5 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -50,6 +50,7 @@ import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; @@ -191,6 +192,7 @@ class KeyguardController { // state when evaluating visibilities. updateKeyguardSleepToken(); mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); + InputMethodManagerInternal.get().updateImeWindowStatus(); } /** diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java index 15e078b478b8..dea83f0c00e5 100644 --- a/services/core/java/com/android/server/wm/PinnedTaskController.java +++ b/services/core/java/com/android/server/wm/PinnedTaskController.java @@ -16,20 +16,26 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; + import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.app.PictureInPictureParams; import android.app.RemoteAction; import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.content.res.Resources; +import android.graphics.Matrix; +import android.graphics.Rect; import android.os.IBinder; import android.os.RemoteException; -import android.util.DisplayMetrics; import android.util.Log; import android.util.Slog; -import android.view.DisplayInfo; import android.view.IPinnedTaskListener; +import android.view.SurfaceControl; +import android.window.PictureInPictureSurfaceTransaction; import java.io.PrintWriter; import java.util.ArrayList; @@ -54,6 +60,7 @@ import java.util.List; class PinnedTaskController { private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedTaskController" : TAG_WM; + private static final int DEFER_ORIENTATION_CHANGE_TIMEOUT_MS = 1000; private final WindowManagerService mService; private final DisplayContent mDisplayContent; @@ -62,8 +69,21 @@ class PinnedTaskController { private final PinnedTaskListenerDeathHandler mPinnedTaskListenerDeathHandler = new PinnedTaskListenerDeathHandler(); - /** Whether the PiP is entering or leaving. */ - private boolean mIsPipWindowingModeChanging; + /** + * Non-null if the entering PiP task will cause display rotation to change. The bounds are + * based on the new rotation. + */ + private Rect mDestRotatedBounds; + /** + * Non-null if the entering PiP task from recents animation will cause display rotation to + * change. The transaction is based on the old rotation. + */ + private PictureInPictureSurfaceTransaction mPipTransaction; + /** Whether to skip task configuration change once. */ + private boolean mFreezingTaskConfig; + /** Defer display orientation change if the PiP task is animating across orientations. */ + private boolean mDeferOrientationChanging; + private final Runnable mDeferOrientationTimeoutRunnable; private boolean mIsImeShowing; private int mImeHeight; @@ -72,16 +92,10 @@ class PinnedTaskController { private ArrayList<RemoteAction> mActions = new ArrayList<>(); private float mAspectRatio = -1f; - // Used to calculate task bounds across rotations - private final DisplayInfo mDisplayInfo = new DisplayInfo(); - // The aspect ratio bounds of the PIP. private float mMinAspectRatio; private float mMaxAspectRatio; - // Temp vars for calculation - private final DisplayMetrics mTmpMetrics = new DisplayMetrics(); - /** * Handler for the case where the listener dies. */ @@ -89,23 +103,32 @@ class PinnedTaskController { @Override public void binderDied() { - // Clean up the state if the listener dies - if (mPinnedTaskListener != null) { - mPinnedTaskListener.asBinder().unlinkToDeath(mPinnedTaskListenerDeathHandler, 0); + synchronized (mService.mGlobalLock) { + mPinnedTaskListener = null; + mFreezingTaskConfig = false; + mDeferOrientationTimeoutRunnable.run(); } - mPinnedTaskListener = null; } } PinnedTaskController(WindowManagerService service, DisplayContent displayContent) { mService = service; mDisplayContent = displayContent; - mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo()); + mDeferOrientationTimeoutRunnable = () -> { + synchronized (mService.mGlobalLock) { + if (mDeferOrientationChanging) { + continueOrientationChange(); + mService.mWindowPlacerLocked.requestTraversal(); + } + } + }; reloadResources(); } - void onConfigurationChanged() { + /** Updates the resources used by pinned controllers. */ + void onPostDisplayConfigurationChanged() { reloadResources(); + mFreezingTaskConfig = false; } /** @@ -113,7 +136,6 @@ class PinnedTaskController { */ private void reloadResources() { final Resources res = mService.mContext.getResources(); - mDisplayContent.getDisplay().getRealMetrics(mTmpMetrics); mMinAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); mMaxAspectRatio = res.getFloat( @@ -143,18 +165,150 @@ class PinnedTaskController { && Float.compare(aspectRatio, mMaxAspectRatio) <= 0; } - /** Returns {@code true} if the PiP is on screen or is changing windowing mode. */ - boolean isPipActiveOrWindowingModeChanging() { - if (mIsPipWindowingModeChanging) { - return true; + /** + * Called when a fullscreen task is entering PiP with display orientation change. This is used + * to avoid flickering when running PiP animation across different orientations. + */ + void deferOrientationChangeForEnteringPipFromFullScreenIfNeeded() { + final Task topFullscreenTask = mDisplayContent.getDefaultTaskDisplayArea() + .getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN); + final ActivityRecord topFullscreen = topFullscreenTask != null + ? topFullscreenTask.topRunningActivity() : null; + if (topFullscreen == null || topFullscreen.hasFixedRotationTransform()) { + return; + } + final int rotation = mDisplayContent.rotationForActivityInDifferentOrientation( + topFullscreen); + if (rotation == ROTATION_UNDEFINED) { + return; + } + // If the next top activity will change the orientation of display, start fixed rotation to + // notify PipTaskOrganizer before it receives task appeared. And defer display orientation + // update until the new PiP bounds are set. + mDisplayContent.setFixedRotationLaunchingApp(topFullscreen, rotation); + mDeferOrientationChanging = true; + mService.mH.removeCallbacks(mDeferOrientationTimeoutRunnable); + final float animatorScale = Math.max(1, mService.getCurrentAnimatorScale()); + mService.mH.postDelayed(mDeferOrientationTimeoutRunnable, + (int) (animatorScale * DEFER_ORIENTATION_CHANGE_TIMEOUT_MS)); + } + + /** Defers orientation change while there is a top fixed rotation activity. */ + boolean shouldDeferOrientationChange() { + return mDeferOrientationChanging; + } + + /** + * Sets the bounds for {@link #startSeamlessRotationIfNeeded} if the orientation of display + * will be changed. + */ + void setEnterPipBounds(Rect bounds) { + if (!mDeferOrientationChanging) { + return; + } + mFreezingTaskConfig = true; + mDestRotatedBounds = new Rect(bounds); + continueOrientationChange(); + } + + /** + * Sets the transaction for {@link #startSeamlessRotationIfNeeded} if the orientation of display + * will be changed. This is only called when finishing recents animation with pending + * orientation change that will be handled by + * {@link DisplayContent.FixedRotationTransitionListener#onFinishRecentsAnimation}. + */ + void setEnterPipTransaction(PictureInPictureSurfaceTransaction tx) { + mFreezingTaskConfig = true; + mPipTransaction = tx; + } + + /** Called when the activity in PiP task has PiP windowing mode (at the end of animation). */ + private void continueOrientationChange() { + mDeferOrientationChanging = false; + mService.mH.removeCallbacks(mDeferOrientationTimeoutRunnable); + final WindowContainer<?> orientationSource = mDisplayContent.getLastOrientationSource(); + if (orientationSource != null && !orientationSource.isAppTransitioning()) { + mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); + } + } + + /** + * Resets rotation and applies scale and position to PiP task surface to match the current + * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it + * receives the callback of fixed rotation completion. + */ + void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t) { + final Rect bounds = mDestRotatedBounds; + final PictureInPictureSurfaceTransaction pipTx = mPipTransaction; + if (bounds == null && pipTx == null) { + return; + } + final TaskDisplayArea taskArea = mDisplayContent.getDefaultTaskDisplayArea(); + final Task pinnedTask = taskArea.getRootPinnedTask(); + if (pinnedTask == null) { + return; } - final Task pinnedTask = mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask(); - return pinnedTask != null && pinnedTask.hasChild(); + + mDestRotatedBounds = null; + mPipTransaction = null; + final Rect areaBounds = taskArea.getBounds(); + if (pipTx != null) { + // The transaction from recents animation is in old rotation. So the position needs to + // be rotated. + float dx = pipTx.mPositionX; + float dy = pipTx.mPositionY; + if (pipTx.mRotation == 90) { + dx = pipTx.mPositionY; + dy = areaBounds.right - pipTx.mPositionX; + } else if (pipTx.mRotation == -90) { + dx = areaBounds.bottom - pipTx.mPositionY; + dy = pipTx.mPositionX; + } + final Matrix matrix = new Matrix(); + matrix.setScale(pipTx.mScaleX, pipTx.mScaleY); + matrix.postTranslate(dx, dy); + t.setMatrix(pinnedTask.getSurfaceControl(), matrix, new float[9]); + Slog.i(TAG, "Seamless rotation PiP tx=" + pipTx + " pos=" + dx + "," + dy); + return; + } + + final PictureInPictureParams params = pinnedTask.getPictureInPictureParams(); + final Rect sourceHintRect = params != null && params.hasSourceBoundsHint() + ? params.getSourceRectHint() + : null; + Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect); + final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect) + ? sourceHintRect : areaBounds; + final int w = contentBounds.width(); + final int h = contentBounds.height(); + final float scale = w <= h ? (float) bounds.width() / w : (float) bounds.height() / h; + final int insetLeft = (int) ((contentBounds.left - areaBounds.left) * scale + .5f); + final int insetTop = (int) ((contentBounds.top - areaBounds.top) * scale + .5f); + final Matrix matrix = new Matrix(); + matrix.setScale(scale, scale); + matrix.postTranslate(bounds.left - insetLeft, bounds.top - insetTop); + t.setMatrix(pinnedTask.getSurfaceControl(), matrix, new float[9]); + } + + /** + * Returns {@code true} to skip {@link Task#onConfigurationChanged} because it is expected that + * there will be a orientation change and a PiP configuration change. + */ + boolean isFreezingTaskConfig(Task task) { + return mFreezingTaskConfig + && task == mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask(); } - /** Sets whether a visible task is changing from or to pinned mode. */ - void setPipWindowingModeChanging(boolean isPipWindowingModeChanging) { - mIsPipWindowingModeChanging = isPipWindowingModeChanging; + /** Resets the states which were used to perform fixed rotation with PiP task. */ + void onCancelFixedRotationTransform(Task task) { + mFreezingTaskConfig = false; + mDeferOrientationChanging = false; + mDestRotatedBounds = null; + mPipTransaction = null; + if (!task.isOrganized()) { + // Force clearing Task#mForceNotOrganized because the display didn't rotate. + task.onConfigurationChanged(task.getParent().getConfiguration()); + } } /** @@ -272,6 +426,14 @@ class PinnedTaskController { void dump(String prefix, PrintWriter pw) { pw.println(prefix + "PinnedTaskController"); + if (mDeferOrientationChanging) pw.println(prefix + " mDeferOrientationChanging=true"); + if (mFreezingTaskConfig) pw.println(prefix + " mFreezingTaskConfig=true"); + if (mDestRotatedBounds != null) { + pw.println(prefix + " mPendingBounds=" + mDestRotatedBounds); + } + if (mPipTransaction != null) { + pw.println(prefix + " mPipTransaction=" + mPipTransaction); + } pw.println(prefix + " mIsImeShowing=" + mIsImeShowing); pw.println(prefix + " mImeHeight=" + mImeHeight); pw.println(prefix + " mAspectRatio=" + mAspectRatio); @@ -288,6 +450,5 @@ class PinnedTaskController { } pw.println(prefix + " ]"); } - pw.println(prefix + " mDisplayInfo=" + mDisplayInfo); } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 19c2e734a125..dec6460147cf 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -36,7 +36,6 @@ import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; import android.annotation.IntDef; import android.annotation.NonNull; import android.app.WindowConfiguration; -import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; @@ -231,17 +230,16 @@ public class RecentsAnimationController implements DeathRecipient { } @Override - public void setFinishTaskBounds(int taskId, Rect destinationBounds, + public void setFinishTaskTransaction(int taskId, PictureInPictureSurfaceTransaction finishTransaction) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, - "setFinishTaskBounds(%d): bounds=%s", taskId, destinationBounds); + "setFinishTaskTransaction(%d): transaction=%s", taskId, finishTransaction); final long token = Binder.clearCallingIdentity(); try { synchronized (mService.getWindowManagerLock()) { for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); if (taskAdapter.mTask.mTaskId == taskId) { - taskAdapter.mFinishBounds.set(destinationBounds); taskAdapter.mFinishTransaction = finishTransaction; break; } @@ -1106,9 +1104,7 @@ public class RecentsAnimationController implements DeathRecipient { private final Rect mBounds = new Rect(); // The bounds of the target relative to its parent. private final Rect mLocalBounds = new Rect(); - // The bounds of the target when animation is finished - private final Rect mFinishBounds = new Rect(); - // Bounds and transform for the final transaction. + // The final surface transaction when animation is finished. private PictureInPictureSurfaceTransaction mFinishTransaction; TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) { @@ -1145,29 +1141,18 @@ public class RecentsAnimationController implements DeathRecipient { } void onCleanup() { - if (!mFinishBounds.isEmpty()) { - final SurfaceControl taskSurface = mTask.mSurfaceControl; + if (mFinishTransaction != null) { final Transaction pendingTransaction = mTask.getPendingTransaction(); - if (mFinishTransaction != null) { - final Matrix matrix = new Matrix(); - matrix.setScale(mFinishTransaction.mScaleX, mFinishTransaction.mScaleY); - if (mFinishTransaction.mRotation != 0) { - matrix.postRotate(mFinishTransaction.mRotation); - } - pendingTransaction.setMatrix(taskSurface, matrix, new float[9]) - .setPosition(taskSurface, - mFinishTransaction.mPositionX, mFinishTransaction.mPositionY) - .setWindowCrop(taskSurface, mFinishTransaction.getWindowCrop()) - .setCornerRadius(taskSurface, mFinishTransaction.mCornerRadius); - mTask.mLastRecentsAnimationBounds.set(mFinishBounds); - mFinishTransaction = null; - } else { - pendingTransaction - .setPosition(taskSurface, mFinishBounds.left, mFinishBounds.top) - .setWindowCrop(taskSurface, mFinishBounds); + PictureInPictureSurfaceTransaction.apply(mFinishTransaction, + mTask.mSurfaceControl, pendingTransaction); + mTask.setLastRecentsAnimationTransaction(mFinishTransaction); + if (mDisplayContent.isFixedRotationLaunchingApp(mTargetActivityRecord)) { + // The transaction is needed for position when rotating the display. + mDisplayContent.mPinnedTaskController.setEnterPipTransaction( + mFinishTransaction); } + mFinishTransaction = null; pendingTransaction.apply(); - mFinishBounds.setEmpty(); } else if (!mTask.isAttached()) { // Apply the task's pending transaction in case it is detached and its transaction // is not reachable. @@ -1220,7 +1205,7 @@ public class RecentsAnimationController implements DeathRecipient { } pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible); pw.println("mLocalBounds=" + mLocalBounds); - pw.println("mFinishBounds=" + mFinishBounds); + pw.println("mFinishTransaction=" + mFinishTransaction); pw.println("mBounds=" + mBounds); pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 176e7e9b4231..d9c5fa43d9e4 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2137,9 +2137,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent> rootTask.setLastNonFullscreenBounds(task.mLastNonFullscreenBounds); rootTask.setBounds(task.getBounds()); - // Move reparent bounds from original task to the new one. - rootTask.mLastRecentsAnimationBounds.set(task.mLastRecentsAnimationBounds); - task.mLastRecentsAnimationBounds.setEmpty(); + // Move the last recents animation transaction from original task to the new one. + if (task.mLastRecentsAnimationTransaction != null) { + rootTask.setLastRecentsAnimationTransaction( + task.mLastRecentsAnimationTransaction); + task.clearLastRecentsAnimationTransaction(); + } // There are multiple activities in the task and moving the top activity should // reveal/leave the other activities in their original task. @@ -2148,7 +2151,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> r.reparent(rootTask, MAX_VALUE, reason); // Ensure the leash of new task is in sync with its current bounds after reparent. - rootTask.maybeApplyLastRecentsAnimationBounds(); + rootTask.maybeApplyLastRecentsAnimationTransaction(); // In the case of this activity entering PIP due to it being moved to the back, // the old activity would have a TRANSIT_TASK_TO_BACK transition that needs to be @@ -2807,10 +2810,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return false; } - Task getLaunchRootTask(@Nullable ActivityRecord r, - @Nullable ActivityOptions options, @Nullable Task candidateTask, boolean onTop) { - return getLaunchRootTask(r, options, candidateTask, onTop, null /* launchParams */, - -1 /* no realCallingPid */, -1 /* no realCallingUid */); + Task getLaunchRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options, + @Nullable Task candidateTask, boolean onTop) { + return getLaunchRootTask(r, options, candidateTask, null /* sourceTask */, onTop, + null /* launchParams */, 0 /* launchFlags */, -1 /* no realCallingPid */, + -1 /* no realCallingUid */); } /** @@ -2819,15 +2823,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * @param r The activity we are trying to launch. Can be null. * @param options The activity options used to the launch. Can be null. * @param candidateTask The possible task the activity might be launched in. Can be null. + * @param sourceTask The task requesting to start activity. Can be null. * @param launchParams The resolved launch params to use. + * @param launchFlags The launch flags for this launch. * @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid} * @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid} * @return The root task to use for the launch or INVALID_TASK_ID. */ Task getLaunchRootTask(@Nullable ActivityRecord r, - @Nullable ActivityOptions options, @Nullable Task candidateTask, boolean onTop, - @Nullable LaunchParamsController.LaunchParams launchParams, int realCallingPid, - int realCallingUid) { + @Nullable ActivityOptions options, @Nullable Task candidateTask, + @Nullable Task sourceTask, boolean onTop, + @Nullable LaunchParamsController.LaunchParams launchParams, int launchFlags, + int realCallingPid, int realCallingUid) { int taskId = INVALID_TASK_ID; int displayId = INVALID_DISPLAY; TaskDisplayArea taskDisplayArea = null; @@ -2891,7 +2898,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Falling back to default task container taskDisplayArea = taskDisplayArea.mDisplayContent.getDefaultTaskDisplayArea(); rootTask = taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, - launchParams, activityType, onTop); + sourceTask, launchParams, launchFlags, activityType, onTop); if (rootTask != null) { return rootTask; } @@ -2946,8 +2953,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - return container.getOrCreateRootTask( - r, options, candidateTask, launchParams, activityType, onTop); + return container.getOrCreateRootTask(r, options, candidateTask, sourceTask, launchParams, + launchFlags, activityType, onTop); } /** @return true if activity record is null or can be launched on provided display. */ diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java index 39ac16a2f5d3..48a7bdec2b94 100644 --- a/services/core/java/com/android/server/wm/StrictModeFlash.java +++ b/services/core/java/com/android/server/wm/StrictModeFlash.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.graphics.BLASTBufferQueue; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PixelFormat; @@ -27,28 +28,27 @@ import android.view.Surface; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; -import java.util.function.Supplier; - class StrictModeFlash { private static final String TAG = TAG_WITH_CLASS_NAME ? "StrictModeFlash" : TAG_WM; + private static final String TITLE = "StrictModeFlash"; private final SurfaceControl mSurfaceControl; private final Surface mSurface; + private final BLASTBufferQueue mBlastBufferQueue; + private int mLastDW; private int mLastDH; private boolean mDrawNeeded; private final int mThickness = 20; - StrictModeFlash(Supplier<Surface> surfaceFactory, DisplayContent dc, - SurfaceControl.Transaction t) { - mSurface = surfaceFactory.get(); + StrictModeFlash(DisplayContent dc, SurfaceControl.Transaction t) { SurfaceControl ctrl = null; try { ctrl = dc.makeOverlay() - .setName("StrictModeFlash") - .setBufferSize(1, 1) + .setName(TITLE) + .setBLASTLayer() .setFormat(PixelFormat.TRANSLUCENT) - .setCallsite("StrictModeFlash") + .setCallsite(TITLE) .build(); // one more than Watermark? arbitrary. @@ -56,14 +56,15 @@ class StrictModeFlash { t.setPosition(ctrl, 0, 0); t.show(ctrl); // Ensure we aren't considered as obscuring for Input purposes. - InputMonitor.setTrustedOverlayInputInfo(ctrl, t, dc.getDisplayId(), - "StrictModeFlash"); - - mSurface.copyFrom(ctrl); + InputMonitor.setTrustedOverlayInputInfo(ctrl, t, dc.getDisplayId(), TITLE); } catch (OutOfResourcesException e) { } mSurfaceControl = ctrl; mDrawNeeded = true; + + mBlastBufferQueue = new BLASTBufferQueue(TITLE, mSurfaceControl, 1 /* width */, + 1 /* height */, PixelFormat.RGBA_8888); + mSurface = mBlastBufferQueue.createSurface(); } private void drawIfNeeded() { @@ -73,13 +74,12 @@ class StrictModeFlash { mDrawNeeded = false; final int dw = mLastDW; final int dh = mLastDH; + mBlastBufferQueue.update(mSurfaceControl, dw, dh, PixelFormat.RGBA_8888); - Rect dirty = new Rect(0, 0, dw, dh); Canvas c = null; try { - c = mSurface.lockCanvas(dirty); - } catch (IllegalArgumentException e) { - } catch (Surface.OutOfResourcesException e) { + c = mSurface.lockCanvas(null); + } catch (IllegalArgumentException | OutOfResourcesException e) { } if (c == null) { return; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 858d9f366a83..2a0041afd9d0 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -176,12 +176,15 @@ import android.app.servertransaction.NewIntentItem; import android.app.servertransaction.PauseActivityItem; import android.app.servertransaction.ResumeActivityItem; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; @@ -211,6 +214,7 @@ import android.view.SurfaceControl; import android.view.WindowManager; import android.view.WindowManager.TransitionOldType; import android.window.ITaskOrganizer; +import android.window.PictureInPictureSurfaceTransaction; import android.window.StartingWindowInfo; import android.window.TaskSnapshot; import android.window.WindowContainerToken; @@ -470,14 +474,14 @@ class Task extends WindowContainer<WindowContainer> { int mMinWidth; int mMinHeight; - // The bounds of the target when recents animation is finished. + // The surface transition of the target when recents animation is finished. // This is originally introduced to carry out the current surface control position and window // crop when a multi-activity task enters pip with autoEnterPip enabled. In such case, // the surface control of the task will be animated in Launcher and then the top activity is // reparented to pinned root task. - // Do not forget to reset this to null after reparenting. + // Do not forget to reset this after reparenting. // TODO: remove this once the recents animation is moved to the Shell - final Rect mLastRecentsAnimationBounds = new Rect(); + PictureInPictureSurfaceTransaction mLastRecentsAnimationTransaction; static final int LAYER_RANK_INVISIBLE = -1; // Ranking (from top) of this task among all visible tasks. (-1 means it's not visible) @@ -2265,7 +2269,6 @@ class Task extends WindowContainer<WindowContainer> { } if (pipChanging) { - mDisplayContent.getPinnedTaskController().setPipWindowingModeChanging(true); // If the top activity is using fixed rotation, it should be changing from PiP to // fullscreen with display orientation change. Do not notify fullscreen task organizer // because the restoration of task surface and the transformation of activity surface @@ -2274,29 +2277,15 @@ class Task extends WindowContainer<WindowContainer> { if (r != null && mDisplayContent.isFixedRotationLaunchingApp(r)) { mForceNotOrganized = true; } - } else if (mForceNotOrganized) { + } else { // If the display orientation change is done, let the corresponding task organizer take // back the control of this task. - final ActivityRecord r = topRunningActivity(); - if (r == null || !mDisplayContent.isFixedRotationLaunchingApp(r)) { - mForceNotOrganized = false; - } + mForceNotOrganized = false; } - try { - // We have 2 reasons why we need to report orientation change here. - // 1. In some cases (e.g. freeform -> fullscreen) we don't have other ways of reporting. - // 2. Report orientation as soon as possible so that the display can freeze earlier if - // the display orientation will be changed. Because the surface bounds of activity - // may have been set to fullscreen but the activity hasn't redrawn its content yet, - // the rotation animation needs to capture snapshot earlier to avoid animating from - // an intermediate state. - if (oldOrientation != getOrientation()) { - onDescendantOrientationChanged(this); - } - } finally { - if (pipChanging) { - mDisplayContent.getPinnedTaskController().setPipWindowingModeChanging(false); - } + + // Report orientation change such as changing from freeform to fullscreen. + if (oldOrientation != getOrientation()) { + onDescendantOrientationChanged(this); } saveLaunchingStateIfNeeded(); @@ -2319,6 +2308,15 @@ class Task extends WindowContainer<WindowContainer> { @Override public void onConfigurationChanged(Configuration newParentConfig) { + if (mDisplayContent != null + && mDisplayContent.mPinnedTaskController.isFreezingTaskConfig(this)) { + // It happens when animating from fullscreen to PiP with orientation change. Because + // the activity in this pinned task is in fullscreen windowing mode (see + // RootWindowContainer#moveActivityToPinnedRootTask) and the activity will be set to + // pinned mode after the animation is done, the configuration change by orientation + // change is just an intermediate state that should be ignored to avoid flickering. + return; + } // Calling Task#onConfigurationChanged() for leaf task since the ops in this method are // particularly for root tasks, like preventing bounds changes when inheriting certain // windowing mode. @@ -4469,10 +4467,10 @@ class Task extends WindowContainer<WindowContainer> { pw.print(" mSupportsPictureInPicture="); pw.print(mSupportsPictureInPicture); pw.print(" isResizeable="); pw.println(isResizeable()); pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime); + pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)"); if (mForceNotOrganized) { pw.print(prefix); pw.println("mForceNotOrganized=true"); } - pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)"); } @Override @@ -5435,6 +5433,13 @@ class Task extends WindowContainer<WindowContainer> { // Nothing else to do if we don't have a window container yet. E.g. call from ctor. return; } + + // From fullscreen to PiP. + if (topActivity != null && currentMode == WINDOWING_MODE_FULLSCREEN + && windowingMode == WINDOWING_MODE_PINNED) { + mDisplayContent.mPinnedTaskController + .deferOrientationChangeForEnteringPipFromFullScreenIfNeeded(); + } } finally { mAtmService.continueWindowLayout(); } @@ -6674,8 +6679,30 @@ class Task extends WindowContainer<WindowContainer> { prev = null; } } - final int splashScreenThemeResId = options != null + + // TODO(185200798): Persist theme name instead of theme if + int splashScreenThemeResId = options != null ? options.getSplashScreenThemeResId() : 0; + + // User can override the splashscreen theme. The theme name is used to persist + // the setting, so if no theme is set in the ActivityOptions, we check if has + // been persisted here. + if (splashScreenThemeResId == 0) { + try { + String themeName = mAtmService.getPackageManager() + .getSplashScreenTheme(r.packageName, r.mUserId); + if (themeName != null) { + Context packageContext = mAtmService.mContext + .createPackageContext(r.packageName, 0); + splashScreenThemeResId = packageContext.getResources() + .getIdentifier(themeName, null, null); + } + } catch (RemoteException | PackageManager.NameNotFoundException + | Resources.NotFoundException ignore) { + // Just use the default theme + } + } + r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity), splashScreenThemeResId, samePackage); } @@ -7665,14 +7692,22 @@ class Task extends WindowContainer<WindowContainer> { reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM); } - void maybeApplyLastRecentsAnimationBounds() { - if (!mLastRecentsAnimationBounds.isEmpty()) { - getPendingTransaction() - .setPosition(mSurfaceControl, mLastRecentsAnimationBounds.left, - mLastRecentsAnimationBounds.top) - .setWindowCrop(mSurfaceControl, mLastRecentsAnimationBounds.width(), - mLastRecentsAnimationBounds.height()); - mLastRecentsAnimationBounds.setEmpty(); + void setLastRecentsAnimationTransaction( + @NonNull PictureInPictureSurfaceTransaction transaction) { + mLastRecentsAnimationTransaction = new PictureInPictureSurfaceTransaction(transaction); + } + + void clearLastRecentsAnimationTransaction() { + mLastRecentsAnimationTransaction = null; + // reset also the transform introduced by mLastRecentsAnimationTransaction + getPendingTransaction().setMatrix(mSurfaceControl, Matrix.IDENTITY_MATRIX, new float[9]); + } + + void maybeApplyLastRecentsAnimationTransaction() { + if (mLastRecentsAnimationTransaction != null) { + PictureInPictureSurfaceTransaction.apply(mLastRecentsAnimationTransaction, + mSurfaceControl, getPendingTransaction()); + mLastRecentsAnimationTransaction = null; } } @@ -7917,6 +7952,17 @@ class Task extends WindowContainer<WindowContainer> { private boolean mHasBeenVisible; private boolean mRemoveWithTaskOrganizer; + /** + * Records the source task that requesting to build a new task, used to determine which of + * the adjacent roots should be launch root of the new task. + */ + private Task mSourceTask; + + /** + * Records launch flags to apply when launching new task. + */ + private int mLaunchFlags; + Builder(ActivityTaskManagerService atm) { mAtmService = atm; } @@ -7926,6 +7972,16 @@ class Task extends WindowContainer<WindowContainer> { return this; } + Builder setSourceTask(Task sourceTask) { + mSourceTask = sourceTask; + return this; + } + + Builder setLaunchFlags(int launchFlags) { + mLaunchFlags = launchFlags; + return this; + } + Builder setTaskId(int taskId) { mTaskId = taskId; return this; @@ -8180,9 +8236,14 @@ class Task extends WindowContainer<WindowContainer> { tda.getRootPinnedTask().dismissPip(); } + if (mIntent != null) { + mLaunchFlags |= mIntent.getFlags(); + } + // Task created by organizer are added as root. final Task launchRootTask = mCreatedByOrganizer - ? null : tda.getLaunchRootTask(mWindowingMode, mActivityType, mActivityOptions); + ? null : tda.getLaunchRootTask(mWindowingMode, mActivityType, mActivityOptions, + mSourceTask, mLaunchFlags); if (launchRootTask != null) { // Since this task will be put into a root task, its windowingMode will be // inherited. diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 4d85e7bda900..cda8c4b78b0c 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -27,6 +27,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -43,7 +44,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.Nullable; import android.app.ActivityOptions; import android.app.WindowConfiguration; -import android.content.Intent; import android.os.UserHandle; import android.util.IntArray; import android.util.Slog; @@ -133,6 +133,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { private final ArrayList<LaunchRootTaskDef> mLaunchRootTasks = new ArrayList<>(); /** + * A launch root task for activity launching with {@link FLAG_ACTIVITY_LAUNCH_ADJACENT} flag. + */ + private Task mLaunchAdjacentFlagRootTask; + + /** * A focusable root task that is purposely to be positioned at the top. Although the root * task may not have the topmost index, it is used as a preferred candidate to prevent being * unable to resume target root task properly when there are other focusable always-on-top @@ -1013,6 +1018,9 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { if (mPreferredTopFocusableRootTask == rootTask) { mPreferredTopFocusableRootTask = null; } + if (mLaunchAdjacentFlagRootTask == rootTask) { + mLaunchAdjacentFlagRootTask = null; + } mDisplayContent.releaseSelfIfNeeded(); onRootTaskOrderChanged(rootTask); } @@ -1047,11 +1055,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { * Returns an existing root task compatible with the windowing mode and activity type or * creates one if a compatible root task doesn't exist. * - * @see #getOrCreateRootTask(int, int, boolean, Intent, Task, ActivityOptions) + * @see #getOrCreateRootTask(int, int, boolean, Task, Task, ActivityOptions, int) */ Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop) { - return getOrCreateRootTask(windowingMode, activityType, onTop, null /* intent */, - null /* candidateTask */, null /* options */); + return getOrCreateRootTask(windowingMode, activityType, onTop, null /* candidateTask */, + null /* sourceTask */, null /* options */, 0 /* intent */); } /** @@ -1060,11 +1068,21 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { * For one level task, the candidate task would be reused to also be the root task or create * a new root task if no candidate task. * + * @param windowingMode The windowing mode the root task should be created in. + * @param activityType The activityType the root task should be created in. + * @param onTop If true the root task will be created at the top of the display, + * else at the bottom. + * @param candidateTask The possible task the activity might be launched in. Can be null. + * @param sourceTask The task requesting to start activity. Used to determine which of the + * adjacent roots should be launch root of the new task. Can be null. + * @param options The activity options used to the launch. Can be null. + * @param launchFlags The launch flags for this launch. + * @return The root task to use for the launch. * @see #getRootTask(int, int) - * @see #createRootTask(int, int, boolean) */ Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop, - Intent intent, Task candidateTask, ActivityOptions options) { + @Nullable Task candidateTask, @Nullable Task sourceTask, + @Nullable ActivityOptions options, int launchFlags) { // Need to pass in a determined windowing mode to see if a new root task should be created, // so use its parent's windowing mode if it is undefined. if (!alwaysCreateRootTask( @@ -1077,7 +1095,8 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } else if (candidateTask != null) { final Task rootTask = candidateTask; final int position = onTop ? POSITION_TOP : POSITION_BOTTOM; - final Task launchRootTask = getLaunchRootTask(windowingMode, activityType, options); + final Task launchRootTask = getLaunchRootTask(windowingMode, activityType, options, + sourceTask, launchFlags); if (launchRootTask != null) { if (rootTask.getParent() == null) { @@ -1103,8 +1122,9 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { .setActivityType(activityType) .setOnTop(onTop) .setParent(this) - .setIntent(intent) + .setSourceTask(sourceTask) .setActivityOptions(options) + .setLaunchFlags(launchFlags) .build(); } @@ -1114,9 +1134,9 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { * * @see #getOrCreateRootTask(int, int, boolean) */ - Task getOrCreateRootTask(@Nullable ActivityRecord r, - @Nullable ActivityOptions options, @Nullable Task candidateTask, - @Nullable LaunchParams launchParams, int activityType, boolean onTop) { + Task getOrCreateRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options, + @Nullable Task candidateTask, @Nullable Task sourceTask, + @Nullable LaunchParams launchParams, int launchFlags, int activityType, boolean onTop) { int windowingMode = WINDOWING_MODE_UNDEFINED; if (launchParams != null) { // If launchParams isn't null, windowing mode is already resolved. @@ -1130,8 +1150,8 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // UNDEFINED windowing mode is a valid result and means that the new root task will inherit // it's display's windowing mode. windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType); - return getOrCreateRootTask(windowingMode, activityType, onTop, null /* intent */, - candidateTask, options); + return getOrCreateRootTask(windowingMode, activityType, onTop, candidateTask, sourceTask, + options, launchFlags); } @VisibleForTesting @@ -1199,6 +1219,24 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } } + void setLaunchAdjacentFlagRootTask(@Nullable Task adjacentFlagRootTask) { + if (adjacentFlagRootTask != null) { + if (!adjacentFlagRootTask.mCreatedByOrganizer) { + throw new IllegalArgumentException( + "Can't set not mCreatedByOrganizer as launch adjacent flag root tr=" + + adjacentFlagRootTask); + } + + if (adjacentFlagRootTask.mAdjacentTask == null) { + throw new UnsupportedOperationException( + "Can't set non-adjacent root as launch adjacent flag root tr=" + + adjacentFlagRootTask); + } + } + + mLaunchAdjacentFlagRootTask = adjacentFlagRootTask; + } + private @Nullable LaunchRootTaskDef getLaunchRootTaskDef(Task rootTask) { LaunchRootTaskDef def = null; for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) { @@ -1209,7 +1247,9 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { return def; } - Task getLaunchRootTask(int windowingMode, int activityType, ActivityOptions options) { + @Nullable + Task getLaunchRootTask(int windowingMode, int activityType, @Nullable ActivityOptions options, + @Nullable Task sourceTask, int launchFlags) { // Try to use the launch root task in options if available. if (options != null) { final Task launchRootTask = Task.fromWindowContainerToken(options.getLaunchRootTask()); @@ -1219,6 +1259,19 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } } + // Use launch-adjacent-flag-root if launching with launch-adjacent flag. + if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0 + && mLaunchAdjacentFlagRootTask != null) { + // If the adjacent launch is coming from the same root, launch to adjacent root instead. + if (sourceTask != null + && sourceTask.getRootTask().mTaskId == mLaunchAdjacentFlagRootTask.mTaskId + && mLaunchAdjacentFlagRootTask.mAdjacentTask != null) { + return mLaunchAdjacentFlagRootTask.mAdjacentTask; + } else { + return mLaunchAdjacentFlagRootTask; + } + } + for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) { if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) { return mLaunchRootTasks.get(i).task; @@ -1963,7 +2016,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // Reparent task to corresponding launch root or display area. final WindowContainer launchRoot = task.supportsSplitScreenWindowingMode() ? toDisplayArea.getLaunchRootTask( - task.getWindowingMode(), task.getActivityType(), null /* options */) + task.getWindowingMode(), + task.getActivityType(), + null /* options */, + null /* sourceTask */, + 0 /* launchFlags */) : null; task.reparent(launchRoot == null ? toDisplayArea : launchRoot, POSITION_TOP); diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 625cff340912..29677b22ea81 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -292,7 +292,8 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { mSupervisor.mRootWindowContainer.resolveActivityType(root, options, task); display.forAllTaskDisplayAreas(displayArea -> { final Task launchRoot = displayArea.getLaunchRootTask( - resolvedMode, activityType, null /* ActivityOptions */); + resolvedMode, activityType, null /* ActivityOptions */, + null /* sourceTask*/, 0 /* launchFlags */); if (launchRoot == null) { return false; } diff --git a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java index 1779d2a394eb..7e992ac263bc 100644 --- a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java +++ b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java @@ -17,8 +17,8 @@ package com.android.server.wm; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; +import android.graphics.GraphicBuffer; import android.hardware.HardwareBuffer; -import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -51,14 +51,14 @@ class TaskScreenshotAnimatable implements SurfaceAnimator.Animatable { task, mWidth, mHeight); mSurfaceControl = surfaceControlFactory.apply(new SurfaceSession()) .setName("RecentTaskScreenshotSurface") - .setBufferSize(mWidth, mHeight) + .setBLASTLayer() .setCallsite("TaskScreenshotAnimatable") .build(); if (buffer != null) { - final Surface surface = new Surface(); - surface.copyFrom(mSurfaceControl); - surface.attachAndQueueBufferWithColorSpace(buffer, screenshotBuffer.getColorSpace()); - surface.release(); + GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(buffer); + getPendingTransaction().setBuffer(mSurfaceControl, graphicBuffer); + getPendingTransaction().setColorSpace(mSurfaceControl, + screenshotBuffer.getColorSpace()); final float scale = 1.0f * mTask.getBounds().width() / mWidth; getPendingTransaction().setMatrix(mSurfaceControl, scale, 0, 0, scale); } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index b6109b43851f..42e2d2fc79d3 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -32,6 +32,7 @@ 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.transitTypeToString; +import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; @@ -876,8 +877,18 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // checks to use requested visibility. flags |= FLAG_TRANSLUCENT; } - if (wc.asActivityRecord() != null && wc.asActivityRecord().mUseTransferredAnimation) { - flags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; + final Task task = wc.asTask(); + if (task != null && task.voiceSession != null) { + flags |= FLAG_IS_VOICE_INTERACTION; + } + final ActivityRecord record = wc.asActivityRecord(); + if (record != null) { + if (record.mUseTransferredAnimation) { + flags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; + } + if (record.mVoiceInteraction) { + flags |= FLAG_IS_VOICE_INTERACTION; + } } if (isWallpaper(wc)) { flags |= FLAG_IS_WALLPAPER; diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java index 7f28ffc5634c..66ab094f0994 100644 --- a/services/core/java/com/android/server/wm/Watermark.java +++ b/services/core/java/com/android/server/wm/Watermark.java @@ -18,30 +18,26 @@ package com.android.server.wm; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.graphics.BLASTBufferQueue; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.FontMetricsInt; import android.graphics.PixelFormat; import android.graphics.PorterDuff; -import android.graphics.Rect; import android.graphics.Typeface; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; -import android.view.Display; -import android.view.InputWindowHandle; import android.view.Surface; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; -import java.util.function.Supplier; - /** * Displays a watermark on top of the window manager's windows. */ class Watermark { - private final Display mDisplay; - private final String[] mTokens; + private static final String TITLE = "WatermarkSurface"; + private final String mText; private final Paint mTextPaint; private final int mTextWidth; @@ -51,28 +47,26 @@ class Watermark { private final SurfaceControl mSurfaceControl; private final Surface mSurface; + private final BLASTBufferQueue mBlastBufferQueue; + private int mLastDW; private int mLastDH; private boolean mDrawNeeded; - Watermark(Supplier<Surface> surfaceFactory, DisplayContent dc, DisplayMetrics dm, - String[] tokens, SurfaceControl.Transaction t) { + Watermark(DisplayContent dc, DisplayMetrics dm, String[] tokens, SurfaceControl.Transaction t) { if (false) { Log.i(TAG_WM, "*********************** WATERMARK"); for (int i=0; i<tokens.length; i++) { Log.i(TAG_WM, " TOKEN #" + i + ": " + tokens[i]); } } - mSurface = surfaceFactory.get(); - mDisplay = dc.getDisplay(); - mTokens = tokens; StringBuilder builder = new StringBuilder(32); - int len = mTokens[0].length(); + int len = tokens[0].length(); len = len & ~1; for (int i=0; i<len; i+=2) { - int c1 = mTokens[0].charAt(i); - int c2 = mTokens[0].charAt(i+1); + int c1 = tokens[0].charAt(i); + int c2 = tokens[0].charAt(i + 1); if (c1 >= 'a' && c1 <= 'f') c1 = c1 - 'a' + 10; else if (c1 >= 'A' && c1 <= 'F') c1 = c1 - 'A' + 10; else c1 -= '0'; @@ -118,20 +112,22 @@ class Watermark { SurfaceControl ctrl = null; try { ctrl = dc.makeOverlay() - .setName("WatermarkSurface") - .setBufferSize(1, 1) + .setName(TITLE) + .setBLASTLayer() .setFormat(PixelFormat.TRANSLUCENT) - .setCallsite("Watermark") + .setCallsite(TITLE) .build(); t.setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 100) .setPosition(ctrl, 0, 0) .show(ctrl); // Ensure we aren't considered as obscuring for Input purposes. - InputMonitor.setTrustedOverlayInputInfo(ctrl, t, dc.getDisplayId(), "Watermark"); - mSurface.copyFrom(ctrl); + InputMonitor.setTrustedOverlayInputInfo(ctrl, t, dc.getDisplayId(), TITLE); } catch (OutOfResourcesException e) { } mSurfaceControl = ctrl; + mBlastBufferQueue = new BLASTBufferQueue(TITLE, mSurfaceControl, 1 /* width */, + 1 /* height */, PixelFormat.RGBA_8888); + mSurface = mBlastBufferQueue.createSurface(); } void positionSurface(int dw, int dh, SurfaceControl.Transaction t) { @@ -144,45 +140,46 @@ class Watermark { } void drawIfNeeded() { - if (mDrawNeeded) { - final int dw = mLastDW; - final int dh = mLastDH; - - mDrawNeeded = false; - Rect dirty = new Rect(0, 0, dw, dh); - Canvas c = null; - try { - c = mSurface.lockCanvas(dirty); - } catch (IllegalArgumentException e) { - } catch (Surface.OutOfResourcesException e) { + if (!mDrawNeeded) { + return; + } + + final int dw = mLastDW; + final int dh = mLastDH; + + mDrawNeeded = false; + mBlastBufferQueue.update(mSurfaceControl, dw, dh, PixelFormat.RGBA_8888); + Canvas c = null; + try { + c = mSurface.lockCanvas(null); + } catch (IllegalArgumentException | OutOfResourcesException e) { + } + if (c != null) { + c.drawColor(0, PorterDuff.Mode.CLEAR); + + int deltaX = mDeltaX; + int deltaY = mDeltaY; + + // deltaX shouldn't be close to a round fraction of our + // x step, or else things will line up too much. + int div = (dw + mTextWidth) / deltaX; + int rem = (dw + mTextWidth) - (div * deltaX); + int qdelta = deltaX / 4; + if (rem < qdelta || rem > (deltaX - qdelta)) { + deltaX += deltaX / 3; } - if (c != null) { - c.drawColor(0, PorterDuff.Mode.CLEAR); - - int deltaX = mDeltaX; - int deltaY = mDeltaY; - - // deltaX shouldn't be close to a round fraction of our - // x step, or else things will line up too much. - int div = (dw+mTextWidth)/deltaX; - int rem = (dw+mTextWidth) - (div*deltaX); - int qdelta = deltaX/4; - if (rem < qdelta || rem > (deltaX-qdelta)) { - deltaX += deltaX/3; - } - int y = -mTextHeight; - int x = -mTextWidth; - while (y < (dh+mTextHeight)) { - c.drawText(mText, x, y, mTextPaint); - x += deltaX; - if (x >= dw) { - x -= (dw+mTextWidth); - y += deltaY; - } + int y = -mTextHeight; + int x = -mTextWidth; + while (y < (dh + mTextHeight)) { + c.drawText(mText, x, y, mTextPaint); + x += deltaX; + if (x >= dw) { + x -= (dw + mTextWidth); + y += deltaY; } - mSurface.unlockCanvasAndPost(c); } + mSurface.unlockCanvasAndPost(c); } } } diff --git a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java index b9f67a590c2a..7f21eeb43d59 100644 --- a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java +++ b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java @@ -28,12 +28,13 @@ 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.WindowManagerService.MAX_ANIMATION_DURATION; +import android.graphics.ColorSpace; +import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; import android.graphics.Point; import android.hardware.HardwareBuffer; import android.os.Process; import android.util.proto.ProtoOutputStream; -import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl.Builder; import android.view.SurfaceControl.Transaction; @@ -43,8 +44,6 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.server.wm.SurfaceAnimator.Animatable; import com.android.server.wm.SurfaceAnimator.AnimationType; -import java.util.function.Supplier; - /** * Represents a surface that is displayed over a subclass of {@link WindowContainer} */ @@ -57,30 +56,20 @@ class WindowContainerThumbnail implements Animatable { private final SurfaceAnimator mSurfaceAnimator; private final int mWidth; private final int mHeight; - private final boolean mRelative; - - WindowContainerThumbnail(Supplier<Surface> surfaceFactory, Transaction t, - WindowContainer container, HardwareBuffer thumbnailHeader) { - this(surfaceFactory, t, container, thumbnailHeader, false /* relative */); - } /** * @param t Transaction to create the thumbnail in. * @param container The sub-class of {@link WindowContainer} to associate this thumbnail with. * @param thumbnailHeader A thumbnail or placeholder for thumbnail to initialize with. - * @param relative Whether this thumbnail will be a child of the container (and thus positioned - * relative to it) or not. */ - WindowContainerThumbnail(Supplier<Surface> surfaceFactory, Transaction t, - WindowContainer container, HardwareBuffer thumbnailHeader, boolean relative) { - this(t, container, thumbnailHeader, relative, surfaceFactory.get(), null); + WindowContainerThumbnail(Transaction t, WindowContainer container, + HardwareBuffer thumbnailHeader) { + this(t, container, thumbnailHeader, null /* animator */); } WindowContainerThumbnail(Transaction t, WindowContainer container, - HardwareBuffer thumbnailHeader, boolean relative, Surface drawSurface, - SurfaceAnimator animator) { + HardwareBuffer thumbnailHeader, SurfaceAnimator animator) { mWindowContainer = container; - mRelative = relative; if (animator != null) { mSurfaceAnimator = animator; } else { @@ -99,7 +88,7 @@ class WindowContainerThumbnail implements Animatable { // this to the task. mSurfaceControl = mWindowContainer.makeChildSurface(mWindowContainer.getTopChild()) .setName("thumbnail anim: " + mWindowContainer.toString()) - .setBufferSize(mWidth, mHeight) + .setBLASTLayer() .setFormat(PixelFormat.TRANSLUCENT) .setMetadata(METADATA_WINDOW_TYPE, mWindowContainer.getWindowingMode()) .setMetadata(METADATA_OWNER_UID, Process.myUid()) @@ -108,18 +97,14 @@ class WindowContainerThumbnail implements Animatable { ProtoLog.i(WM_SHOW_TRANSACTIONS, " THUMBNAIL %s: CREATE", mSurfaceControl); - // Transfer the thumbnail to the surface - drawSurface.copyFrom(mSurfaceControl); - drawSurface.attachAndQueueBufferWithColorSpace(thumbnailHeader, null); - drawSurface.release(); + GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(thumbnailHeader); + t.setBuffer(mSurfaceControl, graphicBuffer); + t.setColorSpace(mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB)); t.show(mSurfaceControl); // We parent the thumbnail to the container, and just place it on top of anything else in // the container. t.setLayer(mSurfaceControl, Integer.MAX_VALUE); - if (relative) { - t.reparent(mSurfaceControl, mWindowContainer.getSurfaceControl()); - } } void startAnimation(Transaction t, Animation anim) { @@ -194,9 +179,6 @@ class WindowContainerThumbnail implements Animatable { @Override public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) { t.setLayer(leash, Integer.MAX_VALUE); - if (mRelative) { - t.reparent(leash, mWindowContainer.getSurfaceControl()); - } } @Override diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java index 015a0fb30a5b..a5ebf9ac74b9 100644 --- a/services/core/java/com/android/server/wm/WindowManagerConstants.java +++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java @@ -49,10 +49,6 @@ final class WindowManagerConstants { static final String KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS = "system_gesture_exclusion_log_debounce_millis"; - // Enable logging from the sensor which publishes accel and gyro data generating a rotation - // event - private static final String KEY_RAW_SENSOR_LOGGING_ENABLED = "raw_sensor_logging_enabled"; - private static final int MIN_GESTURE_EXCLUSION_LIMIT_DP = 200; /** @see #KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS */ @@ -62,8 +58,6 @@ final class WindowManagerConstants { /** @see AndroidDeviceConfig#KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE */ boolean mSystemGestureExcludedByPreQStickyImmersive; - boolean mRawSensorLoggingEnabled; - private final WindowManagerGlobalLock mGlobalLock; private final Runnable mUpdateSystemGestureExclusionCallback; private final DeviceConfigInterface mDeviceConfig; @@ -139,9 +133,6 @@ final class WindowManagerConstants { case KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS: updateSystemGestureExclusionLogDebounceMillis(); break; - case KEY_RAW_SENSOR_LOGGING_ENABLED: - updateRawSensorDataLoggingEnabled(); - break; default: break; } @@ -167,12 +158,6 @@ final class WindowManagerConstants { KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false); } - private void updateRawSensorDataLoggingEnabled() { - mRawSensorLoggingEnabled = DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_WINDOW_MANAGER, - KEY_RAW_SENSOR_LOGGING_ENABLED, false); - } - void dump(PrintWriter pw) { pw.println("WINDOW MANAGER CONSTANTS (dumpsys window constants):"); @@ -182,8 +167,6 @@ final class WindowManagerConstants { pw.print("="); pw.println(mSystemGestureExclusionLimitDp); pw.print(" "); pw.print(KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE); pw.print("="); pw.println(mSystemGestureExcludedByPreQStickyImmersive); - pw.print(" "); pw.print(KEY_RAW_SENSOR_LOGGING_ENABLED); - pw.print("="); pw.println(mRawSensorLoggingEnabled); pw.println(); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 12c9b972cecc..9e8b6a34e1d5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -451,8 +451,7 @@ public class WindowManagerService extends IWindowManager.Stub private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000; - @VisibleForTesting - WindowManagerConstants mConstants; + final WindowManagerConstants mConstants; final WindowTracing mWindowTracing; @@ -3659,7 +3658,7 @@ public class WindowManagerService extends IWindowManager.Stub if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> showEmulatorDisplayOverlay"); if (mEmulatorDisplayOverlay == null) { - mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(mSurfaceFactory, mContext, + mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(mContext, getDefaultDisplayContentLocked(), mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER) * TYPE_LAYER_MULTIPLIER + 10, mTransaction); @@ -3699,8 +3698,8 @@ public class WindowManagerService extends IWindowManager.Stub // b/31532461 // TODO(multi-display): support multiple displays if (mStrictModeFlash == null) { - mStrictModeFlash = new StrictModeFlash(mSurfaceFactory, - getDefaultDisplayContentLocked(), mTransaction); + mStrictModeFlash = new StrictModeFlash(getDefaultDisplayContentLocked(), + mTransaction); } mStrictModeFlash.setVisibility(on, mTransaction); mTransaction.apply(); @@ -5554,11 +5553,6 @@ public class WindowManagerService extends IWindowManager.Stub mBlurController.unregisterCrossWindowBlurEnabledListener(listener); } - @Override - public void setForceCrossWindowBlurDisabled(boolean disable) { - mBlurController.setForceCrossWindowBlurDisabled(disable); - } - // ------------------------------------------------------------- // Internals // ------------------------------------------------------------- @@ -5895,8 +5889,8 @@ public class WindowManagerService extends IWindowManager.Stub if (toks != null && toks.length > 0) { // TODO(multi-display): Show watermarks on secondary displays. final DisplayContent displayContent = getDefaultDisplayContentLocked(); - mWatermark = new Watermark(mSurfaceFactory, displayContent, - displayContent.mRealDisplayMetrics, toks, mTransaction); + mWatermark = new Watermark(displayContent, displayContent.mRealDisplayMetrics, + toks, mTransaction); mTransaction.apply(); } } @@ -6298,7 +6292,7 @@ public class WindowManagerService extends IWindowManager.Stub } }); pw.print(" mInTouchMode="); pw.println(mInTouchMode); - pw.print(" mBlurEnabled="); pw.println(mBlurController.mBlurEnabled); + pw.print(" mBlurEnabled="); pw.println(mBlurController.getBlurEnabled()); pw.print(" mLastDisplayFreezeDuration="); TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw); if ( mLastFinishedFreezeSource != null) { diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 1b578d1d452c..4dc60070d6ec 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -31,6 +31,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ShellCommand; import android.os.UserHandle; +import android.provider.Settings; import android.util.DisplayMetrics; import android.util.Pair; import android.view.Display; @@ -216,7 +217,7 @@ public class WindowManagerShellCommand extends ShellCommand { String arg = getNextArg(); if (arg == null) { pw.println("Blur supported on device: " + CROSS_WINDOW_BLUR_SUPPORTED); - pw.println("Blur enabled: " + mInternal.mBlurController.mBlurEnabled); + pw.println("Blur enabled: " + mInternal.mBlurController.getBlurEnabled()); return 0; } @@ -235,7 +236,9 @@ public class WindowManagerShellCommand extends ShellCommand { return -1; } - mInterface.setForceCrossWindowBlurDisabled(disableBlur); + Settings.Global.putInt(mInternal.mContext.getContentResolver(), + Settings.Global.DISABLE_WINDOW_BLURS, disableBlur ? 1 : 0); + return 0; } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 9382b8eed0b8..c29211f3bb65 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -22,6 +22,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; @@ -320,6 +321,26 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } break; } + case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: { + final WindowContainer wc = WindowContainer.fromBinder( + hop.getContainer()); + final Task task = wc != null ? wc.asTask() : null; + if (task == null) { + throw new IllegalArgumentException("Cannot set " + + "non-task as launch root: " + wc); + } else if (!task.mCreatedByOrganizer) { + throw new UnsupportedOperationException("Cannot set " + + "non-organized task as adjacent flag root: " + wc); + } else if (task.mAdjacentTask == null) { + throw new UnsupportedOperationException("Cannot set " + + "non-adjacent task as adjacent flag root: " + wc); + } + + final boolean clearRoot = hop.getToTop(); + task.getDisplayArea() + .setLaunchAdjacentFlagRootTask(clearRoot ? null : task); + break; + } case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); break; @@ -491,7 +512,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub Rect enterPipBounds = c.getEnterPipBounds(); if (enterPipBounds != null) { - mService.mTaskSupervisor.updatePictureInPictureMode(tr, enterPipBounds, true); + tr.mDisplayContent.mPinnedTaskController.setEnterPipBounds(enterPipBounds); } return effects; diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java index be6847aba12a..3e099fb84f03 100644 --- a/services/core/java/com/android/server/wm/WindowOrientationListener.java +++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java @@ -85,7 +85,6 @@ public abstract class WindowOrientationListener { private int mCurrentRotation = -1; private final Context mContext; - private final WindowManagerConstants mConstants; private final Object mLock = new Object(); @@ -94,11 +93,9 @@ public abstract class WindowOrientationListener { * * @param context for the WindowOrientationListener. * @param handler Provides the Looper for receiving sensor updates. - * @param wmService WindowManagerService to read the device config from. */ - public WindowOrientationListener( - Context context, Handler handler, WindowManagerService wmService) { - this(context, handler, wmService, SensorManager.SENSOR_DELAY_UI); + public WindowOrientationListener(Context context, Handler handler) { + this(context, handler, SensorManager.SENSOR_DELAY_UI); } /** @@ -115,10 +112,9 @@ public abstract class WindowOrientationListener { * This constructor is private since no one uses it. */ private WindowOrientationListener( - Context context, Handler handler, WindowManagerService wmService, int rate) { + Context context, Handler handler, int rate) { mContext = context; mHandler = handler; - mConstants = wmService.mConstants; mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); mRate = rate; List<Sensor> l = mSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION); @@ -1134,16 +1130,11 @@ public abstract class WindowOrientationListener { return; } - // Log raw sensor rotation. - if (evaluateRotationChangeLocked() >= 0) { - if (mConstants.mRawSensorLoggingEnabled) { - FrameworkStatsLog.write( - FrameworkStatsLog.DEVICE_ROTATED, - event.timestamp, - rotationToLogEnum(reportedRotation), - FrameworkStatsLog.DEVICE_ROTATED__ROTATION_EVENT_TYPE__ACTUAL_EVENT); - } - } + FrameworkStatsLog.write( + FrameworkStatsLog.DEVICE_ROTATED, + event.timestamp, + rotationToLogEnum(reportedRotation), + FrameworkStatsLog.DEVICE_ROTATED__ROTATION_EVENT_TYPE__ACTUAL_EVENT); if (isRotationResolverEnabled()) { if (mRotationResolverService == null) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 83f74cd49b59..f71d08afb1cb 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -906,6 +906,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // The transform of its surface is handled by fixed rotation. return; } + final Task task = getTask(); + if (task != null && task.inPinnedWindowingMode()) { + // It is handled by PinnedTaskController. Note that the windowing mode of activity + // and windows may still be fullscreen. + return; + } if (mPendingSeamlessRotate != null) { oldRotation = mPendingSeamlessRotate.getOldRotation(); @@ -5348,7 +5354,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } private boolean shouldDrawBlurBehind() { - return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 && mWmService.mBlurController.mBlurEnabled; + return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 + && mWmService.mBlurController.getBlurEnabled(); } /** diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 3ee2b8dd57d2..cca62b9a7274 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -118,6 +118,7 @@ static struct { jmethodID getVirtualKeyQuietTimeMillis; jmethodID getExcludedDeviceNames; jmethodID getInputPortAssociations; + jmethodID getInputUniqueIdAssociations; jmethodID getKeyRepeatTimeout; jmethodID getKeyRepeatDelay; jmethodID getHoverTapTimeout; @@ -579,6 +580,21 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon } env->DeleteLocalRef(portAssociations); } + outConfig->uniqueIdAssociations.clear(); + jobjectArray uniqueIdAssociations = jobjectArray( + env->CallObjectMethod(mServiceObj, gServiceClassInfo.getInputUniqueIdAssociations)); + if (!checkAndClearExceptionFromCallback(env, "getInputUniqueIdAssociations") && + uniqueIdAssociations) { + jsize length = env->GetArrayLength(uniqueIdAssociations); + for (jsize i = 0; i < length / 2; i++) { + std::string inputDeviceUniqueId = + getStringElementFromJavaArray(env, uniqueIdAssociations, 2 * i); + std::string displayUniqueId = + getStringElementFromJavaArray(env, uniqueIdAssociations, 2 * i + 1); + outConfig->uniqueIdAssociations.insert({inputDeviceUniqueId, displayUniqueId}); + } + env->DeleteLocalRef(uniqueIdAssociations); + } jint hoverTapTimeout = env->CallIntMethod(mServiceObj, gServiceClassInfo.getHoverTapTimeout); @@ -2134,6 +2150,12 @@ static void nativeNotifyPortAssociationsChanged(JNIEnv* env, jclass /* clazz */, InputReaderConfiguration::CHANGE_DISPLAY_INFO); } +static void nativeChangeUniqueIdAssociation(JNIEnv* env, jclass /* clazz */, jlong ptr) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + im->getInputManager()->getReader()->requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_DISPLAY_INFO); +} + static void nativeSetMotionClassifierEnabled(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jboolean enabled) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); @@ -2316,6 +2338,7 @@ static const JNINativeMethod gInputManagerMethods[] = { (void*)nativeSetCustomPointerIcon}, {"nativeCanDispatchToDisplay", "(JII)Z", (void*)nativeCanDispatchToDisplay}, {"nativeNotifyPortAssociationsChanged", "(J)V", (void*)nativeNotifyPortAssociationsChanged}, + {"nativeChangeUniqueIdAssociation", "(J)V", (void*)nativeChangeUniqueIdAssociation}, {"nativeSetMotionClassifierEnabled", "(JZ)V", (void*)nativeSetMotionClassifierEnabled}, {"nativeGetSensorList", "(JI)[Landroid/hardware/input/InputSensorInfo;", (void*)nativeGetSensorList}, @@ -2425,6 +2448,9 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gServiceClassInfo.getInputPortAssociations, clazz, "getInputPortAssociations", "()[Ljava/lang/String;"); + GET_METHOD_ID(gServiceClassInfo.getInputUniqueIdAssociations, clazz, + "getInputUniqueIdAssociations", "()[Ljava/lang/String;"); + GET_METHOD_ID(gServiceClassInfo.getKeyRepeatTimeout, clazz, "getKeyRepeatTimeout", "()I"); diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index e4b961299f12..01834ddfbeac 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -48,6 +48,9 @@ <xs:element type="nonNegativeDecimal" name="screenBrightnessRampSlowIncrease"> <xs:annotation name="final"/> </xs:element> + <xs:element type="sensorDetails" name="lightSensor"> + <xs:annotation name="final"/> + </xs:element> </xs:sequence> </xs:complexType> </xs:element> @@ -119,4 +122,18 @@ <xs:minInclusive value="0.0"/> </xs:restriction> </xs:simpleType> + + <xs:complexType name="sensorDetails"> + <xs:sequence> + <xs:element type="xs:string" name="type" minOccurs="0" maxOccurs="1"> + <xs:annotation name="nullable" /> + <xs:annotation name="final"/> + </xs:element> + <xs:element type="xs:string" name="name" minOccurs="0" maxOccurs="1"> + <xs:annotation name="nullable" /> + <xs:annotation name="final"/> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:schema> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index eb3f1b751963..a848f82a0cb6 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -4,6 +4,7 @@ package com.android.server.display.config { public class DisplayConfiguration { ctor public DisplayConfiguration(); method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode(); + method public final com.android.server.display.config.SensorDetails getLightSensor(); method public com.android.server.display.config.DisplayQuirks getQuirks(); method @NonNull public final java.math.BigDecimal getScreenBrightnessDefault(); method @NonNull public final com.android.server.display.config.NitsMap getScreenBrightnessMap(); @@ -12,6 +13,7 @@ package com.android.server.display.config { method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease(); method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease(); method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode); + method public final void setLightSensor(com.android.server.display.config.SensorDetails); method public void setQuirks(com.android.server.display.config.DisplayQuirks); method public final void setScreenBrightnessDefault(@NonNull java.math.BigDecimal); method public final void setScreenBrightnessMap(@NonNull com.android.server.display.config.NitsMap); @@ -61,6 +63,14 @@ package com.android.server.display.config { method public final void setValue(@NonNull java.math.BigDecimal); } + public class SensorDetails { + ctor public SensorDetails(); + method @Nullable public final String getName(); + method @Nullable public final String getType(); + method public final void setName(@Nullable String); + method public final void setType(@Nullable String); + } + public class XmlParser { ctor public XmlParser(); method public static com.android.server.display.config.DisplayConfiguration read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index cda01dbae945..8274e380c89a 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4507,8 +4507,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(parentUser); final List<PasswordValidationError> passwordValidationErrors = - PasswordMetrics.validatePasswordMetrics( - minMetrics, complexity, false, metrics); + PasswordMetrics.validatePasswordMetrics(minMetrics, complexity, metrics); isSufficient = passwordValidationErrors.isEmpty(); } DevicePolicyEventLogger @@ -4585,7 +4584,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity); } return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics), - maxRequiredComplexity, false, metrics).isEmpty(); + maxRequiredComplexity, metrics).isEmpty(); } } @@ -4621,8 +4620,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int complexity = getAggregatedPasswordComplexityLocked(userId); PasswordMetrics minMetrics = getPasswordMinimumMetricsUnchecked(userId); final List<PasswordValidationError> passwordValidationErrors = - PasswordMetrics.validatePasswordMetrics( - minMetrics, complexity, false, metrics); + PasswordMetrics.validatePasswordMetrics(minMetrics, complexity, metrics); return passwordValidationErrors.isEmpty(); } @@ -4969,8 +4967,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // TODO: Consider changing validation API to take LockscreenCredential. if (password.isEmpty()) { validationErrors = PasswordMetrics.validatePasswordMetrics( - minMetrics, complexity, isPin, - new PasswordMetrics(CREDENTIAL_TYPE_NONE)); + minMetrics, complexity, new PasswordMetrics(CREDENTIAL_TYPE_NONE)); } else { // TODO(b/120484642): remove getBytes() below validationErrors = PasswordMetrics.validatePassword( @@ -5459,7 +5456,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { && (isProfileOwner(caller) || isDeviceOwner(caller))) || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp))); if (isCredentialManagementApp) { - Preconditions.checkCallAuthorization(isUserSelectable, "The credential " + Preconditions.checkCallAuthorization(!isUserSelectable, "The credential " + "management app is not allowed to install a user selectable key pair"); Preconditions.checkCallAuthorization( isAliasInCredentialManagementAppPolicy(caller, alias), @@ -16774,7 +16771,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { provisioningParams.isKeepAccountMigrated(), callerPackage); if (provisioningParams.isOrganizationOwnedProvisioning()) { - setProfileOwnerOnOrgOwnedDeviceState(admin, userInfo.id, caller.getUserId()); + synchronized (getLockObject()) { + markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, userInfo.id); + } } return userInfo.getUserHandle(); @@ -17006,22 +17005,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void setProfileOwnerOnOrgOwnedDeviceState( - ComponentName admin, @UserIdInt int profileId, @UserIdInt int parentUserId) { - synchronized (getLockObject()) { - markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, profileId); - } - restrictRemovalOfManagedProfile(parentUserId); - } - - private void restrictRemovalOfManagedProfile(@UserIdInt int parentUserId) { - final UserHandle parentUserHandle = UserHandle.of(parentUserId); - mUserManager.setUserRestriction( - UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, - /* value= */ true, - parentUserHandle); - } - @Override public void provisionFullyManagedDevice( @NonNull FullyManagedDeviceProvisioningParams provisioningParams, diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 3511543aff48..f3e7d672ec95 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -2654,9 +2654,6 @@ bool IncrementalService::DataLoaderStub::fsmStep() { } switch (targetStatus) { - case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE: - // Do nothing, this is a reset state. - break; case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: { switch (currentStatus) { case IDataLoaderStatusListener::DATA_LOADER_BINDING: @@ -2677,8 +2674,12 @@ bool IncrementalService::DataLoaderStub::fsmStep() { } case IDataLoaderStatusListener::DATA_LOADER_CREATED: switch (currentStatus) { - case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE: + case IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE: + // Before binding need to make sure we are unbound. + // Otherwise we'll get stuck binding. + return destroy(); + case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: case IDataLoaderStatusListener::DATA_LOADER_BINDING: return bind(); case IDataLoaderStatusListener::DATA_LOADER_BOUND: @@ -2703,7 +2704,8 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount << ", but got: " << mountId; return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch."); } - if (newStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) { + if (newStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE || + newStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) { // User-provided status, let's postpone the handling to avoid possible deadlocks. mService.addTimedJob(*mService.mTimedQueue, id(), Constants::userStatusDelay, [this, newStatus]() { setCurrentStatus(newStatus); }); @@ -2715,7 +2717,7 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount } void IncrementalService::DataLoaderStub::setCurrentStatus(int newStatus) { - int targetStatus, oldStatus; + int oldStatus, oldTargetStatus, newTargetStatus; DataLoaderStatusListener listener; { std::unique_lock lock(mMutex); @@ -2724,22 +2726,31 @@ void IncrementalService::DataLoaderStub::setCurrentStatus(int newStatus) { } oldStatus = mCurrentStatus; - targetStatus = mTargetStatus; + oldTargetStatus = mTargetStatus; listener = mStatusListener; // Change the status. mCurrentStatus = newStatus; mCurrentStatusTs = mService.mClock->now(); - if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE || - mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) { - // For unavailable, unbind from DataLoader to ensure proper re-commit. - setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_DESTROYED); + switch (mCurrentStatus) { + case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE: + // Unavailable, retry. + setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_STARTED); + break; + case IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE: + // Unrecoverable, just unbind. + setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_DESTROYED); + break; + default: + break; } + + newTargetStatus = mTargetStatus; } LOG(DEBUG) << "Current status update for DataLoader " << id() << ": " << oldStatus << " -> " - << newStatus << " (target " << targetStatus << ")"; + << newStatus << " (target " << oldTargetStatus << " -> " << newTargetStatus << ")"; if (listener) { listener->onStatusChanged(id(), newStatus); diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index d339942f367b..68586a89ff07 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -126,7 +126,7 @@ private: class MockDataLoader : public IDataLoader { public: MockDataLoader() { - ON_CALL(*this, create(_, _, _, _)).WillByDefault(Invoke(this, &MockDataLoader::createOk)); + initializeCreateOk(); ON_CALL(*this, start(_)).WillByDefault(Invoke(this, &MockDataLoader::startOk)); ON_CALL(*this, stop(_)).WillByDefault(Invoke(this, &MockDataLoader::stopOk)); ON_CALL(*this, destroy(_)).WillByDefault(Invoke(this, &MockDataLoader::destroyOk)); @@ -145,6 +145,10 @@ public: binder::Status(int32_t id, const std::vector<InstallationFileParcel>& addedFiles, const std::vector<std::string>& removedFiles)); + void initializeCreateOk() { + ON_CALL(*this, create(_, _, _, _)).WillByDefault(Invoke(this, &MockDataLoader::createOk)); + } + void initializeCreateOkNoStatus() { ON_CALL(*this, create(_, _, _, _)) .WillByDefault(Invoke(this, &MockDataLoader::createOkNoStatus)); @@ -275,6 +279,14 @@ public: } return binder::Status::ok(); } + binder::Status bindToDataLoaderOkWithNoDelay(int32_t mountId, + const DataLoaderParamsParcel& params, + int bindDelayMs, + const sp<IDataLoaderStatusListener>& listener, + bool* _aidl_return) { + CHECK(bindDelayMs == 0) << bindDelayMs; + return bindToDataLoaderOk(mountId, params, bindDelayMs, listener, _aidl_return); + } binder::Status bindToDataLoaderOkWith1sDelay(int32_t mountId, const DataLoaderParamsParcel& params, int bindDelayMs, @@ -338,14 +350,13 @@ public: mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE); } binder::Status unbindFromDataLoaderOk(int32_t id) { + mBindDelayMs = -1; if (mDataLoader) { if (auto status = mDataLoader->destroy(id); !status.isOk()) { return status; } mDataLoader = nullptr; - } - mBindDelayMs = -1; - if (mListener) { + } else if (mListener) { mListener->onStatusChanged(id, IDataLoaderStatusListener::DATA_LOADER_DESTROYED); } return binder::Status::ok(); @@ -1210,12 +1221,81 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) { ASSERT_GE(storageId, 0); ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {})); - mDataLoaderManager->setDataLoaderStatusUnavailable(); + mDataLoaderManager->setDataLoaderStatusUnrecoverable(); + + // Timed callback present. + ASSERT_EQ(storageId, mTimedQueue->mId); + ASSERT_GE(mTimedQueue->mAfter, 10ms); + auto timedCallback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + // First callback call to propagate unrecoverable. + timedCallback(); + + // And second call to trigger recreation. ASSERT_NE(nullptr, mLooper->mCallback); ASSERT_NE(nullptr, mLooper->mCallbackData); mLooper->mCallback(-1, -1, mLooper->mCallbackData); } +TEST_F(IncrementalServiceTest, testStartDataLoaderUnavailable) { + mIncFs->openMountSuccess(); + mDataLoader->initializeCreateOkNoStatus(); + + EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(3); + EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(3); + EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(3); + EXPECT_CALL(*mDataLoader, start(_)).Times(1); + EXPECT_CALL(*mDataLoader, destroy(_)).Times(2); + EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); + EXPECT_CALL(*mLooper, addFd(MockIncFs::kPendingReadsFd, _, _, _, _)).Times(1); + EXPECT_CALL(*mLooper, removeFd(MockIncFs::kPendingReadsFd)).Times(1); + TemporaryDir tempDir; + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); + ASSERT_GE(storageId, 0); + + ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)) + .WillByDefault(Invoke(mDataLoaderManager, + &MockDataLoaderManager::bindToDataLoaderOkWithNoDelay)); + + ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, + {}, {})); + + // Unavailable. + mDataLoaderManager->setDataLoaderStatusUnavailable(); + + // Timed callback present. + ASSERT_EQ(storageId, mTimedQueue->mId); + ASSERT_GE(mTimedQueue->mAfter, 10ms); + auto timedCallback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + // Propagating unavailable and expecting it to trigger rebind with 1s retry delay. + ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)) + .WillByDefault(Invoke(mDataLoaderManager, + &MockDataLoaderManager::bindToDataLoaderOkWith1sDelay)); + timedCallback(); + + // Unavailable #2. + mDataLoaderManager->setDataLoaderStatusUnavailable(); + + // Timed callback present. + ASSERT_EQ(storageId, mTimedQueue->mId); + ASSERT_GE(mTimedQueue->mAfter, 10ms); + timedCallback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + // Propagating unavailable and expecting it to trigger rebind with 10s retry delay. + // This time succeed. + mDataLoader->initializeCreateOk(); + ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)) + .WillByDefault(Invoke(mDataLoaderManager, + &MockDataLoaderManager::bindToDataLoaderOkWith10sDelay)); + timedCallback(); +} + TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) { mIncFs->openMountSuccess(); diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index d55bbd1d8e45..683fbd17b78d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -15,6 +15,7 @@ */ package com.android.server.alarm; +import static android.Manifest.permission.SCHEDULE_EXACT_ALARM; import static android.app.AlarmManager.ELAPSED_REALTIME; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE; @@ -54,6 +55,7 @@ import static com.android.server.alarm.AlarmManagerService.ACTIVE_INDEX; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.EXACT_ALARM_DENY_LIST_CHANGED; +import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_ALARMS; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA; @@ -99,7 +101,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verifyNoMoreInteractions; -import android.Manifest; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AlarmManager; @@ -114,7 +115,6 @@ import android.app.compat.CompatChanges; import android.app.usage.UsageStatsManagerInternal; import android.content.Context; import android.content.Intent; -import android.content.PermissionChecker; import android.content.pm.PackageManagerInternal; import android.os.BatteryManager; import android.os.Bundle; @@ -139,14 +139,18 @@ import com.android.dx.mockito.inline.extended.MockedVoidMethod; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; +import com.android.internal.util.ArrayUtils; import com.android.server.AlarmManagerInternal; import com.android.server.AppStateTracker; import com.android.server.AppStateTrackerImpl; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.usage.AppStandbyInternal; +import libcore.util.EmptyArray; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -160,6 +164,7 @@ import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; @@ -176,6 +181,7 @@ public class AlarmManagerServiceTest { private long mAllowWhileIdleWindow; private AlarmManagerService mService; private AppStandbyInternal.AppIdleStateChangeListener mAppStandbyListener; + private AlarmManagerService.UninstallReceiver mPackageChangesReceiver; private AlarmManagerService.ChargingReceiver mChargingReceiver; private IAppOpsCallback mIAppOpsCallback; private IAlarmManager mBinder; @@ -190,6 +196,8 @@ public class AlarmManagerServiceTest { @Mock private DeviceIdleInternal mDeviceIdleInternal; @Mock + private PermissionManagerServiceInternal mPermissionManagerInternal; + @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal; @Mock private AppStandbyInternal mAppStandbyInternal; @@ -351,7 +359,6 @@ public class AlarmManagerServiceTest { .spyStatic(DeviceConfig.class) .mockStatic(LocalServices.class) .spyStatic(Looper.class) - .mockStatic(PermissionChecker.class) .mockStatic(Settings.Global.class) .mockStatic(ServiceManager.class) .spyStatic(UserHandle.class) @@ -361,6 +368,8 @@ public class AlarmManagerServiceTest { doReturn(mIActivityManager).when(ActivityManager::getService); doReturn(mDeviceIdleInternal).when( () -> LocalServices.getService(DeviceIdleInternal.class)); + doReturn(mPermissionManagerInternal).when( + () -> LocalServices.getService(PermissionManagerServiceInternal.class)); doReturn(mActivityManagerInternal).when( () -> LocalServices.getService(ActivityManagerInternal.class)); doReturn(mPackageManagerInternal).when( @@ -399,8 +408,10 @@ public class AlarmManagerServiceTest { when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); - when(mPackageManagerInternal.getPackageUid(eq(TEST_CALLING_PACKAGE), anyInt(), - eq(TEST_CALLING_USER))).thenReturn(TEST_CALLING_UID); + registerAppIds(new String[]{TEST_CALLING_PACKAGE}, + new Integer[]{UserHandle.getAppId(TEST_CALLING_UID)}); + when(mPermissionManagerInternal.getAppOpPermissionPackages( + SCHEDULE_EXACT_ALARM)).thenReturn(EmptyArray.STRING); mInjector = new Injector(mMockContext); mService = new AlarmManagerService(mMockContext, mInjector); @@ -424,13 +435,22 @@ public class AlarmManagerServiceTest { verify(mAppStandbyInternal).addListener(captor.capture()); mAppStandbyListener = captor.getValue(); - ArgumentCaptor<AlarmManagerService.ChargingReceiver> chargingReceiverCaptor = + final ArgumentCaptor<AlarmManagerService.ChargingReceiver> chargingReceiverCaptor = ArgumentCaptor.forClass(AlarmManagerService.ChargingReceiver.class); verify(mMockContext).registerReceiver(chargingReceiverCaptor.capture(), argThat((filter) -> filter.hasAction(BatteryManager.ACTION_CHARGING) && filter.hasAction(BatteryManager.ACTION_DISCHARGING))); mChargingReceiver = chargingReceiverCaptor.getValue(); + final ArgumentCaptor<AlarmManagerService.UninstallReceiver> packageReceiverCaptor = + ArgumentCaptor.forClass(AlarmManagerService.UninstallReceiver.class); + verify(mMockContext).registerReceiver(packageReceiverCaptor.capture(), + argThat((filter) -> filter.hasAction(Intent.ACTION_PACKAGE_ADDED) + && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED))); + mPackageChangesReceiver = packageReceiverCaptor.getValue(); + + assertEquals(mService.mExactAlarmCandidates, Collections.emptySet()); + ArgumentCaptor<IBinder> binderCaptor = ArgumentCaptor.forClass(IBinder.class); verify(() -> ServiceManager.addService(eq(Context.ALARM_SERVICE), binderCaptor.capture(), anyBoolean(), anyInt())); @@ -927,7 +947,8 @@ public class AlarmManagerServiceTest { private void assertAndHandleMessageSync(int what) { final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); - verify(mService.mHandler, atLeastOnce()).sendMessage(messageCaptor.capture()); + verify(mService.mHandler, atLeastOnce()).sendMessageAtTime(messageCaptor.capture(), + anyLong()); final Message lastMessage = messageCaptor.getValue(); assertEquals("Unexpected message send to handler", lastMessage.what, what); @@ -1795,70 +1816,45 @@ public class AlarmManagerServiceTest { } @Test - public void hasScheduleExactAlarmBinderCallEmptyDenyList() throws RemoteException { - doReturn(PermissionChecker.PERMISSION_GRANTED).when( - () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + public void hasScheduleExactAlarmBinderCallNotDenyListed() throws RemoteException { + mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); + assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + + mockExactAlarmPermissionGrant(true, false, MODE_IGNORED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } @Test - public void hasScheduleExactAlarmBinderCallWithDenyList() throws RemoteException { - setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, TEST_CALLING_PACKAGE); - - when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, - TEST_CALLING_PACKAGE)).thenReturn(MODE_ERRORED); - + public void hasScheduleExactAlarmBinderCallDenyListed() throws RemoteException { + mockExactAlarmPermissionGrant(true, true, MODE_ERRORED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never()); - - when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, - TEST_CALLING_PACKAGE)).thenReturn(MODE_DEFAULT); + mockExactAlarmPermissionGrant(true, true, MODE_DEFAULT); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never()); - - when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, - TEST_CALLING_PACKAGE)).thenReturn(MODE_IGNORED); + mockExactAlarmPermissionGrant(true, true, MODE_IGNORED); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never()); - - when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, - TEST_CALLING_PACKAGE)).thenReturn(MODE_ALLOWED); - doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED); + assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + } + @Test + public void hasScheduleExactAlarmBinderCallNotDeclared() throws RemoteException { + mockExactAlarmPermissionGrant(false, false, MODE_DEFAULT); assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); - doReturn(PermissionChecker.PERMISSION_GRANTED).when( - () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + mockExactAlarmPermissionGrant(false, false, MODE_ALLOWED); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), times(2)); + mockExactAlarmPermissionGrant(false, true, MODE_ALLOWED); + assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } @Test @@ -1884,9 +1880,8 @@ public class AlarmManagerServiceTest { mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null); - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never()); + verify(mService, never()).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, + TEST_CALLING_UID); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); } @@ -1969,10 +1964,7 @@ public class AlarmManagerServiceTest { () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), anyString(), any(UserHandle.class))); - doReturn(PermissionChecker.PERMISSION_GRANTED).when( - () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); final PendingIntent alarmPi = getNewMockPendingIntent(); final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class); @@ -1980,9 +1972,7 @@ public class AlarmManagerServiceTest { alarmPi, null, null, null, alarmClock); // Correct permission checks are invoked. - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + verify(mService).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); @@ -1996,6 +1986,22 @@ public class AlarmManagerServiceTest { assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type); } + private void mockExactAlarmPermissionGrant(boolean declared, boolean denyList, int mode) { + String[] requesters = declared ? new String[]{TEST_CALLING_PACKAGE} : EmptyArray.STRING; + when(mPermissionManagerInternal.getAppOpPermissionPackages(SCHEDULE_EXACT_ALARM)) + .thenReturn(requesters); + mService.refreshExactAlarmCandidates(); + + if (denyList) { + setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, TEST_CALLING_PACKAGE); + } else { + setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, ""); + } + + when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, + TEST_CALLING_PACKAGE)).thenReturn(mode); + } + @Test public void alarmClockBinderCallWithoutPermission() throws RemoteException { setDeviceConfigBoolean(KEY_CRASH_NON_CLOCK_APPS, true); @@ -2003,10 +2009,7 @@ public class AlarmManagerServiceTest { () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), anyString(), any(UserHandle.class))); - doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2018,9 +2021,6 @@ public class AlarmManagerServiceTest { } catch (SecurityException se) { // Expected. } - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); } @@ -2030,14 +2030,12 @@ public class AlarmManagerServiceTest { () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), anyString(), any(UserHandle.class))); - // Permission check is granted by default by the mock. + mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0, alarmPi, null, null, null, null); - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + verify(mService).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); @@ -2057,19 +2055,13 @@ public class AlarmManagerServiceTest { () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), anyString(), any(UserHandle.class))); // If permission is denied, only then allowlist will be checked. - doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0, alarmPi, null, null, null, null); - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID)); verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), @@ -2084,14 +2076,11 @@ public class AlarmManagerServiceTest { () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), anyString(), any(UserHandle.class))); - // Permission check is granted by default by the mock. + mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); @@ -2111,19 +2100,13 @@ public class AlarmManagerServiceTest { () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), anyString(), any(UserHandle.class))); // If permission is denied, only then allowlist will be checked. - doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); final PendingIntent alarmPi = getNewMockPendingIntent(); mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID)); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); @@ -2145,10 +2128,7 @@ public class AlarmManagerServiceTest { () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), anyString(), any(UserHandle.class))); - doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false); final PendingIntent alarmPi = getNewMockPendingIntent(); @@ -2166,9 +2146,6 @@ public class AlarmManagerServiceTest { } catch (SecurityException se) { // Expected. } - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), times(2)); verify(mDeviceIdleInternal, times(2)).isAppOnWhitelist(anyInt()); } @@ -2184,9 +2161,7 @@ public class AlarmManagerServiceTest { mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); - verify(() -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE)), never()); + verify(mService, never()).hasScheduleExactAlarmInternal(anyString(), anyInt()); verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); @@ -2205,10 +2180,7 @@ public class AlarmManagerServiceTest { () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), anyString(), any(UserHandle.class))); - doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); when(mAppStateTracker.isUidPowerSaveUserExempt(TEST_CALLING_UID)).thenReturn(true); @@ -2355,10 +2327,7 @@ public class AlarmManagerServiceTest { @Test public void opScheduleExactAlarmRevoked() throws Exception { - doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( - () -> PermissionChecker.checkPermissionForPreflight(eq(mMockContext), - eq(Manifest.permission.SCHEDULE_EXACT_ALARM), anyInt(), - eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE))); + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE); assertAndHandleMessageSync(REMOVE_EXACT_ALARMS); verify(mService).removeExactAlarmsOnPermissionRevokedLocked(TEST_CALLING_UID, @@ -2456,6 +2425,73 @@ public class AlarmManagerServiceTest { assertEquals(LazyAlarmStore.TAG, mService.mAlarmStore.getName()); } + private void registerAppIds(String[] packages, Integer[] ids) { + assertEquals(packages.length, ids.length); + + when(mPackageManagerInternal.getPackageUid(anyString(), anyInt(), anyInt())).thenAnswer( + invocation -> { + final String pkg = invocation.getArgument(0); + final int index = ArrayUtils.indexOf(packages, pkg); + if (index < 0) { + return index; + } + final int userId = invocation.getArgument(2); + return UserHandle.getUid(userId, ids[index]); + }); + } + + @Test + public void refreshExactAlarmCandidatesOnPackageAdded() { + final String[] exactAlarmRequesters = new String[]{"p11", "p2", "p9"}; + final Integer[] appIds = new Integer[]{11, 2, 9}; + registerAppIds(exactAlarmRequesters, appIds); + + when(mPermissionManagerInternal.getAppOpPermissionPackages( + SCHEDULE_EXACT_ALARM)).thenReturn(exactAlarmRequesters); + + final Intent packageAdded = new Intent(Intent.ACTION_PACKAGE_ADDED) + .setPackage(TEST_CALLING_PACKAGE); + mPackageChangesReceiver.onReceive(mMockContext, packageAdded); + + assertAndHandleMessageSync(REFRESH_EXACT_ALARM_CANDIDATES); + assertEquals(new ArraySet<>(appIds), mService.mExactAlarmCandidates); + } + + @Test + public void refreshExactAlarmCandidatesOnPackageReplaced() { + final String[] exactAlarmRequesters = new String[]{"p15", "p21", "p3"}; + final Integer[] appIds = new Integer[]{15, 21, 3}; + registerAppIds(exactAlarmRequesters, appIds); + + when(mPermissionManagerInternal.getAppOpPermissionPackages( + SCHEDULE_EXACT_ALARM)).thenReturn(exactAlarmRequesters); + + final Intent packageAdded = new Intent(Intent.ACTION_PACKAGE_ADDED) + .setPackage(TEST_CALLING_PACKAGE) + .putExtra(Intent.EXTRA_REPLACING, true); + mPackageChangesReceiver.onReceive(mMockContext, packageAdded); + + assertAndHandleMessageSync(REFRESH_EXACT_ALARM_CANDIDATES); + assertEquals(new ArraySet<>(appIds), mService.mExactAlarmCandidates); + } + + @Test + public void refreshExactAlarmCandidatesOnPackageRemoved() { + final String[] exactAlarmRequesters = new String[]{"p99", "p1", "p19"}; + final Integer[] appIds = new Integer[]{99, 1, 19}; + registerAppIds(exactAlarmRequesters, appIds); + + when(mPermissionManagerInternal.getAppOpPermissionPackages( + SCHEDULE_EXACT_ALARM)).thenReturn(exactAlarmRequesters); + + final Intent packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED) + .setPackage(TEST_CALLING_PACKAGE); + mPackageChangesReceiver.onReceive(mMockContext, packageRemoved); + + assertAndHandleMessageSync(REFRESH_EXACT_ALARM_CANDIDATES); + assertEquals(new ArraySet<>(appIds), mService.mExactAlarmCandidates); + } + @After public void tearDown() { if (mMockingSession != null) { diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index ffbcc45107a1..68cb8f94aa0a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -295,11 +295,11 @@ public class ConnectivityControllerTest { doNothing().when(mConnManager).registerNetworkCallback(any(), callbackCaptor.capture()); final ArgumentCaptor<NetworkCallback> redCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class); - doNothing().when(mConnManager).registerDefaultNetworkCallbackAsUid( + doNothing().when(mConnManager).registerDefaultNetworkCallbackForUid( eq(UID_RED), redCallbackCaptor.capture(), any()); final ArgumentCaptor<NetworkCallback> blueCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class); - doNothing().when(mConnManager).registerDefaultNetworkCallbackAsUid( + doNothing().when(mConnManager).registerDefaultNetworkCallbackForUid( eq(UID_BLUE), blueCallbackCaptor.capture(), any()); final ConnectivityController controller = new ConnectivityController(mService); @@ -600,11 +600,11 @@ public class ConnectivityControllerTest { doNothing().when(mConnManager).registerNetworkCallback(any(), callback.capture()); final ArgumentCaptor<NetworkCallback> redCallback = ArgumentCaptor.forClass(NetworkCallback.class); - doNothing().when(mConnManager).registerDefaultNetworkCallbackAsUid( + doNothing().when(mConnManager).registerDefaultNetworkCallbackForUid( eq(UID_RED), redCallback.capture(), any()); final ArgumentCaptor<NetworkCallback> blueCallback = ArgumentCaptor.forClass(NetworkCallback.class); - doNothing().when(mConnManager).registerDefaultNetworkCallbackAsUid( + doNothing().when(mConnManager).registerDefaultNetworkCallbackForUid( eq(UID_BLUE), blueCallback.capture(), any()); final JobStatus networked = createJobStatus(createJob() diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerUtilsTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerUtilsTest.java new file mode 100644 index 000000000000..96103e36ae92 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerUtilsTest.java @@ -0,0 +1,130 @@ +/* + * 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.am; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +@SmallTest +public class ActivityManagerUtilsTest { + @Test + public void getAndroidIdHash() { + // getAndroidIdHash() essentially returns a random a value. Just make sure it's + // non-negative. + assertThat(ActivityManagerUtils.getAndroidIdHash()).isAtLeast(0); + } + + @Test + public void getUnsignedHashCached() { + assertThat(ActivityManagerUtils.getUnsignedHashCached("x")).isEqualTo( + ActivityManagerUtils.getUnsignedHashCached("x")); + + assertThat(ActivityManagerUtils.getUnsignedHashCached("x")).isNotEqualTo( + ActivityManagerUtils.getUnsignedHashCached("y")); + } + + @Test + public void shouldSamplePackage_sampleNone() { + final int numTests = 100000; + for (int i = 0; i < numTests; i++) { + assertThat(ActivityManagerUtils.shouldSamplePackageForAtom("" + i, 0)) + .isFalse(); + } + } + + @Test + public void shouldSamplePackage_sampleAll() { + final int numTests = 100000; + + for (int i = 0; i < numTests; i++) { + assertThat(ActivityManagerUtils.shouldSamplePackageForAtom("" + i, 1)) + .isTrue(); + } + } + + /** + * Make sure, with the same android ID, an expected rate of the packages are selected. + */ + @Test + public void shouldSamplePackage_sampleSome_fixedAndroidId() { + checkShouldSamplePackage_fixedAndroidId(0.1f); + checkShouldSamplePackage_fixedAndroidId(0.5f); + checkShouldSamplePackage_fixedAndroidId(0.9f); + } + + /** + * Make sure, the same package is selected on an expected rate of the devices. + */ + @Test + public void shouldSamplePackage_sampleSome_fixedPackage() { + checkShouldSamplePackage_fixedPackage(0.1f); + checkShouldSamplePackage_fixedPackage(0.5f); + checkShouldSamplePackage_fixedPackage(0.9f); + } + + private void checkShouldSamplePackage_fixedPackage(float sampleRate) { + checkShouldSamplePackage(sampleRate, sampleRate, true, false); + } + + private void checkShouldSamplePackage_fixedAndroidId(float sampleRate) { + checkShouldSamplePackage(sampleRate, sampleRate, false, true); + } + + @Test + public void testSheckShouldSamplePackage() { + // Just make sure checkShouldSamplePackage is actually working... + try { + checkShouldSamplePackage(0.3f, 0.6f, false, true); + fail(); + } catch (AssertionError expected) { + } + try { + checkShouldSamplePackage(0.6f, 0.3f, true, false); + fail(); + } catch (AssertionError expected) { + } + } + + private void checkShouldSamplePackage(float inputSampleRate, float expectedRate, + boolean fixedPackage, boolean fixedAndroidId) { + final int numTests = 100000; + + try { + int numSampled = 0; + for (int i = 0; i < numTests; i++) { + final String pkg = fixedPackage ? "fixed-package" : "" + i; + ActivityManagerUtils.injectAndroidIdForTest( + fixedAndroidId ? "fixed-android-id" : "" + i); + + if (ActivityManagerUtils.shouldSamplePackageForAtom(pkg, inputSampleRate)) { + numSampled++; + } + assertThat(ActivityManagerUtils.getUnsignedHashCached(pkg)).isEqualTo( + ActivityManagerUtils.getUnsignedHashCached(pkg)); + } + final double actualSampleRate = ((double) numSampled) / numTests; + + assertThat(actualSampleRate).isWithin(0.05).of(expectedRate); + } finally { + ActivityManagerUtils.injectAndroidIdForTest(null); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index c5ed20afacec..4d1f241787a7 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -37,6 +37,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; import androidx.annotation.NonNull; import androidx.test.InstrumentationRegistry; @@ -47,6 +48,7 @@ import com.android.server.biometrics.nano.BiometricsProto; import com.android.server.biometrics.sensors.BiometricScheduler.Operation; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -63,10 +65,12 @@ public class BiometricSchedulerTest { private IBinder mToken; @Mock - private Context mContext; - @Mock private IBiometricService mBiometricService; + @Rule + public final TestableContext mContext = + new TestableContext(InstrumentationRegistry.getContext(), null); + @Before public void setUp() { MockitoAnnotations.initMocks(this); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index 2fcc021575b1..2e004683acdb 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -287,6 +287,12 @@ public class DpmMockContext extends MockContext { } @Override + public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions, + Bundle options) { + spiedContext.sendBroadcastMultiplePermissions(intent, receiverPermissions, options); + } + + @Override public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user, String[] receiverPermissions) { spiedContext.sendBroadcastAsUserMultiplePermissions(intent, user, receiverPermissions); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index 7cd60284627f..f156779034ab 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -93,9 +93,10 @@ public class DisplayManagerServiceTest { private static final long SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS = 10; private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display"; private static final String PACKAGE_NAME = "com.android.frameworks.servicestests"; - private static final long ALL_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED + private static final long STANDARD_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED; + @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule(); @@ -764,7 +765,8 @@ public class DisplayManagerServiceTest { // register display listener callback FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(); - displayManagerBinderService.registerCallbackWithEventMask(callback, ALL_DISPLAY_EVENTS); + displayManagerBinderService.registerCallbackWithEventMask( + callback, STANDARD_DISPLAY_EVENTS); waitForIdleHandler(handler); @@ -793,7 +795,7 @@ public class DisplayManagerServiceTest { // register display listener callback FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(); - long allEventsExceptDisplayAdded = ALL_DISPLAY_EVENTS + long allEventsExceptDisplayAdded = STANDARD_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_ADDED; displayManagerBinderService.registerCallbackWithEventMask(callback, allEventsExceptDisplayAdded); @@ -862,7 +864,7 @@ public class DisplayManagerServiceTest { waitForIdleHandler(handler); FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(); - long allEventsExceptDisplayRemoved = ALL_DISPLAY_EVENTS + long allEventsExceptDisplayRemoved = STANDARD_DISPLAY_EVENTS & ~DisplayManager.EVENT_FLAG_DISPLAY_REMOVED; displayManagerBinderService.registerCallbackWithEventMask(callback, allEventsExceptDisplayRemoved); @@ -1032,7 +1034,8 @@ public class DisplayManagerServiceTest { // register display listener callback FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(displayId); - displayManagerBinderService.registerCallbackWithEventMask(callback, ALL_DISPLAY_EVENTS); + displayManagerBinderService.registerCallbackWithEventMask( + callback, STANDARD_DISPLAY_EVENTS); return callback; } diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java new file mode 100644 index 000000000000..88a21b4a8fa8 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java @@ -0,0 +1,275 @@ +/* + * 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.display; + +import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; +import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT; + +import static org.junit.Assert.assertEquals; + +import android.os.Handler; +import android.os.Message; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData; +import com.android.server.testutils.OffsettableClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class HighBrightnessModeControllerTest { + + private static final int MINIMUM_LUX = 100; + private static final float TRANSITION_POINT = 0.763f; + private static final long TIME_WINDOW_MILLIS = 55 * 1000; + private static final long TIME_ALLOWED_IN_WINDOW_MILLIS = 12 * 1000; + private static final long TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS = 5 * 1000; + + private static final float DEFAULT_MIN = 0.01f; + private static final float DEFAULT_MAX = 0.80f; + + private static final float EPSILON = 0.000001f; + + private OffsettableClock mClock; + private TestLooper mTestLooper; + private Handler mHandler; + + private static final HighBrightnessModeData DEFAULT_HBM_DATA = + new HighBrightnessModeData(MINIMUM_LUX, TRANSITION_POINT, TIME_WINDOW_MILLIS, + TIME_ALLOWED_IN_WINDOW_MILLIS, TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS); + + @Before + public void setUp() { + mClock = new OffsettableClock.Stopped(); + mTestLooper = new TestLooper(mClock::now); + mHandler = new Handler(mTestLooper.getLooper(), new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + return true; + } + }); + } + + ///////////////// + // Test Methods + ///////////////// + + @Test + public void testNoHbmData() { + final HighBrightnessModeController hbmc = new HighBrightnessModeController( + mClock::now, mHandler, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}); + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF); + } + + @Test + public void testNoHbmData_Enabled() { + final HighBrightnessModeController hbmc = new HighBrightnessModeController( + mClock::now, mHandler, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}); + hbmc.setAutoBrightnessEnabled(true); + hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF); + } + + @Test + public void testOffByDefault() { + final HighBrightnessModeController hbmc = createDefaultHbm(); + + assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); + } + + @Test + public void testAutoBrightnessEnabled_NoLux() { + final HighBrightnessModeController hbmc = createDefaultHbm(); + + hbmc.setAutoBrightnessEnabled(true); + assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); + } + + @Test + public void testAutoBrightnessEnabled_LowLux() { + final HighBrightnessModeController hbmc = createDefaultHbm(); + + hbmc.setAutoBrightnessEnabled(true); + hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range + assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); + } + + @Test + public void testAutoBrightnessEnabled_HighLux() { + final HighBrightnessModeController hbmc = createDefaultHbm(); + + hbmc.setAutoBrightnessEnabled(true); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); + } + + @Test + public void testAutoBrightnessEnabled_HighLux_ThenDisable() { + final HighBrightnessModeController hbmc = createDefaultHbm(); + + hbmc.setAutoBrightnessEnabled(true); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + hbmc.setAutoBrightnessEnabled(false); + + assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); + } + + @Test + public void testWithinHighRange_thenOverTime_thenEarnBackTime() { + final HighBrightnessModeController hbmc = createDefaultHbm(); + + hbmc.setAutoBrightnessEnabled(true); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f); + + // Verify we are in HBM + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); + + // Use up all the time in the window. + advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS); + + // Verify we are not out of HBM + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); + + // Shift time so that the HBM event is at the beginning of the current window + advanceTime(TIME_WINDOW_MILLIS - TIME_ALLOWED_IN_WINDOW_MILLIS); + // Shift time again so that we are just below the minimum allowable + advanceTime(TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS - 1); + + // Verify we are not out of HBM + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); + + // Advance the necessary millisecond + advanceTime(1); + + // Verify we are allowed HBM again. + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); + } + + @Test + public void testInHBM_ThenLowLux() { + final HighBrightnessModeController hbmc = createDefaultHbm(); + + hbmc.setAutoBrightnessEnabled(true); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f); + + // Verify we are in HBM + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); + + advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2); + + // Verify we are in HBM + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); + + hbmc.onAmbientLuxChange(1); + advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2 + 1); + + // Verify we are out of HBM + assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); + + } + + @Test + public void testInHBM_TestMultipleEvents_DueToAutoBrightness() { + final HighBrightnessModeController hbmc = createDefaultHbm(); + + hbmc.setAutoBrightnessEnabled(true); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + + hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f); + advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2); + + // Verify we are in HBM + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); + + hbmc.onAutoBrightnessChanged(TRANSITION_POINT - 0.01f); + advanceTime(1); + + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); + + hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f); + advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2); + + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); + + advanceTime(2); + + // Now we should be out again. + assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); + } + + @Test + public void testInHBM_TestMultipleEvents_DueToLux() { + final HighBrightnessModeController hbmc = createDefaultHbm(); + + hbmc.setAutoBrightnessEnabled(true); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + + // Go into HBM for half the allowed window + hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f); + advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2); + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); + + // Move lux below threshold (ending first event); + hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); + hbmc.onAutoBrightnessChanged(TRANSITION_POINT); + assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); + + // Move up some amount of time so that there's still time in the window even after a + // second event. + advanceTime((TIME_WINDOW_MILLIS - TIME_ALLOWED_IN_WINDOW_MILLIS) / 2); + assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); + + // Go into HBM for just under the second half of allowed window + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 1); + advanceTime((TIME_ALLOWED_IN_WINDOW_MILLIS / 2) - 1); + + assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT); + + // Now exhaust the time + advanceTime(2); + assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF); + } + + private void assertState(HighBrightnessModeController hbmc, + float brightnessMin, float brightnessMax, int hbmMode) { + assertEquals(brightnessMin, hbmc.getCurrentBrightnessMin(), EPSILON); + assertEquals(brightnessMax, hbmc.getCurrentBrightnessMax(), EPSILON); + assertEquals(hbmMode, hbmc.getHighBrightnessMode()); + } + + // Creates instance with standard initialization values. + private HighBrightnessModeController createDefaultHbm() { + return new HighBrightnessModeController(mClock::now, mHandler, DEFAULT_MIN, DEFAULT_MAX, + DEFAULT_HBM_DATA, () -> {}); + } + + private void advanceTime(long timeMs) { + mClock.fastForward(timeMs); + mTestLooper.dispatchAll(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java index d784a221fbd7..8279624f6b97 100644 --- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java +++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java @@ -27,17 +27,20 @@ import static org.junit.Assert.assertNotEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.PropertyInvalidatedCache; import android.content.Context; +import android.content.res.Resources; +import android.os.Handler; import android.os.Parcel; import android.os.Process; +import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import android.view.Display; import android.view.DisplayAddress; import android.view.DisplayInfo; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -61,9 +64,12 @@ public class LogicalDisplayMapperTest { private DisplayDeviceRepository mDisplayDeviceRepo; private LogicalDisplayMapper mLogicalDisplayMapper; - private Context mContext; + private TestLooper mLooper; + private Handler mHandler; @Mock LogicalDisplayMapper.Listener mListenerMock; + @Mock Context mContextMock; + @Mock Resources mResourcesMock; @Captor ArgumentCaptor<LogicalDisplay> mDisplayCaptor; @@ -73,7 +79,6 @@ public class LogicalDisplayMapperTest { System.setProperty("dexmaker.share_classloader", "true"); MockitoAnnotations.initMocks(this); - mContext = InstrumentationRegistry.getContext(); mDisplayDeviceRepo = new DisplayDeviceRepository( new DisplayManagerService.SyncRoot(), new PersistentDataStore(new PersistentDataStore.Injector() { @@ -94,7 +99,15 @@ public class LogicalDisplayMapperTest { // Disable binder caches in this process. PropertyInvalidatedCache.disableForTestMode(); - mLogicalDisplayMapper = new LogicalDisplayMapper(mDisplayDeviceRepo, mListenerMock); + when(mContextMock.getResources()).thenReturn(mResourcesMock); + when(mResourcesMock.getBoolean( + com.android.internal.R.bool.config_supportsConcurrentInternalDisplays)) + .thenReturn(true); + + mLooper = new TestLooper(); + mHandler = new Handler(mLooper.getLooper()); + mLogicalDisplayMapper = new LogicalDisplayMapper(mContextMock, mDisplayDeviceRepo, + mListenerMock, new DisplayManagerService.SyncRoot(), mHandler); } @@ -299,7 +312,7 @@ public class LogicalDisplayMapperTest { private DisplayDeviceInfo mSentInfo; TestDisplayDevice() { - super(null, null, "test_display_" + sUniqueTestDisplayId++, mContext); + super(null, null, "test_display_" + sUniqueTestDisplayId++, mContextMock); mInfo = new DisplayDeviceInfo(); } diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java index f1e5782f110d..b1582be4532f 100644 --- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java +++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java @@ -24,7 +24,9 @@ import static org.junit.Assert.fail; import android.content.Context; import android.graphics.FontListParser; import android.graphics.fonts.FontManager; +import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontUpdateRequest; +import android.graphics.fonts.SystemFonts; import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.platform.test.annotations.Presubmit; @@ -56,6 +58,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -73,8 +76,7 @@ public final class UpdatableFontDirTest { @Override public String getPostScriptName(File file) throws IOException { String content = FileUtils.readTextFile(file, 100, ""); - String filename = content.split(",")[0]; - return filename.substring(0, filename.length() - 4); + return content.split(",")[2]; } @Override @@ -86,7 +88,6 @@ public final class UpdatableFontDirTest { @Override public long getRevision(File file) throws IOException { String content = FileUtils.readTextFile(file, 100, ""); - android.util.Log.e("Debug", "content: " + content); return Long.parseLong(content.split(",")[1]); } } @@ -135,6 +136,8 @@ public final class UpdatableFontDirTest { private File mConfigFile; private List<File> mPreinstalledFontDirs; private Supplier<Long> mCurrentTimeSupplier = () -> CURRENT_TIME; + private Function<Map<String, File>, FontConfig> mConfigSupplier = + (map) -> SystemFonts.getSystemFontConfig(map, 0, 0); @SuppressWarnings("ResultOfMethodCallIgnored") @Before @@ -168,16 +171,16 @@ public final class UpdatableFontDirTest { config.lastModifiedMillis = expectedModifiedDate; writeConfig(config, mConfigFile); UpdatableFontDir dirForPreparation = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dirForPreparation.loadFontFileMap(); assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis()) .isEqualTo(expectedModifiedDate); dirForPreparation.update(Arrays.asList( - newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), - newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE), - newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE), - newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE), + newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), + newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE), + newFontUpdateRequest("foo.ttf,3,foo", GOOD_SIGNATURE), + newFontUpdateRequest("bar.ttf,4,bar", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" @@ -191,13 +194,13 @@ public final class UpdatableFontDirTest { .isNotEqualTo(expectedModifiedDate); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - assertThat(dir.getFontFileMap()).containsKey("foo.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(3); - assertThat(dir.getFontFileMap()).containsKey("bar.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(4); + assertThat(dir.getPostScriptMap()).containsKey("foo"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(3); + assertThat(dir.getPostScriptMap()).containsKey("bar"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4); // Outdated font dir should be deleted. assertThat(mUpdatableFontFilesDir.list()).hasLength(2); assertNamedFamilyExists(dir.getSystemFontConfig(), "foobar"); @@ -205,9 +208,9 @@ public final class UpdatableFontDirTest { FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar"); assertThat(foobar.getFontList()).hasSize(2); assertThat(foobar.getFontList().get(0).getFile()) - .isEqualTo(dir.getFontFileMap().get("foo.ttf")); + .isEqualTo(dir.getPostScriptMap().get("foo")); assertThat(foobar.getFontList().get(1).getFile()) - .isEqualTo(dir.getFontFileMap().get("bar.ttf")); + .isEqualTo(dir.getPostScriptMap().get("bar")); } @Test @@ -215,10 +218,10 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - assertThat(dir.getFontFileMap()).isEmpty(); + assertThat(dir.getPostScriptMap()).isEmpty(); assertThat(dir.getFontFamilyMap()).isEmpty(); } @@ -227,14 +230,14 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dirForPreparation = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( - newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), - newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE), - newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE), - newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE), + newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), + newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE), + newFontUpdateRequest("foo.ttf,3,foo", GOOD_SIGNATURE), + newFontUpdateRequest("bar.ttf,4,bar", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" @@ -243,12 +246,12 @@ public final class UpdatableFontDirTest { assertThat(mUpdatableFontFilesDir.list()).hasLength(4); fakeFsverityUtil.remove( - dirForPreparation.getFontFileMap().get("foo.ttf").getAbsolutePath()); + dirForPreparation.getPostScriptMap().get("foo").getAbsolutePath()); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - assertThat(dir.getFontFileMap()).isEmpty(); + assertThat(dir.getPostScriptMap()).isEmpty(); // All font dirs (including dir for "bar.ttf") should be deleted. assertThat(mUpdatableFontFilesDir.list()).hasLength(0); assertThat(dir.getFontFamilyMap()).isEmpty(); @@ -259,14 +262,14 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dirForPreparation = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( - newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), - newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE), - newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE), - newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE), + newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), + newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE), + newFontUpdateRequest("foo.ttf,3,foo", GOOD_SIGNATURE), + newFontUpdateRequest("bar.ttf,4,bar", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" @@ -275,13 +278,13 @@ public final class UpdatableFontDirTest { assertThat(mUpdatableFontFilesDir.list()).hasLength(4); // Overwrite "foo.ttf" with wrong contents. - FileUtils.stringToFile(dirForPreparation.getFontFileMap().get("foo.ttf"), "bar,4"); + FileUtils.stringToFile(dirForPreparation.getPostScriptMap().get("foo"), "bar,4"); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - assertThat(dir.getFontFileMap()).isEmpty(); + assertThat(dir.getPostScriptMap()).isEmpty(); // All font dirs (including dir for "bar.ttf") should be deleted. assertThat(mUpdatableFontFilesDir.list()).hasLength(0); assertThat(dir.getFontFamilyMap()).isEmpty(); @@ -291,15 +294,30 @@ public final class UpdatableFontDirTest { public void construct_olderThanPreinstalledFont() throws Exception { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); + Function<Map<String, File>, FontConfig> configSupplier = (map) -> { + FontConfig.Font fooFont = new FontConfig.Font( + new File(mPreinstalledFontDirs.get(0), "foo.ttf"), null, "foo", + new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null, null); + FontConfig.Font barFont = new FontConfig.Font( + new File(mPreinstalledFontDirs.get(1), "bar.ttf"), null, "bar", + new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null, null); + + FontConfig.FontFamily family = new FontConfig.FontFamily( + Arrays.asList(fooFont, barFont), "sans-serif", null, + FontConfig.FontFamily.VARIANT_DEFAULT); + return new FontConfig(Collections.singletonList(family), + Collections.emptyList(), 0, 1); + }; + UpdatableFontDir dirForPreparation = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, configSupplier); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( - newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), - newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE), - newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE), - newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE), + newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), + newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE), + newFontUpdateRequest("foo.ttf,3,foo", GOOD_SIGNATURE), + newFontUpdateRequest("bar.ttf,4,bar", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" @@ -308,18 +326,18 @@ public final class UpdatableFontDirTest { assertThat(mUpdatableFontFilesDir.list()).hasLength(4); // Add preinstalled fonts. - FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "foo.ttf"), "foo,5"); - FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "bar.ttf"), "bar,1"); - FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,2"); + FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "foo.ttf"), "foo,5,foo"); + FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,1,bar"); + FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,2,bar"); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, configSupplier); dir.loadFontFileMap(); // For foo.ttf, preinstalled font (revision 5) should be used. - assertThat(dir.getFontFileMap()).doesNotContainKey("foo.ttf"); + assertThat(dir.getPostScriptMap()).doesNotContainKey("foo"); // For bar.ttf, updated font (revision 4) should be used. - assertThat(dir.getFontFileMap()).containsKey("bar.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(4); + assertThat(dir.getPostScriptMap()).containsKey("bar"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4); // Outdated font dir should be deleted. // We don't delete bar.ttf in this case, because it's normal that OTA updates preinstalled // fonts. @@ -333,10 +351,10 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - new File("/dev/null"), mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + new File("/dev/null"), mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - assertThat(dir.getFontFileMap()).isEmpty(); + assertThat(dir.getPostScriptMap()).isEmpty(); assertThat(dir.getFontFamilyMap()).isEmpty(); } @@ -345,18 +363,18 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dirForPreparation = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( - newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), + newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + "</family>"))); try { dirForPreparation.update(Arrays.asList( - newFontUpdateRequest("foo.ttf,2", GOOD_SIGNATURE), - newFontUpdateRequest("bar.ttf,2", "Invalid signature"), + newFontUpdateRequest("foo.ttf,2,foo", GOOD_SIGNATURE), + newFontUpdateRequest("bar.ttf,2,bar", "Invalid signature"), newAddFontFamilyRequest("<family name='foobar'>" + " <font>foo.ttf</font>" + " <font>bar.ttf</font>" @@ -367,17 +385,17 @@ public final class UpdatableFontDirTest { } UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); // The state should be rolled back as a whole if one of the update requests fail. - assertThat(dir.getFontFileMap()).containsKey("foo.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1); + assertThat(dir.getPostScriptMap()).containsKey("foo"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); assertThat(dir.getFontFamilyMap()).containsKey("foobar"); FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar"); assertThat(foobar.getFontList()).hasSize(1); assertThat(foobar.getFontList().get(0).getFile()) - .isEqualTo(dir.getFontFileMap().get("foo.ttf")); + .isEqualTo(dir.getPostScriptMap().get("foo")); } @Test @@ -385,14 +403,15 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); - assertThat(dir.getFontFileMap()).containsKey("test.ttf"); - File fontFile = dir.getFontFileMap().get("test.ttf"); + dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", + GOOD_SIGNATURE))); + assertThat(dir.getPostScriptMap()).containsKey("test"); + File fontFile = dir.getPostScriptMap().get("test"); dir.loadFontFileMap(); - assertThat(dir.getFontFileMap().get("test.ttf")).isEqualTo(fontFile); + assertThat(dir.getPostScriptMap().get("test")).isEqualTo(fontFile); } @Test @@ -400,14 +419,15 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); - assertThat(dir.getFontFileMap()).containsKey("test.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1); - File fontFile = dir.getFontFileMap().get("test.ttf"); + dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", + GOOD_SIGNATURE))); + assertThat(dir.getPostScriptMap()).containsKey("test"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1); + File fontFile = dir.getPostScriptMap().get("test"); assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644); File fontDir = fontFile.getParentFile(); assertThat(Os.stat(fontDir.getAbsolutePath()).st_mode & 0777).isEqualTo(0711); @@ -418,22 +438,58 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); - Map<String, File> mapBeforeUpgrade = dir.getFontFileMap(); - dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE))); - assertThat(dir.getFontFileMap()).containsKey("test.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2); - assertThat(mapBeforeUpgrade).containsKey("test.ttf"); + dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", + GOOD_SIGNATURE))); + Map<String, File> mapBeforeUpgrade = dir.getPostScriptMap(); + dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2,test", + GOOD_SIGNATURE))); + assertThat(dir.getPostScriptMap()).containsKey("test"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2); + assertThat(mapBeforeUpgrade).containsKey("test"); assertWithMessage("Older fonts should not be deleted until next loadFontFileMap") - .that(parser.getRevision(mapBeforeUpgrade.get("test.ttf"))).isEqualTo(1); + .that(parser.getRevision(mapBeforeUpgrade.get("test"))).isEqualTo(1); // Check that updatedFontDirs is pruned. assertWithMessage("config.updatedFontDirs should only list latest active dirs") .that(readConfig(mConfigFile).updatedFontDirs) - .containsExactly(dir.getFontFileMap().get("test.ttf").getParentFile().getName()); + .containsExactly(dir.getPostScriptMap().get("test").getParentFile().getName()); + } + + @Test + public void installFontFile_systemFontHasPSNameDifferentFromFileName() throws Exception { + FakeFontFileParser parser = new FakeFontFileParser(); + FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); + + // Setup the environment that the system installed font file named "foo.ttf" has PostScript + // name "bar". + File file = new File(mPreinstalledFontDirs.get(0), "foo.ttf"); + FileUtils.stringToFile(file, "foo.ttf,1,bar"); + UpdatableFontDir dir = new UpdatableFontDir( + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, (map) -> { + FontConfig.Font font = new FontConfig.Font( + file, null, "bar", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), + 0, null, null); + FontConfig.FontFamily family = new FontConfig.FontFamily( + Collections.singletonList(font), "sans-serif", null, + FontConfig.FontFamily.VARIANT_DEFAULT); + return new FontConfig(Collections.singletonList(family), + Collections.emptyList(), 0, 1); + }); + dir.loadFontFileMap(); + + dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2,bar", + GOOD_SIGNATURE))); + assertThat(dir.getPostScriptMap()).containsKey("bar"); + assertThat(dir.getPostScriptMap().size()).isEqualTo(1); + assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2); + File fontFile = dir.getPostScriptMap().get("bar"); + assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644); + File fontDir = fontFile.getParentFile(); + assertThat(Os.stat(fontDir.getAbsolutePath()).st_mode & 0777).isEqualTo(0711); } @Test @@ -441,14 +497,16 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); - dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); - assertThat(dir.getFontFileMap()).containsKey("test.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1); + dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", + GOOD_SIGNATURE))); + dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", + GOOD_SIGNATURE))); + assertThat(dir.getPostScriptMap()).containsKey("test"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1); } @Test @@ -456,25 +514,26 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE))); + dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2,test", + GOOD_SIGNATURE))); try { - dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", + dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING); } - assertThat(dir.getFontFileMap()).containsKey("test.ttf"); + assertThat(dir.getPostScriptMap()).containsKey("test"); assertWithMessage("Font should not be downgraded to an older revision") - .that(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2); + .that(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2); // Check that updatedFontDirs is not updated. assertWithMessage("config.updatedFontDirs should only list latest active dirs") .that(readConfig(mConfigFile).updatedFontDirs) - .containsExactly(dir.getFontFileMap().get("test.ttf").getParentFile().getName()); + .containsExactly(dir.getPostScriptMap().get("test").getParentFile().getName()); } @Test @@ -482,16 +541,18 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE))); - dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE))); - assertThat(dir.getFontFileMap()).containsKey("foo.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1); - assertThat(dir.getFontFileMap()).containsKey("bar.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(2); + dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1,foo", + GOOD_SIGNATURE))); + dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2,bar", + GOOD_SIGNATURE))); + assertThat(dir.getPostScriptMap()).containsKey("foo"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); + assertThat(dir.getPostScriptMap()).containsKey("bar"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2); } @Test @@ -499,17 +560,17 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); dir.update(Arrays.asList( - newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE), - newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE))); - assertThat(dir.getFontFileMap()).containsKey("foo.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1); - assertThat(dir.getFontFileMap()).containsKey("bar.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(2); + newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), + newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE))); + assertThat(dir.getPostScriptMap()).containsKey("foo"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); + assertThat(dir.getPostScriptMap()).containsKey("bar"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2); } @Test @@ -517,70 +578,83 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); try { dir.update( - Collections.singletonList(newFontUpdateRequest("test.ttf,1", + Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", "Invalid signature"))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) .isEqualTo(FontManager.RESULT_ERROR_VERIFICATION_FAILURE); } - assertThat(dir.getFontFileMap()).isEmpty(); + assertThat(dir.getPostScriptMap()).isEmpty(); } @Test public void installFontFile_preinstalled_upgrade() throws Exception { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); - FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1"); + FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), + "test.ttf,1,test"); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE))); - assertThat(dir.getFontFileMap()).containsKey("test.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2); + dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2,test", + GOOD_SIGNATURE))); + assertThat(dir.getPostScriptMap()).containsKey("test"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2); } @Test public void installFontFile_preinstalled_sameVersion() throws Exception { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); - FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1"); + FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), + "test.ttf,1,test"); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE))); - assertThat(dir.getFontFileMap()).containsKey("test.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1); + dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", + GOOD_SIGNATURE))); + assertThat(dir.getPostScriptMap()).containsKey("test"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1); } @Test public void installFontFile_preinstalled_downgrade() throws Exception { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); - FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,2"); + File file = new File(mPreinstalledFontDirs.get(0), "test.ttf"); + FileUtils.stringToFile(file, "test.ttf,2,test"); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, (map) -> { + FontConfig.Font font = new FontConfig.Font( + file, null, "test", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null, + null); + FontConfig.FontFamily family = new FontConfig.FontFamily( + Collections.singletonList(font), "sans-serif", null, + FontConfig.FontFamily.VARIANT_DEFAULT); + return new FontConfig(Collections.singletonList(family), Collections.emptyList(), 0, 1); + }); dir.loadFontFileMap(); try { - dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", + dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING); } - assertThat(dir.getFontFileMap()).isEmpty(); + assertThat(dir.getPostScriptMap()).isEmpty(); } @Test @@ -588,7 +662,8 @@ public final class UpdatableFontDirTest { long expectedModifiedDate = 1234567890; FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); - FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1"); + FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), + "test.ttf,1,test"); File readonlyDir = new File(mCacheDir, "readonly"); assertThat(readonlyDir.mkdir()).isTrue(); @@ -601,13 +676,13 @@ public final class UpdatableFontDirTest { assertThat(readonlyDir.setWritable(false, false)).isTrue(); try { UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - readonlyFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + readonlyFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); try { dir.update( - Collections.singletonList(newFontUpdateRequest("test.ttf,2", + Collections.singletonList(newFontUpdateRequest("test.ttf,2,test", GOOD_SIGNATURE))); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) @@ -615,7 +690,7 @@ public final class UpdatableFontDirTest { } assertThat(dir.getSystemFontConfig().getLastModifiedTimeMillis()) .isEqualTo(expectedModifiedDate); - assertThat(dir.getFontFileMap()).isEmpty(); + assertThat(dir.getPostScriptMap()).isEmpty(); } finally { assertThat(readonlyDir.setWritable(true, true)).isTrue(); } @@ -625,7 +700,7 @@ public final class UpdatableFontDirTest { public void installFontFile_failedToParsePostScript() throws Exception { FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, + mUpdatableFontFilesDir, new UpdatableFontDir.FontFileParser() { @Override @@ -642,25 +717,25 @@ public final class UpdatableFontDirTest { public long getRevision(File file) throws IOException { return 0; } - }, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier); + }, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); try { - dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", + dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) .isEqualTo(FontManager.RESULT_ERROR_INVALID_FONT_NAME); } - assertThat(dir.getFontFileMap()).isEmpty(); + assertThat(dir.getPostScriptMap()).isEmpty(); } @Test public void installFontFile_failedToParsePostScriptName_invalidFont() throws Exception { FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, + mUpdatableFontFilesDir, new UpdatableFontDir.FontFileParser() { @Override public String getPostScriptName(File file) throws IOException { @@ -676,18 +751,18 @@ public final class UpdatableFontDirTest { public long getRevision(File file) throws IOException { return 0; } - }, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier); + }, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); try { - dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", + dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) .isEqualTo(FontManager.RESULT_ERROR_INVALID_FONT_FILE); } - assertThat(dir.getFontFileMap()).isEmpty(); + assertThat(dir.getPostScriptMap()).isEmpty(); } @Test @@ -712,19 +787,19 @@ public final class UpdatableFontDirTest { }; FakeFontFileParser parser = new FakeFontFileParser(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); try { - dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", + dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE))); fail("Expect SystemFontException"); } catch (FontManagerService.SystemFontException e) { assertThat(e.getErrorCode()) .isEqualTo(FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE); } - assertThat(dir.getFontFileMap()).isEmpty(); + assertThat(dir.getPostScriptMap()).isEmpty(); } @Test @@ -732,22 +807,23 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); - dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE))); + dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1,foo", + GOOD_SIGNATURE))); try { dir.update(Arrays.asList( - newFontUpdateRequest("foo.ttf,2", GOOD_SIGNATURE), - newFontUpdateRequest("bar.ttf,2", "Invalid signature"))); + newFontUpdateRequest("foo.ttf,2,foo", GOOD_SIGNATURE), + newFontUpdateRequest("bar.ttf,2,bar", "Invalid signature"))); fail("Batch update with invalid signature should fail"); } catch (FontManagerService.SystemFontException e) { // Expected } // The state should be rolled back as a whole if one of the update requests fail. - assertThat(dir.getFontFileMap()).containsKey("foo.ttf"); - assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1); + assertThat(dir.getPostScriptMap()).containsKey("foo"); + assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); } @Test @@ -755,21 +831,21 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); dir.update(Arrays.asList( - newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE), + newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='test'>" + " <font>test.ttf</font>" + "</family>"))); - assertThat(dir.getFontFileMap()).containsKey("test.ttf"); + assertThat(dir.getPostScriptMap()).containsKey("test"); assertThat(dir.getFontFamilyMap()).containsKey("test"); FontConfig.FontFamily test = dir.getFontFamilyMap().get("test"); assertThat(test.getFontList()).hasSize(1); assertThat(test.getFontList().get(0).getFile()) - .isEqualTo(dir.getFontFileMap().get("test.ttf")); + .isEqualTo(dir.getPostScriptMap().get("test")); } @Test @@ -777,13 +853,13 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); try { dir.update(Arrays.asList( - newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE), + newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE), newAddFontFamilyRequest("<family lang='en'>" + " <font>test.ttf</font>" + "</family>"))); @@ -798,8 +874,8 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); try { @@ -818,14 +894,14 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); // We assume we have monospace. assertNamedFamilyExists(dir.getSystemFontConfig(), "monospace"); dir.update(Arrays.asList( - newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE), + newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE), // Updating an existing font family. newAddFontFamilyRequest("<family name='monospace'>" + " <font>test.ttf</font>" @@ -839,7 +915,7 @@ public final class UpdatableFontDirTest { FontConfig.FontFamily monospace = getLastFamily(fontConfig, "monospace"); assertThat(monospace.getFontList()).hasSize(1); assertThat(monospace.getFontList().get(0).getFile()) - .isEqualTo(dir.getFontFileMap().get("test.ttf")); + .isEqualTo(dir.getPostScriptMap().get("test")); assertNamedFamilyExists(fontConfig, "test"); assertThat(getLastFamily(fontConfig, "test").getFontList()) .isEqualTo(monospace.getFontList()); @@ -850,15 +926,15 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, - mConfigFile, mCurrentTimeSupplier); + mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); assertThat(dir.getSystemFontConfig().getFontFamilies()).isNotEmpty(); FontConfig.FontFamily firstFontFamily = dir.getSystemFontConfig().getFontFamilies().get(0); assertThat(firstFontFamily.getName()).isNotEmpty(); dir.update(Arrays.asList( - newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE), + newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE), newAddFontFamilyRequest("<family name='" + firstFontFamily.getName() + "'>" + " <font>test.ttf</font>" + "</family>"))); @@ -868,7 +944,7 @@ public final class UpdatableFontDirTest { FontConfig.FontFamily updated = getLastFamily(fontConfig, firstFontFamily.getName()); assertThat(updated.getFontList()).hasSize(1); assertThat(updated.getFontList().get(0).getFile()) - .isEqualTo(dir.getFontFileMap().get("test.ttf")); + .isEqualTo(dir.getPostScriptMap().get("test")); assertThat(updated).isNotEqualTo(firstFontFamily); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java index ef7b274eeb83..011b8f899753 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java @@ -138,6 +138,7 @@ public class DevicePowerStatusActionTest { mDevicePowerStatusAction = DevicePowerStatusAction.create(mPlaybackDevice, ADDR_TV, mCallbackMock); mTestLooper.dispatchAll(); + mNativeWrapper.clearResultMessages(); } @Test diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index 80da6961cf16..a29a76b438df 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -60,6 +60,7 @@ import java.util.concurrent.TimeUnit; @RunWith(JUnit4.class) /** Tests for {@link HdmiCecLocalDevicePlayback} class. */ public class HdmiCecLocalDevicePlaybackTest { + private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1; private static final int PORT_1 = 1; private static final HdmiDeviceInfo INFO_TV = new HdmiDeviceInfo( @@ -1045,6 +1046,10 @@ public class HdmiCecLocalDevicePlaybackTest { assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue(); // 4. DUT turned off. mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF); + // TODO(b/184939731): remove waiting times once pending actions no longer block <Standby> + mTestLooper.moveTimeForward(TIMEOUT_MS); + mTestLooper.dispatchAll(); + mTestLooper.moveTimeForward(TIMEOUT_MS); mTestLooper.dispatchAll(); HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby( mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST); @@ -1502,6 +1507,7 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void queryDisplayStatus() { + mTestLooper.moveTimeForward(TIMEOUT_MS); mHdmiControlService.queryDisplayStatus(new IHdmiControlCallback.Stub() { @Override public void onComplete(int result) { @@ -1618,6 +1624,12 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void shouldHandleTvPowerKey_CecDisabled() { + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + HdmiCecMessage reportPowerStatusMessage = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_TV, mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_ON); + mNativeWrapper.onCecMessage(reportPowerStatusMessage); + mTestLooper.dispatchAll(); + mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); @@ -1626,6 +1638,12 @@ public class HdmiCecLocalDevicePlaybackTest { @Test public void shouldHandleTvPowerKey_PowerControlModeNone() { + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + HdmiCecMessage reportPowerStatusMessage = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_TV, mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_ON); + mNativeWrapper.onCecMessage(reportPowerStatusMessage); + mTestLooper.dispatchAll(); + mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, HdmiControlManager.POWER_CONTROL_MODE_NONE); @@ -1633,7 +1651,22 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void shouldHandleTvPowerKey_CecNotAvailable() { + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + // TV doesn't report its power status + mTestLooper.moveTimeForward(TIMEOUT_MS); + mTestLooper.dispatchAll(); + assertThat(mHdmiControlService.shouldHandleTvPowerKey()).isFalse(); + } + + @Test public void shouldHandleTvPowerKey_CecEnabled_PowerControlModeTv() { + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + HdmiCecMessage reportPowerStatusMessage = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_TV, mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_ON); + mNativeWrapper.onCecMessage(reportPowerStatusMessage); + mTestLooper.dispatchAll(); + mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 950b8a254007..c7a508a9d626 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -61,6 +61,7 @@ import java.util.ArrayList; @RunWith(JUnit4.class) /** Tests for {@link HdmiCecLocalDeviceTv} class. */ public class HdmiCecLocalDeviceTvTest { + private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1; private HdmiControlService mHdmiControlService; private HdmiCecController mHdmiCecController; @@ -294,6 +295,10 @@ public class HdmiCecLocalDeviceTvTest { HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED); mTestLooper.dispatchAll(); mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF); + // TODO(184939731): remove waiting times once pending actions no longer block <Standby> + mTestLooper.moveTimeForward(TIMEOUT_MS); + mTestLooper.dispatchAll(); + mTestLooper.moveTimeForward(TIMEOUT_MS); mTestLooper.dispatchAll(); HdmiCecMessage standby = HdmiCecMessageBuilder.buildStandby(ADDR_TV, ADDR_BROADCAST); assertThat(mNativeWrapper.getResultMessages()).contains(standby); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java index 5b019207688f..2307a85fe9cd 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java @@ -392,9 +392,9 @@ public class HdmiCecMessageValidatorTest { @Test public void isValid_giveFeatures() { assertMessageValidity("40:A5").isEqualTo(OK); + assertMessageValidity("F0:A5").isEqualTo(OK); assertMessageValidity("4F:A5").isEqualTo(ERROR_DESTINATION); - assertMessageValidity("F0:A5").isEqualTo(ERROR_SOURCE); } @Test diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java index d74bff2837ce..48931739d9cf 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java @@ -142,6 +142,7 @@ public class OneTouchPlayActionTest { mPhysicalAddress = 0x2000; mNativeWrapper.setPhysicalAddress(mPhysicalAddress); mTestLooper.dispatchAll(); + mNativeWrapper.clearResultMessages(); } private OneTouchPlayAction createOneTouchPlayAction(HdmiCecLocalDevicePlayback device, @@ -161,6 +162,7 @@ public class OneTouchPlayActionTest { mLocalDevices.add(playbackDevice); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); + mNativeWrapper.clearResultMessages(); TestActionTimer actionTimer = new TestActionTimer(); TestCallback callback = new TestCallback(); @@ -203,6 +205,7 @@ public class OneTouchPlayActionTest { mLocalDevices.add(playbackDevice); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); + mNativeWrapper.clearResultMessages(); TestActionTimer actionTimer = new TestActionTimer(); TestCallback callback = new TestCallback(); @@ -245,6 +248,7 @@ public class OneTouchPlayActionTest { mLocalDevices.add(playbackDevice); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); + mNativeWrapper.clearResultMessages(); TestActionTimer actionTimer = new TestActionTimer(); TestCallback callback = new TestCallback(); @@ -297,6 +301,7 @@ public class OneTouchPlayActionTest { mLocalDevices.add(playbackDevice); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); + mNativeWrapper.clearResultMessages(); TestActionTimer actionTimer = new TestActionTimer(); TestCallback callback = new TestCallback(); @@ -342,6 +347,7 @@ public class OneTouchPlayActionTest { mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_TV, HdmiControlManager.POWER_STATUS_ON); mTestLooper.dispatchAll(); + mNativeWrapper.clearResultMessages(); TestActionTimer actionTimer = new TestActionTimer(); TestCallback callback = new TestCallback(); @@ -376,6 +382,7 @@ public class OneTouchPlayActionTest { mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_TV, HdmiControlManager.POWER_STATUS_UNKNOWN); mTestLooper.dispatchAll(); + mNativeWrapper.clearResultMessages(); TestActionTimer actionTimer = new TestActionTimer(); TestCallback callback = new TestCallback(); @@ -420,6 +427,7 @@ public class OneTouchPlayActionTest { mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_TV, HdmiControlManager.POWER_STATUS_STANDBY); mTestLooper.dispatchAll(); + mNativeWrapper.clearResultMessages(); TestActionTimer actionTimer = new TestActionTimer(); TestCallback callback = new TestCallback(); diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java index b64810b66542..5234bb725a8f 100644 --- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java @@ -95,6 +95,8 @@ public class RecoverySystemServiceTest { mUncryptUpdateFileWriter = mock(FileWriter.class); mLockSettingsInternal = mock(LockSettingsInternal.class); + doReturn(true).when(mLockSettingsInternal).prepareRebootEscrow(); + doReturn(true).when(mLockSettingsInternal).clearRebootEscrow(); doReturn(LockSettingsInternal.ARM_REBOOT_ERROR_NONE).when(mLockSettingsInternal) .armRebootEscrow(); @@ -254,7 +256,6 @@ public class RecoverySystemServiceTest { + RecoverySystemService.REQUEST_LSKF_TIMESTAMP_PREF_SUFFIX), eq(100_000L)); } - @Test public void requestLskf_success() throws Exception { IntentSender intentSender = mock(IntentSender.class); @@ -299,6 +300,14 @@ public class RecoverySystemServiceTest { } @Test + public void requestLskf_lockSettingsError() throws Exception { + IntentSender intentSender = mock(IntentSender.class); + + doReturn(false).when(mLockSettingsInternal).prepareRebootEscrow(); + assertFalse(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender)); + } + + @Test public void isLskfCaptured_requestedButNotPrepared() throws Exception { IntentSender intentSender = mock(IntentSender.class); assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, intentSender), diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/recoverysystem/TEST_MAPPING new file mode 100644 index 000000000000..4d1e4051032f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/recoverysystem/TEST_MAPPING @@ -0,0 +1,15 @@ +{ + "presubmit": [ + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.recoverysystem." + }, + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } + ] +}
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 916a278a1567..a24691791938 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -59,11 +59,14 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; import static org.mockito.AdditionalMatchers.not; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.app.ActivityManager; @@ -77,6 +80,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.Looper; @@ -92,6 +96,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; @@ -101,6 +106,8 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.io.File; import java.util.ArrayList; @@ -194,6 +201,8 @@ public class AppStandbyControllerTests { } static class MyInjector extends AppStandbyController.Injector { + @Mock + private PackageManagerInternal mPackageManagerInternal; long mElapsedRealtime; boolean mIsAppIdleEnabled = true; boolean mIsCharging; @@ -222,6 +231,7 @@ public class AppStandbyControllerTests { MyInjector(Context context, Looper looper) { super(context, looper); + MockitoAnnotations.initMocks(this); } @Override @@ -269,6 +279,11 @@ public class AppStandbyControllerTests { } @Override + PackageManagerInternal getPackageManagerInternal() { + return mPackageManagerInternal; + } + + @Override void updatePowerWhitelistCache() { } @@ -491,6 +506,37 @@ public class AppStandbyControllerTests { mInjector.mElapsedRealtime, false)); } + @Test + public void testGetIdleUidsForUser() { + final AppStandbyController controllerUnderTest = spy(mController); + + final int userIdForTest = 325; + final int[] uids = new int[]{129, 23, 129, 129, 44, 23, 41, 751}; + final boolean[] idle = new boolean[]{true, true, false, true, false, true, false, true}; + // Based on uids[] and idle[], the only two uids that have all true's in idle[]. + final int[] expectedIdleUids = new int[]{23, 751}; + + final List<ApplicationInfo> installedApps = new ArrayList<>(); + for (int i = 0; i < uids.length; i++) { + final ApplicationInfo ai = mock(ApplicationInfo.class); + ai.uid = uids[i]; + ai.packageName = "example.package.name." + i; + installedApps.add(ai); + when(controllerUnderTest.isAppIdleFiltered(eq(ai.packageName), + eq(UserHandle.getAppId(ai.uid)), eq(userIdForTest), anyLong())) + .thenReturn(idle[i]); + } + when(mInjector.mPackageManagerInternal.getInstalledApplications(anyInt(), eq(userIdForTest), + anyInt())).thenReturn(installedApps); + final int[] returnedIdleUids = controllerUnderTest.getIdleUidsForUser(userIdForTest); + + assertEquals(expectedIdleUids.length, returnedIdleUids.length); + for (final int uid : expectedIdleUids) { + assertTrue("Idle uid: " + uid + " not found in result: " + Arrays.toString( + returnedIdleUids), ArrayUtils.contains(returnedIdleUids, uid)); + } + } + private static class TestParoleListener extends AppIdleStateChangeListener { private boolean mIsParoleOn = false; private CountDownLatch mLatch; diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java index 8c92a4732b17..0e615a6812fe 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java @@ -51,8 +51,6 @@ public class WindowOrientationListenerTest { private InputSensorInfo mMockInputSensorInfo; @Mock private SensorManager mMockSensorManager; - @Mock - private WindowManagerService mMockWindowManagerService; private TestableRotationResolver mFakeRotationResolverInternal; private com.android.server.wm.WindowOrientationListener mWindowOrientationListener; @@ -69,7 +67,7 @@ public class WindowOrientationListenerTest { mFakeRotationResolverInternal = new TestableRotationResolver(); doReturn(mMockSensorManager).when(mMockContext).getSystemService(Context.SENSOR_SERVICE); mWindowOrientationListener = new TestableWindowOrientationListener(mMockContext, - mMockHandler, mMockWindowManagerService); + mMockHandler); mWindowOrientationListener.mRotationResolverService = mFakeRotationResolverInternal; mFakeSensor = new Sensor(mMockInputSensorInfo); @@ -115,9 +113,8 @@ public class WindowOrientationListenerTest { final class TestableWindowOrientationListener extends WindowOrientationListener { - TestableWindowOrientationListener(Context context, Handler handler, - WindowManagerService service) { - super(context, handler, service); + TestableWindowOrientationListener(Context context, Handler handler) { + super(context, handler); this.mOrientationJudge = new OrientationSensorJudge(); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java index 9433bf28e237..6a8f602a8350 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java @@ -22,12 +22,15 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Person; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -72,6 +75,7 @@ public class NotificationComparatorTest extends UiServiceTestCase { private NotificationRecord mRecordMinCallNonInterruptive; private NotificationRecord mRecordMinCall; private NotificationRecord mRecordHighCall; + private NotificationRecord mRecordHighCallStyle; private NotificationRecord mRecordEmail; private NotificationRecord mRecordInlineReply; private NotificationRecord mRecordSms; @@ -90,9 +94,12 @@ public class NotificationComparatorTest extends UiServiceTestCase { int userId = UserHandle.myUserId(); when(mContext.getResources()).thenReturn(getContext().getResources()); + when(mContext.getTheme()).thenReturn(getContext().getTheme()); when(mContext.getContentResolver()).thenReturn(getContext().getContentResolver()); when(mContext.getPackageManager()).thenReturn(mPm); when(mContext.getSystemService(eq(Context.TELECOM_SERVICE))).thenReturn(mTm); + when(mContext.getString(anyInt())).thenCallRealMethod(); + when(mContext.getColor(anyInt())).thenCallRealMethod(); when(mTm.getDefaultDialerPackage()).thenReturn(callPkg); final ApplicationInfo legacy = new ApplicationInfo(); legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1; @@ -137,6 +144,19 @@ public class NotificationComparatorTest extends UiServiceTestCase { new UserHandle(userId), "", 1999), getDefaultChannel()); mRecordHighCall.setSystemImportance(NotificationManager.IMPORTANCE_HIGH); + Notification nHighCallStyle = new Notification.Builder(mContext, TEST_CHANNEL_ID) + .setStyle(Notification.CallStyle.forOngoingCall( + new Person.Builder().setName("caller").build(), + mock(PendingIntent.class) + )) + .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) + .build(); + mRecordHighCallStyle = new NotificationRecord(mContext, new StatusBarNotification(callPkg, + callPkg, 1, "highCallStyle", callUid, callUid, nHighCallStyle, + new UserHandle(userId), "", 2000), getDefaultChannel()); + mRecordHighCallStyle.setSystemImportance(NotificationManager.IMPORTANCE_HIGH); + mRecordHighCallStyle.setInterruptive(true); + Notification n4 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setStyle(new Notification.MessagingStyle("sender!")).build(); mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2, @@ -236,6 +256,7 @@ public class NotificationComparatorTest extends UiServiceTestCase { final List<NotificationRecord> expected = new ArrayList<>(); expected.add(mRecordColorizedCall); expected.add(mRecordColorized); + expected.add(mRecordHighCallStyle); expected.add(mRecordHighCall); expected.add(mRecordInlineReply); if (mRecordSms != null) { 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 37bc23e6a17d..e6ac52d2bf6f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -339,8 +339,8 @@ public class ActivityStarterTests extends WindowTestsBase { // Direct starter to use spy stack. doReturn(stack).when(mRootWindowContainer) .getLaunchRootTask(any(), any(), any(), anyBoolean()); - doReturn(stack).when(mRootWindowContainer).getLaunchRootTask(any(), any(), any(), - anyBoolean(), any(), anyInt(), anyInt()); + doReturn(stack).when(mRootWindowContainer).getLaunchRootTask(any(), any(), any(), any(), + anyBoolean(), any(), anyInt(), anyInt(), anyInt()); } // Set up mock package manager internal and make sure no unmocked methods are called @@ -1119,8 +1119,8 @@ public class ActivityStarterTests extends WindowTestsBase { stack.addChild(targetRecord); - doReturn(stack).when(mRootWindowContainer) - .getLaunchRootTask(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt()); + doReturn(stack).when(mRootWindowContainer).getLaunchRootTask(any(), any(), any(), any(), + anyBoolean(), any(), anyInt(), anyInt(), anyInt()); starter.mStartActivity = new ActivityBuilder(mAtm).build(); @@ -1142,4 +1142,38 @@ public class ActivityStarterTests extends WindowTestsBase { verify(targetRecord).makeVisibleIfNeeded(null, true); assertTrue(targetRecord.mVisibleRequested); } + + @Test + public void testLaunchCookie_newAndExistingTask() { + final ActivityStarter starter = prepareStarter(0, false); + + // Put an activity on default display as the top focused activity. + ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build(); + + // Start an activity with a launch cookie + final Binder cookie = new Binder(); + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchCookie(cookie); + final Intent intent = new Intent(); + intent.setComponent(ActivityBuilder.getDefaultComponent()); + starter.setReason("testLaunchCookie_newTask") + .setIntent(intent) + .setActivityOptions(options.toBundle()) + .execute(); + + // Verify the cookie is set + assertTrue(mRootWindowContainer.topRunningActivity().mLaunchCookie == cookie); + + // Relaunch the activity to bring the task forward + final Binder newCookie = new Binder(); + final ActivityOptions newOptions = ActivityOptions.makeBasic(); + newOptions.setLaunchCookie(newCookie); + starter.setReason("testLaunchCookie_existingTask") + .setIntent(intent) + .setActivityOptions(newOptions.toBundle()) + .execute(); + + // Verify the cookie is updated + assertTrue(mRootWindowContainer.topRunningActivity().mLaunchCookie == newCookie); + } } 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 4dbb2de283f5..d3a825bb57a2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1244,7 +1244,6 @@ public class DisplayContentTests extends WindowTestsBase { final DisplayContent displayContent = createNewDisplay(); Mockito.doReturn(mockLogger).when(displayContent).getMetricsLogger(); Mockito.doReturn(oldConfig).doReturn(newConfig).when(displayContent).getConfiguration(); - doNothing().when(displayContent).preOnConfigurationChanged(); displayContent.onConfigurationChanged(newConfig); @@ -1458,54 +1457,51 @@ public class DisplayContentTests extends WindowTestsBase { public void testFixedRotationWithPip() { final DisplayContent displayContent = mDefaultDisplay; unblockDisplayRotation(displayContent); + // Unblock the condition in PinnedTaskController#continueOrientationChangeIfNeeded. + doNothing().when(displayContent).prepareAppTransition(anyInt()); // Make resume-top really update the activity state. - setBooted(mWm.mAtmService); + setBooted(mAtm); + clearInvocations(mWm); // Speed up the test by a few seconds. - mWm.mAtmService.deferWindowLayout(); - doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); + mAtm.deferWindowLayout(); - final Configuration displayConfig = displayContent.getConfiguration(); - final ActivityRecord pinnedActivity = createActivityRecord(displayContent, - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); + final ActivityRecord homeActivity = createActivityRecord( + displayContent.getDefaultTaskDisplayArea().getRootHomeTask()); + final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm).setCreateTask(true).build(); final Task pinnedTask = pinnedActivity.getRootTask(); - final ActivityRecord homeActivity = createActivityRecord(displayContent); - if (displayConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { - homeActivity.setOrientation(SCREEN_ORIENTATION_PORTRAIT); - pinnedActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - } else { - homeActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - pinnedActivity.setOrientation(SCREEN_ORIENTATION_PORTRAIT); - } - final int homeConfigOrientation = homeActivity.getRequestedConfigurationOrientation(); - final int pinnedConfigOrientation = pinnedActivity.getRequestedConfigurationOrientation(); + doReturn((displayContent.getRotation() + 1) % 4).when(displayContent) + .rotationForActivityInDifferentOrientation(eq(homeActivity)); + // Enter PiP from fullscreen. + pinnedTask.setWindowingMode(WINDOWING_MODE_PINNED); - assertEquals(homeConfigOrientation, displayConfig.orientation); + assertTrue(displayContent.hasTopFixedRotationLaunchingApp()); + assertTrue(displayContent.mPinnedTaskController.shouldDeferOrientationChange()); + verify(mWm, never()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); + clearInvocations(pinnedTask); + + // Assume that the PiP enter animation is done then the new bounds are set. Expect the + // orientation update is no longer deferred. + displayContent.mPinnedTaskController.setEnterPipBounds(pinnedTask.getBounds()); + // The Task Configuration was frozen to skip the change of orientation. + verify(pinnedTask, never()).onConfigurationChanged(any()); + assertFalse(displayContent.mPinnedTaskController.shouldDeferOrientationChange()); + assertFalse(displayContent.hasTopFixedRotationLaunchingApp()); + assertEquals(homeActivity.getConfiguration().orientation, + displayContent.getConfiguration().orientation); - clearInvocations(mWm); + doReturn((displayContent.getRotation() + 1) % 4).when(displayContent) + .rotationForActivityInDifferentOrientation(eq(pinnedActivity)); // Leave PiP to fullscreen. Simulate the step of PipTaskOrganizer that sets the activity // to fullscreen, so fixed rotation will apply on it. pinnedActivity.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - homeActivity.setState(Task.ActivityState.STOPPED, "test"); - assertTrue(displayContent.hasTopFixedRotationLaunchingApp()); - verify(mWm, never()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); - assertNotEquals(pinnedConfigOrientation, displayConfig.orientation); // Assume the animation of PipTaskOrganizer is done and then commit fullscreen to task. pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); displayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); - assertFalse(displayContent.getPinnedTaskController().isPipActiveOrWindowingModeChanging()); - assertEquals(pinnedConfigOrientation, displayConfig.orientation); - - clearInvocations(mWm); - // Enter PiP from fullscreen. The orientation can be updated from - // ensure-visibility/resume-focused-stack -> ActivityRecord#makeActiveIfNeeded -> resume. - pinnedTask.setWindowingMode(WINDOWING_MODE_PINNED); - - assertFalse(displayContent.hasTopFixedRotationLaunchingApp()); - verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); - assertEquals(homeConfigOrientation, displayConfig.orientation); - assertTrue(displayContent.getPinnedTaskController().isPipActiveOrWindowingModeChanging()); + assertFalse(displayContent.mPinnedTaskController.isFreezingTaskConfig(pinnedTask)); + assertEquals(pinnedActivity.getConfiguration().orientation, + displayContent.getConfiguration().orientation); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index 2321a73fefb2..e1aca55762d6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -121,8 +121,6 @@ public class DisplayRotationTests { sMockWm = mock(WindowManagerService.class); sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class); sMockWm.mPolicy = mock(WindowManagerPolicy.class); - sMockWm.mConstants = mock(WindowManagerConstants.class); - sMockWm.mConstants.mRawSensorLoggingEnabled = true; } @AfterClass diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java index bdc4b4eae630..2c3f52e05add 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java @@ -91,7 +91,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider(); // Display: 1920x1200 (landscape). First and second display are both 860x1200 (portrait). - mDisplay = (DualDisplayContent) new DualDisplayContent.Builder(mAtm, 1920, 1200).build(); + mDisplay = new DualDisplayContent.Builder(mAtm, 1920, 1200).build(); mFirstRoot = mDisplay.mFirstRoot; mSecondRoot = mDisplay.mSecondRoot; mFirstTda = mDisplay.getTaskDisplayArea(FEATURE_FIRST_TASK_CONTAINER); @@ -395,7 +395,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { } /** Display with two {@link DisplayAreaGroup}. Each of them take half of the screen. */ - private static class DualDisplayContent extends TestDisplayContent { + static class DualDisplayContent extends TestDisplayContent { final DisplayAreaGroup mFirstRoot; final DisplayAreaGroup mSecondRoot; final Rect mLastDisplayBounds; @@ -476,11 +476,15 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { TestDisplayContent createInternal(Display display) { return new DualDisplayContent(mService.mRootWindowContainer, display); } + + DualDisplayContent build() { + return (DualDisplayContent) super.build(); + } } } /** Policy to create a dual {@link DisplayAreaGroup} policy in test. */ - private static class DualDisplayTestPolicyProvider implements DisplayAreaPolicy.Provider { + static class DualDisplayTestPolicyProvider implements DisplayAreaPolicy.Provider { @Override public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java index 1b9308d32c8c..f2418c68358d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java @@ -47,6 +47,7 @@ import com.android.server.inputmethod.InputMethodMenuController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; // TODO(b/157888351): Move the test to inputmethod package once we find the way to test the // scenario there. @@ -59,10 +60,15 @@ import org.junit.runner.RunWith; public class InputMethodMenuControllerTest extends WindowTestsBase { private InputMethodMenuController mController; - private TestDisplayContent mSecondaryDisplay; + private DualDisplayAreaGroupPolicyTest.DualDisplayContent mSecondaryDisplay; @Before public void setUp() throws Exception { + // Let the Display to be created with the DualDisplay policy. + final DisplayAreaPolicy.Provider policyProvider = + new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider(); + Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider(); + mController = new InputMethodMenuController(mock(InputMethodManagerService.class)); // Mock addWindowTokenWithOptions to create a test window token. @@ -80,7 +86,8 @@ public class InputMethodMenuControllerTest extends WindowTestsBase { }).when(wms).attachWindowContextToDisplayArea(any(), eq(TYPE_INPUT_METHOD_DIALOG), anyInt(), any()); - mSecondaryDisplay = new TestDisplayContent.Builder(mAtm, 1000, 1000).build(); + mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent + .Builder(mAtm, 1000, 1000).build(); // Mock DisplayManagerGlobal to return test display when obtaining Display instance. final int displayId = mSecondaryDisplay.getDisplayId(); @@ -105,6 +112,22 @@ public class InputMethodMenuControllerTest extends WindowTestsBase { assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mSecondaryDisplay); } + @Test + public void testGetSettingsContextOnDualDisplayContent() { + final Context context = mController.getSettingsContext(mSecondaryDisplay.getDisplayId()); + + final DisplayArea.Tokens imeContainer = mSecondaryDisplay.getImeContainer(); + assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondaryDisplay); + + mSecondaryDisplay.mFirstRoot.placeImeContainer(imeContainer); + assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondaryDisplay.mFirstRoot); + assertImeSwitchContextMetricsValidity(context, mSecondaryDisplay); + + mSecondaryDisplay.mSecondRoot.placeImeContainer(imeContainer); + assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondaryDisplay.mSecondRoot); + assertImeSwitchContextMetricsValidity(context, mSecondaryDisplay); + } + private void assertImeSwitchContextMetricsValidity(Context context, DisplayContent dc) { assertThat(context.getDisplayId()).isEqualTo(dc.getDisplayId()); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 0bf237dc6545..4f5511b55d3a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -980,7 +980,8 @@ public class RootWindowContainerTests extends WindowTestsBase { doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId, 300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info); final Task result = mRootWindowContainer.getLaunchRootTask(r, options, - null /* task */, true /* onTop */, null, 300 /* test realCallerPid */, + null /* task */, null /* sourceTask */, true /* onTop */, null /* launchParams */, + 0 /* launchFlags */, 300 /* test realCallerPid */, 300 /* test realCallerUid */); // Assert that the root task is returned as expected. 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 92d4edec85f4..9289ce41cd1e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -28,6 +28,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT; import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; @@ -75,6 +76,52 @@ import org.junit.runner.RunWith; public class TaskDisplayAreaTests extends WindowTestsBase { @Test + public void getLaunchRootTask_checksLaunchAdjacentFlagRoot() { + final Task rootTask = createTask( + mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + rootTask.mCreatedByOrganizer = true; + final Task adjacentRootTask = createTask( + mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + adjacentRootTask.mCreatedByOrganizer = true; + final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); + adjacentRootTask.mAdjacentTask = rootTask; + rootTask.mAdjacentTask = adjacentRootTask; + + taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask); + Task actualRootTask = taskDisplayArea.getLaunchRootTask( + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, null /* options */, + null /* sourceTask */, FLAG_ACTIVITY_LAUNCH_ADJACENT); + assertSame(adjacentRootTask, actualRootTask.getRootTask()); + + taskDisplayArea.setLaunchAdjacentFlagRootTask(null); + actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED, + ACTIVITY_TYPE_STANDARD, null /* options */, null /* sourceTask */, + FLAG_ACTIVITY_LAUNCH_ADJACENT); + assertNull(actualRootTask); + } + + @Test + public void getLaunchRootTask_fromLaunchAdjacentFlagRoot_checksAdjacentRoot() { + final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); + final Task rootTask = createTask( + mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + rootTask.mCreatedByOrganizer = true; + final Task adjacentRootTask = createTask( + mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + adjacentRootTask.mCreatedByOrganizer = true; + final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); + adjacentRootTask.mAdjacentTask = rootTask; + rootTask.mAdjacentTask = adjacentRootTask; + + taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask); + final Task actualRootTask = taskDisplayArea.getLaunchRootTask( + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, null /* options */, + adjacentRootTask /* sourceTask */, FLAG_ACTIVITY_LAUNCH_ADJACENT); + + assertSame(rootTask, actualRootTask.getRootTask()); + } + + @Test public void getOrCreateLaunchRootRespectsResolvedWindowingMode() { final Task rootTask = createTask( mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); @@ -90,8 +137,8 @@ public class TaskDisplayAreaTests extends WindowTestsBase { launchParams.mWindowingMode = WINDOWING_MODE_FREEFORM; final Task actualRootTask = taskDisplayArea.getOrCreateRootTask( - activity, null /* options */, candidateRootTask, - launchParams, ACTIVITY_TYPE_STANDARD, true /* onTop */); + activity, null /* options */, candidateRootTask, null /* sourceTask */, + launchParams, 0 /* launchFlags */, ACTIVITY_TYPE_STANDARD, true /* onTop */); assertSame(rootTask, actualRootTask.getRootTask()); } @@ -111,8 +158,9 @@ public class TaskDisplayAreaTests extends WindowTestsBase { options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); final Task actualRootTask = taskDisplayArea.getOrCreateRootTask( - activity, options, candidateRootTask, - null /* launchParams */, ACTIVITY_TYPE_STANDARD, true /* onTop */); + activity, options, candidateRootTask, null /* sourceTask */, + null /* launchParams */, 0 /* launchFlags */, ACTIVITY_TYPE_STANDARD, + true /* onTop */); assertSame(rootTask, actualRootTask.getRootTask()); } @@ -458,8 +506,8 @@ public class TaskDisplayAreaTests extends WindowTestsBase { boolean reuseCandidate) { final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea(); final Task rootTask = taskDisplayArea.getOrCreateRootTask(windowingMode, activityType, - false /* onTop */, null /* intent */, candidateTask /* candidateTask */, - null /* activityOptions */); + false /* onTop */, candidateTask /* candidateTask */, null /* sourceTask */, + null /* activityOptions */, 0 /* launchFlags */); assertEquals(reuseCandidate, rootTask == candidateTask); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerThumbnailTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerThumbnailTest.java index 212ffd57b152..0b1b877c27de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerThumbnailTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerThumbnailTest.java @@ -18,14 +18,13 @@ package com.android.server.wm; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; -import static org.mockito.ArgumentMatchers.any; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; import android.hardware.HardwareBuffer; import android.platform.test.annotations.Presubmit; -import android.view.Surface; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; @@ -51,8 +50,8 @@ public class WindowContainerThumbnailTest extends WindowTestsBase { when(mockAr.getPendingTransaction()).thenReturn(new StubTransaction()); when(mockAr.makeChildSurface(any())).thenReturn(new MockSurfaceControlBuilder()); when(mockAr.makeSurface()).thenReturn(new MockSurfaceControlBuilder()); - return new WindowContainerThumbnail(new StubTransaction(), mockAr, - buffer, false, mock(Surface.class), mock(SurfaceAnimator.class)); + return new WindowContainerThumbnail(new StubTransaction(), mockAr, buffer, + mock(SurfaceAnimator.class)); } @Test diff --git a/services/translation/java/com/android/server/translation/RemoteTranslationService.java b/services/translation/java/com/android/server/translation/RemoteTranslationService.java index 82cb728f5bd2..c77396d8371b 100644 --- a/services/translation/java/com/android/server/translation/RemoteTranslationService.java +++ b/services/translation/java/com/android/server/translation/RemoteTranslationService.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.os.IBinder; import android.os.ResultReceiver; import android.service.translation.ITranslationService; import android.service.translation.TranslationService; @@ -44,8 +45,10 @@ final class RemoteTranslationService extends ServiceConnector.Impl<ITranslationS private final int mRequestTimeoutMs; private final ComponentName mComponentName; + private final IBinder mRemoteCallback; + RemoteTranslationService(Context context, ComponentName serviceName, - int userId, boolean bindInstantServiceAllowed) { + int userId, boolean bindInstantServiceAllowed, IBinder callback) { super(context, new Intent(TranslationService.SERVICE_INTERFACE).setComponent(serviceName), bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, @@ -53,7 +56,7 @@ final class RemoteTranslationService extends ServiceConnector.Impl<ITranslationS mIdleUnbindTimeoutMs = TIMEOUT_IDLE_UNBIND_MS; mRequestTimeoutMs = TIMEOUT_REQUEST_MS; mComponentName = serviceName; - + mRemoteCallback = callback; // Bind right away. connect(); } @@ -67,7 +70,7 @@ final class RemoteTranslationService extends ServiceConnector.Impl<ITranslationS boolean connected) { try { if (connected) { - service.onConnected(); + service.onConnected(mRemoteCallback); } else { service.onDisconnected(); } diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java index 6bba65dc36ae..628f8cdc8f0c 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerService.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java @@ -44,6 +44,7 @@ import android.view.translation.ITranslationManager; import android.view.translation.TranslationContext; import android.view.translation.TranslationSpec; import android.view.translation.UiTranslationManager.UiTranslationState; +import android.view.translation.UiTranslationSpec; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; @@ -170,6 +171,28 @@ public final class TranslationManagerService } @Override + public void registerTranslationCapabilityCallback(IRemoteCallback callback, int userId) { + TranslationManagerServiceImpl service; + synchronized (mLock) { + service = getServiceForUserLocked(userId); + } + if (service != null) { + service.registerTranslationCapabilityCallback(callback, Binder.getCallingUid()); + } + } + + @Override + public void unregisterTranslationCapabilityCallback(IRemoteCallback callback, int userId) { + TranslationManagerServiceImpl service; + synchronized (mLock) { + service = getServiceForUserLocked(userId); + } + if (service != null) { + service.unregisterTranslationCapabilityCallback(callback); + } + } + + @Override public void onSessionCreated(TranslationContext translationContext, int sessionId, IResultReceiver receiver, int userId) throws RemoteException { synchronized (mLock) { @@ -187,14 +210,14 @@ public final class TranslationManagerService @Override public void updateUiTranslationState(@UiTranslationState int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds, - IBinder token, int taskId, int userId) { + IBinder token, int taskId, UiTranslationSpec uiTranslationSpec, int userId) { enforceCallerHasPermission(MANAGE_UI_TRANSLATION); synchronized (mLock) { final TranslationManagerServiceImpl service = getServiceForUserLocked(userId); if (service != null && (isDefaultServiceLocked(userId) || isCalledByServiceAppLocked(userId, "updateUiTranslationState"))) { service.updateUiTranslationStateLocked(state, sourceSpec, targetSpec, viewIds, - token, taskId); + token, taskId, uiTranslationSpec); } } } diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java index d2a69511f6ea..4198d3b86500 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java @@ -16,6 +16,7 @@ package com.android.server.translation; +import static android.view.translation.TranslationManager.EXTRA_CAPABILITIES; import static android.view.translation.UiTranslationManager.EXTRA_SOURCE_LOCALE; import static android.view.translation.UiTranslationManager.EXTRA_STATE; import static android.view.translation.UiTranslationManager.EXTRA_TARGET_LOCALE; @@ -35,9 +36,12 @@ import android.service.translation.TranslationServiceInfo; import android.util.Slog; import android.view.autofill.AutofillId; import android.view.inputmethod.InputMethodInfo; +import android.view.translation.ITranslationServiceCallback; +import android.view.translation.TranslationCapability; import android.view.translation.TranslationContext; import android.view.translation.TranslationSpec; import android.view.translation.UiTranslationManager.UiTranslationState; +import android.view.translation.UiTranslationSpec; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; @@ -67,6 +71,11 @@ final class TranslationManagerServiceImpl extends private ActivityTaskManagerInternal mActivityTaskManagerInternal; + private final TranslationServiceRemoteCallback mRemoteServiceCallback = + new TranslationServiceRemoteCallback(); + private final RemoteCallbackList<IRemoteCallback> mTranslationCapabilityCallbacks = + new RemoteCallbackList<>(); + protected TranslationManagerServiceImpl( @NonNull TranslationManagerService master, @NonNull Object lock, int userId, boolean disabled) { @@ -117,8 +126,8 @@ final class TranslationManagerServiceImpl extends return null; } final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName); - mRemoteTranslationService = new RemoteTranslationService(getContext(), - serviceComponent, mUserId, /* isInstantAllowed= */ false); + mRemoteTranslationService = new RemoteTranslationService(getContext(), serviceComponent, + mUserId, /* isInstantAllowed= */ false, mRemoteServiceCallback); } return mRemoteTranslationService; } @@ -134,6 +143,15 @@ final class TranslationManagerServiceImpl extends } } + public void registerTranslationCapabilityCallback(IRemoteCallback callback, int sourceUid) { + mTranslationCapabilityCallbacks.register(callback, sourceUid); + ensureRemoteServiceLocked(); + } + + public void unregisterTranslationCapabilityCallback(IRemoteCallback callback) { + mTranslationCapabilityCallbacks.unregister(callback); + } + @GuardedBy("mLock") void onSessionCreatedLocked(@NonNull TranslationContext translationContext, int sessionId, IResultReceiver resultReceiver) { @@ -146,7 +164,7 @@ final class TranslationManagerServiceImpl extends @GuardedBy("mLock") public void updateUiTranslationStateLocked(@UiTranslationState int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds, - IBinder token, int taskId) { + IBinder token, int taskId, UiTranslationSpec uiTranslationSpec) { // Get top activity for a given task id final ActivityTokens taskTopActivityTokens = mActivityTaskManagerInternal.getTopActivityForTask(taskId); @@ -157,6 +175,7 @@ final class TranslationManagerServiceImpl extends return; } try { + // TODO: Pipe uiTranslationSpec through to the UiTranslationController. taskTopActivityTokens.getApplicationThread().updateUiTranslationState( taskTopActivityTokens.getActivityToken(), state, sourceSpec, targetSpec, viewIds); @@ -221,4 +240,29 @@ final class TranslationManagerServiceImpl extends final String packageName = mTranslationServiceInfo.getServiceInfo().packageName; return new ComponentName(packageName, activityName); } + + private void notifyClientsTranslationCapability(TranslationCapability capability) { + final Bundle res = new Bundle(); + res.putParcelable(EXTRA_CAPABILITIES, capability); + mTranslationCapabilityCallbacks.broadcast((callback, uid) -> { + try { + callback.sendResult(res); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e); + } + }); + } + + private final class TranslationServiceRemoteCallback extends + ITranslationServiceCallback.Stub { + + @Override + public void updateTranslationCapability(TranslationCapability capability) { + if (capability == null) { + Slog.wtf(TAG, "received a null TranslationCapability from TranslationService."); + return; + } + notifyClientsTranslationCapability(capability); + } + } } diff --git a/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java b/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java index 2f6a2b017267..f34567f83aff 100644 --- a/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java +++ b/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java @@ -311,6 +311,11 @@ public class UwbServiceImpl extends IUwbAdapter.Stub implements IBinder.DeathRec } @Override + public synchronized int getAdapterState() throws RemoteException { + return getVendorUwbAdapter().getAdapterState(); + } + + @Override public synchronized void setEnabled(boolean enabled) throws RemoteException { getVendorUwbAdapter().setEnabled(enabled); } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index d6ed98f2c3d4..3fbd40f8a060 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -65,7 +65,6 @@ import java.io.PrintWriter; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; @@ -235,19 +234,32 @@ final class HotwordDetectionConnection { Slog.d(TAG, "startListeningFromMic"); } - AudioRecord audioRecord = createMicAudioRecord(audioFormat); - if (audioRecord == null) { - // TODO: Callback.onError(); - return; - } + // TODO: consider making this a non-anonymous class. + IDspHotwordDetectionCallback internalCallback = new IDspHotwordDetectionCallback.Stub() { + @Override + public void onDetected(HotwordDetectedResult result) throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "onDetected"); + } + callback.onDetected(result, null, null); + } - handleSoftwareHotwordDetection( - audioFormat, - AudioReader.createFromAudioRecord(audioRecord), - AUDIO_SOURCE_MICROPHONE, - // TODO: handle bundles better. - new PersistableBundle(), - callback); + @Override + public void onRejected(HotwordRejectedResult result) throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "onRejected"); + } + // onRejected isn't allowed here + } + }; + + mRemoteHotwordDetectionService.run( + service -> service.detectFromMicrophoneSource( + null, + AUDIO_SOURCE_MICROPHONE, + null, + null, + internalCallback)); } public void startListeningFromExternalSource( @@ -298,74 +310,12 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "detectFromDspSourceForTest"); } - - AudioRecord record = createFakeAudioRecord(); - if (record == null) { - Slog.d(TAG, "Failed to create fake audio record"); - return; - } - - Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe(); - if (clientPipe == null) { - Slog.d(TAG, "Failed to create pipe"); - return; - } - ParcelFileDescriptor audioSink = clientPipe.second; - ParcelFileDescriptor clientRead = clientPipe.first; - - record.startRecording(); - - mAudioCopyExecutor.execute(() -> { - try (OutputStream fos = - new ParcelFileDescriptor.AutoCloseOutputStream(audioSink)) { - - int remainToRead = 10240; - byte[] buffer = new byte[1024]; - while (remainToRead > 0) { - int bytesRead = record.read(buffer, 0, 1024); - if (DEBUG) { - Slog.d(TAG, "bytesRead = " + bytesRead); - } - if (bytesRead <= 0) { - break; - } - if (bytesRead > 8) { - System.arraycopy(new byte[] {'h', 'o', 't', 'w', 'o', 'r', 'd', '!'}, 0, - buffer, 0, 8); - } - - fos.write(buffer, 0, bytesRead); - remainToRead -= bytesRead; - } - } catch (IOException e) { - Slog.w(TAG, "Failed supplying audio data to validator", e); - } - }); - - Runnable cancellingJob = () -> { - Slog.d(TAG, "Timeout for getting callback from HotwordDetectionService"); - record.stop(); - record.release(); - bestEffortClose(audioSink); - bestEffortClose(clientRead); - }; - - ScheduledFuture<?> cancelingFuture = - mScheduledExecutorService.schedule( - cancellingJob, VALIDATION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); - IDspHotwordDetectionCallback internalCallback = new IDspHotwordDetectionCallback.Stub() { @Override public void onDetected(HotwordDetectedResult result) throws RemoteException { if (DEBUG) { Slog.d(TAG, "onDetected"); } - cancelingFuture.cancel(true); - record.stop(); - record.release(); - bestEffortClose(audioSink); - bestEffortClose(clientRead); - externalCallback.onKeyphraseDetected(recognitionEvent); } @@ -374,19 +324,13 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "onRejected"); } - cancelingFuture.cancel(true); - record.stop(); - record.release(); - bestEffortClose(audioSink); - bestEffortClose(clientRead); - externalCallback.onRejected(result); } }; mRemoteHotwordDetectionService.run( service -> service.detectFromDspSource( - clientRead, + recognitionEvent, recognitionEvent.getCaptureFormat(), VALIDATION_TIMEOUT_MILLIS, internalCallback)); @@ -398,49 +342,6 @@ final class HotwordDetectionConnection { Slog.d(TAG, "detectFromDspSource"); } - AudioRecord record = createAudioRecord(recognitionEvent); - - Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe(); - - if (clientPipe == null) { - // Error. - // Need to propagate as unknown error or something? - return; - } - ParcelFileDescriptor audioSink = clientPipe.second; - ParcelFileDescriptor clientRead = clientPipe.first; - - record.startRecording(); - - mAudioCopyExecutor.execute(() -> { - try (OutputStream fos = - new ParcelFileDescriptor.AutoCloseOutputStream(audioSink)) { - byte[] buffer = new byte[1024]; - - while (true) { - int bytesRead = record.read(buffer, 0, 1024); - - if (bytesRead < 0) { - break; - } - - fos.write(buffer, 0, bytesRead); - } - } catch (IOException e) { - Slog.w(TAG, "Failed supplying audio data to validator", e); - } - }); - - Runnable cancellingJob = () -> { - record.stop(); - bestEffortClose(audioSink); - // TODO: consider calling externalCallback.onRejected(ERROR_TIMEOUT). - }; - - ScheduledFuture<?> cancelingFuture = - mScheduledExecutorService.schedule( - cancellingJob, VALIDATION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); - // TODO: consider making this a non-anonymous class. IDspHotwordDetectionCallback internalCallback = new IDspHotwordDetectionCallback.Stub() { @Override @@ -448,18 +349,6 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "onDetected"); } - bestEffortClose(audioSink); - cancelingFuture.cancel(true); - - // Give 2 more seconds for the interactor to start consuming the mic. If it fails to - // do so under the given time, we'll force-close the mic to make sure resources are - // freed up. - // TODO: consider modelling these 2 seconds in the API. - mScheduledExecutorService.schedule( - cancellingJob, - VOICE_INTERACTION_TIMEOUT_TO_OPEN_MIC_MILLIS, - TimeUnit.MILLISECONDS); - // TODO: Propagate the HotwordDetectedResult. externalCallback.onKeyphraseDetected(recognitionEvent); } @@ -469,18 +358,16 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "onRejected"); } - cancelingFuture.cancel(true); externalCallback.onRejected(result); } }; mRemoteHotwordDetectionService.run( service -> service.detectFromDspSource( - clientRead, + recognitionEvent, recognitionEvent.getCaptureFormat(), VALIDATION_TIMEOUT_MILLIS, internalCallback)); - bestEffortClose(clientRead); } static final class SoundTriggerCallback extends IRecognitionStatusCallback.Stub { diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java index 65aa9aed8f99..7a1dda37de70 100644 --- a/test-mock/src/android/test/mock/MockContext.java +++ b/test-mock/src/android/test/mock/MockContext.java @@ -375,6 +375,13 @@ public class MockContext extends Context { /** @hide */ @Override + public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions, + Bundle options) { + throw new UnsupportedOperationException(); + } + + /** @hide */ + @Override public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user, String[] receiverPermissions) { throw new UnsupportedOperationException(); diff --git a/tests/Camera2Tests/CameraToo/Android.mk b/tests/Camera2Tests/CameraToo/Android.mk index 7e5911d65bfa..33473143c8cb 100644 --- a/tests/Camera2Tests/CameraToo/Android.mk +++ b/tests/Camera2Tests/CameraToo/Android.mk @@ -17,6 +17,9 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := CameraToo +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_SRC_FILES := $(call all-java-files-under,src) diff --git a/tests/Camera2Tests/CameraToo/tests/Android.mk b/tests/Camera2Tests/CameraToo/tests/Android.mk index fe4dc42aa7d9..dfa64f1feade 100644 --- a/tests/Camera2Tests/CameraToo/tests/Android.mk +++ b/tests/Camera2Tests/CameraToo/tests/Android.mk @@ -17,6 +17,9 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := CameraTooTests +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../NOTICE LOCAL_INSTRUMENTATION_FOR := CameraToo LOCAL_SDK_VERSION := current LOCAL_SRC_FILES := $(call all-java-files-under,src) diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk index 4e3675f78edf..6003628ffb0d 100644 --- a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk +++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk @@ -32,6 +32,9 @@ LOCAL_SRC_FILES := \ $(call all-renderscript-files-under, src) LOCAL_PACKAGE_NAME := SmartCamera +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../NOTICE LOCAL_JNI_SHARED_LIBRARIES := libsmartcamera_jni include $(BUILD_PACKAGE) diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk index a9000774a13a..c23d593d4f86 100644 --- a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk +++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk @@ -22,6 +22,9 @@ LOCAL_MODULE_TAGS := tests LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_PACKAGE_NAME := SmartCamera-tests +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_SRC_FILES += $(call all-java-files-under, src) diff --git a/tests/CanvasCompare/Android.mk b/tests/CanvasCompare/Android.mk index 6a0a93e1bb24..b82ae65b4356 100644 --- a/tests/CanvasCompare/Android.mk +++ b/tests/CanvasCompare/Android.mk @@ -20,6 +20,9 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src) LOCAL_PACKAGE_NAME := CanvasCompare +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_MODULE_TAGS := tests diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.mk b/tests/DynamicCodeLoggerIntegrationTests/Android.mk index bfb5b076237a..dab83046c28f 100644 --- a/tests/DynamicCodeLoggerIntegrationTests/Android.mk +++ b/tests/DynamicCodeLoggerIntegrationTests/Android.mk @@ -89,4 +89,7 @@ LOCAL_JAVA_RESOURCE_FILES := \ $(dynamiccodeloggertest_jar) \ $(dynamiccodeloggertest_executable) \ +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE include $(BUILD_PACKAGE) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt index cdec51d25d7f..7bc004f6c6d9 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt @@ -50,6 +50,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 185400889) class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() private val testApp = ImeAppHelper(instrumentation) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt index 559d95376b65..5703e6cfae48 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt @@ -39,6 +39,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 185400889) class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) { override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java index 8be3b7e35d42..c06f8fd44c03 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java @@ -60,7 +60,7 @@ public class ColorFiltersMutateActivity extends Activity { private float mShaderParam1 = 0.0f; static final String sSkSL = - "in shader bitmapShader;\n" + "uniform shader bitmapShader;\n" + "uniform float param1;\n" + "half4 main(float2 xy) {\n" + " return half4(sample(bitmapShader, xy).rgb, param1);\n" diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java index 487c8566ce37..79410cf35f7a 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java @@ -89,7 +89,7 @@ public class RippleActivity extends Activity { + " d = rand(float2(x, y)) > density ? d : d * .2;\n" + " d = d * rand(float2(fraction, x * y));\n" + " float alpha = 1. - pow(fraction, 3.);\n" - + " return float4(sample(in_paintColor).rgb, d * alpha);\n" + + " return float4(sample(in_paintColor, p).rgb, d * alpha);\n" + "}"; RippleView(Context c) { diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java index 912aee686924..ade94a9672bc 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java @@ -440,7 +440,7 @@ public class StretchShaderActivity extends Activity { } } - private static final String SKSL = "in shader uContentTexture;\n" + private static final String SKSL = "uniform shader uContentTexture;\n" + "uniform float uMaxStretchIntensity; // multiplier to apply to scale effect\n" + "uniform float uStretchAffectedDist; // Maximum percentage to stretch beyond bounds" + " of target\n" diff --git a/tests/LockTaskTests/Android.mk b/tests/LockTaskTests/Android.mk index a693eaa4da04..5406ee19041b 100644 --- a/tests/LockTaskTests/Android.mk +++ b/tests/LockTaskTests/Android.mk @@ -5,6 +5,9 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/priv-app LOCAL_PACKAGE_NAME := LockTaskTests +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE LOCAL_SDK_VERSION := current LOCAL_CERTIFICATE := platform diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk index 204a74eed882..cc0fa1cd0840 100644 --- a/tests/SoundTriggerTests/Android.mk +++ b/tests/SoundTriggerTests/Android.mk @@ -31,6 +31,9 @@ LOCAL_STATIC_JAVA_LIBRARIES := mockito-target LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base LOCAL_PACKAGE_NAME := SoundTriggerTests +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE LOCAL_PRIVATE_PLATFORM_APIS := true include $(BUILD_PACKAGE) diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk index 9b155c930871..b6f34717658c 100644 --- a/tests/backup/Android.mk +++ b/tests/backup/Android.mk @@ -47,4 +47,7 @@ LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_PROGUARD_ENABLED := disabled +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE include $(BUILD_PACKAGE) diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java index 19f884346e6f..591e0cc3504e 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -379,7 +379,7 @@ public class ConnectivityManagerTest { eq(testPkgName), eq(testAttributionTag)); reset(mService); - manager.registerDefaultNetworkCallbackAsUid(42, callback, handler); + manager.registerDefaultNetworkCallbackForUid(42, callback, handler); verify(mService).requestNetwork(eq(42), eq(null), eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java index fee65f06bcad..ccaa5cf7e9f7 100644 --- a/tests/net/java/android/net/VpnTransportInfoTest.java +++ b/tests/net/java/android/net/VpnTransportInfoTest.java @@ -63,6 +63,6 @@ public class VpnTransportInfoTest { assertEquals(v31, v32); assertEquals(v11.hashCode(), v13.hashCode()); assertEquals(REDACT_FOR_NETWORK_SETTINGS, v32.getApplicableRedactions()); - assertEquals(session1, v15.makeCopy(REDACT_NONE).sessionId); + assertEquals(session1, v15.makeCopy(REDACT_NONE).getSessionId()); } } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 16620823dc7e..dcbfb933f867 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -1387,7 +1387,7 @@ public class ConnectivityServiceTest { final TransportInfo ti = nc.getTransportInfo(); assertTrue("VPN TransportInfo is not a VpnTransportInfo: " + ti, ti instanceof VpnTransportInfo); - assertEquals(type, ((VpnTransportInfo) ti).type); + assertEquals(type, ((VpnTransportInfo) ti).getType()); } @@ -2896,7 +2896,7 @@ public class ConnectivityServiceTest { callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); // Set teardown delay and make sure CS has processed it. - mWiFiNetworkAgent.getNetworkAgent().setTeardownDelayMs(300); + mWiFiNetworkAgent.getNetworkAgent().setTeardownDelayMillis(300); waitForIdle(); // Post the duringTeardown lambda to the handler so it fires while teardown is in progress. @@ -4247,7 +4247,7 @@ public class ConnectivityServiceTest { () -> mCm.registerSystemDefaultNetworkCallback(callback, handler)); callback.assertNoCallback(); assertThrows(SecurityException.class, - () -> mCm.registerDefaultNetworkCallbackAsUid(APP1_UID, callback, handler)); + () -> mCm.registerDefaultNetworkCallbackForUid(APP1_UID, callback, handler)); callback.assertNoCallback(); mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); @@ -4255,7 +4255,7 @@ public class ConnectivityServiceTest { callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); mCm.unregisterNetworkCallback(callback); - mCm.registerDefaultNetworkCallbackAsUid(APP1_UID, callback, handler); + mCm.registerDefaultNetworkCallbackForUid(APP1_UID, callback, handler); callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); mCm.unregisterNetworkCallback(callback); } @@ -5679,7 +5679,7 @@ public class ConnectivityServiceTest { for (int i = 0; i < SYSTEM_ONLY_MAX_REQUESTS - 1; i++) { NetworkCallback cb = new NetworkCallback(); if (i % 2 == 0) { - mCm.registerDefaultNetworkCallbackAsUid(1000000 + i, cb, handler); + mCm.registerDefaultNetworkCallbackForUid(1000000 + i, cb, handler); } else { mCm.registerNetworkCallback(networkRequest, cb); } @@ -5688,7 +5688,7 @@ public class ConnectivityServiceTest { waitForIdle(); assertThrows(TooManyRequestsException.class, () -> - mCm.registerDefaultNetworkCallbackAsUid(1001042, new NetworkCallback(), + mCm.registerDefaultNetworkCallbackForUid(1001042, new NetworkCallback(), handler)); assertThrows(TooManyRequestsException.class, () -> mCm.registerNetworkCallback(networkRequest, new NetworkCallback())); @@ -5741,7 +5741,7 @@ public class ConnectivityServiceTest { withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> { for (int i = 0; i < MAX_REQUESTS; i++) { NetworkCallback networkCallback = new NetworkCallback(); - mCm.registerDefaultNetworkCallbackAsUid(1000000 + i, networkCallback, + mCm.registerDefaultNetworkCallbackForUid(1000000 + i, networkCallback, new Handler(ConnectivityThread.getInstanceLooper())); mCm.unregisterNetworkCallback(networkCallback); } @@ -7825,7 +7825,7 @@ public class ConnectivityServiceTest { registerDefaultNetworkCallbackAsUid(vpnUidDefaultCallback, VPN_UID); final TestNetworkCallback vpnDefaultCallbackAsUid = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallbackAsUid(VPN_UID, vpnDefaultCallbackAsUid, + mCm.registerDefaultNetworkCallbackForUid(VPN_UID, vpnDefaultCallbackAsUid, new Handler(ConnectivityThread.getInstanceLooper())); final int uid = Process.myUid(); @@ -10479,7 +10479,7 @@ public class ConnectivityServiceTest { assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_VALIDATED)); assertTrue(mRequests.get(1).isRequest()); assertTrue(mRequests.get(1).hasCapability(NET_CAPABILITY_OEM_PAID)); - assertTrue(mRequests.get(2).isRequest()); + assertEquals(NetworkRequest.Type.TRACK_DEFAULT, mRequests.get(2).type); assertTrue(mService.getDefaultRequest().networkCapabilities.equalsNetCapabilities( mRequests.get(2).networkCapabilities)); } @@ -10991,7 +10991,7 @@ public class ConnectivityServiceTest { final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); withPermission(NETWORK_SETTINGS, () -> - mCm.registerDefaultNetworkCallbackAsUid(TEST_PACKAGE_UID, otherUidDefaultCallback, + mCm.registerDefaultNetworkCallbackForUid(TEST_PACKAGE_UID, otherUidDefaultCallback, new Handler(ConnectivityThread.getInstanceLooper()))); // Setup the test process to use networkPref for their default network. @@ -11039,7 +11039,7 @@ public class ConnectivityServiceTest { final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); withPermission(NETWORK_SETTINGS, () -> - mCm.registerDefaultNetworkCallbackAsUid(TEST_PACKAGE_UID, otherUidDefaultCallback, + mCm.registerDefaultNetworkCallbackForUid(TEST_PACKAGE_UID, otherUidDefaultCallback, new Handler(ConnectivityThread.getInstanceLooper()))); // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. @@ -11081,7 +11081,7 @@ public class ConnectivityServiceTest { final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); withPermission(NETWORK_SETTINGS, () -> - mCm.registerDefaultNetworkCallbackAsUid(TEST_PACKAGE_UID, otherUidDefaultCallback, + mCm.registerDefaultNetworkCallbackForUid(TEST_PACKAGE_UID, otherUidDefaultCallback, new Handler(ConnectivityThread.getInstanceLooper()))); // Setup a process different than the test process to use the default network. This means @@ -11693,10 +11693,12 @@ public class ConnectivityServiceTest { mServiceContext, "internetFactory", internetFilter, mCsHandlerThread); internetFactory.setScoreFilter(40); internetFactory.register(); - // Default internet request & 3rd (fallback) request in OEM_PAID NRI. The unmetered request - // is never sent to factories (it's a LISTEN, not requestable) and the OEM_PAID request - // doesn't match the internetFactory filter. - internetFactory.expectRequestAdds(2); + // Default internet request only. The unmetered request is never sent to factories (it's a + // LISTEN, not requestable). The 3rd (fallback) request in OEM_PAID NRI is TRACK_DEFAULT + // which is also not sent to factories. Finally, the OEM_PAID request doesn't match the + // internetFactory filter. + internetFactory.expectRequestAdds(1); + internetFactory.assertRequestCountEquals(1); NetworkCapabilities oemPaidFilter = new NetworkCapabilities() .addCapability(NET_CAPABILITY_INTERNET) @@ -11719,7 +11721,7 @@ public class ConnectivityServiceTest { expectNoRequestChanged(oemPaidFactory); oemPaidFactory.assertRequestCountEquals(1); // The internet factory however is outscored, and should lose its requests. - internetFactory.expectRequestRemoves(2); + internetFactory.expectRequestRemove(); internetFactory.assertRequestCountEquals(0); final NetworkCapabilities oemPaidNc = new NetworkCapabilities(); diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 6ad4900989f5..b725b826b14f 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -1023,7 +1023,7 @@ public class VpnTest { assertNotNull(nc); VpnTransportInfo ti = (VpnTransportInfo) nc.getTransportInfo(); assertNotNull(ti); - assertEquals(type, ti.type); + assertEquals(type, ti.getType()); } public void startRacoon(final String serverAddr, final String expectedAddr) diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java index 4a1f96d145bd..3da8b460df13 100644 --- a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java +++ b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java @@ -196,6 +196,12 @@ public class BroadcastInterceptingContext extends ContextWrapper { } @Override + public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions, + Bundle options) { + sendBroadcast(intent); + } + + @Override public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user, String[] receiverPermissions) { sendBroadcast(intent); diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk index 6bc2064c6e63..27b6068632f3 100644 --- a/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk +++ b/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk @@ -20,10 +20,13 @@ include $(CLEAR_VARS) LOCAL_USE_AAPT2 := true LOCAL_AAPT_NAMESPACES := true LOCAL_PACKAGE_NAME := AaptTestMergeOnly_App +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_EXPORT_PACKAGE_RESOURCES := true LOCAL_MODULE_TAGS := tests LOCAL_STATIC_ANDROID_LIBRARIES := \ AaptTestMergeOnly_LeafLib \ AaptTestMergeOnly_LocalLib -include $(BUILD_PACKAGE)
\ No newline at end of file +include $(BUILD_PACKAGE) diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk index 446237412370..98b74403a7ff 100644 --- a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk +++ b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk @@ -20,6 +20,9 @@ include $(CLEAR_VARS) LOCAL_USE_AAPT2 := true LOCAL_AAPT_NAMESPACES := true LOCAL_PACKAGE_NAME := AaptTestNamespace_App +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_EXPORT_PACKAGE_RESOURCES := true LOCAL_MODULE_TAGS := tests diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk index 83e2289430f7..30375728c9e0 100644 --- a/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk +++ b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk @@ -20,6 +20,9 @@ include $(CLEAR_VARS) LOCAL_USE_AAPT2 := true LOCAL_AAPT_NAMESPACES := true LOCAL_PACKAGE_NAME := AaptTestNamespace_Split +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under,src) diff --git a/tools/fonts/Android.bp b/tools/fonts/Android.bp index 8ea114f1efc2..eeb9e3ceda1e 100644 --- a/tools/fonts/Android.bp +++ b/tools/fonts/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + python_defaults { name: "fonts_python_defaults", version: { |