Merge "Fix a potential thread safety issue in VectorDrawable" into sc-dev
diff --git a/Android.bp b/Android.bp
index 685c69d..8db5589 100644
--- a/Android.bp
+++ b/Android.bp
@@ -325,6 +325,7 @@
"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 b995f95..e6f50b7 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -403,16 +403,6 @@
}
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 @@
"android-non-updatable.stubs",
"private-stub-annotations-jar",
],
- defaults: ["android_defaults_stubs_current"],
+ defaults: ["android.jar_defaults"],
}
java_library_static {
@@ -441,7 +431,7 @@
"private-stub-annotations-jar",
],
defaults: [
- "android_defaults_stubs_current",
+ "android.jar_defaults",
"android_stubs_dists_default",
],
dist: {
@@ -469,7 +459,7 @@
"private-stub-annotations-jar",
],
defaults: [
- "android_defaults_stubs_current",
+ "android.jar_defaults",
"android_stubs_dists_default",
],
dist: {
@@ -480,7 +470,7 @@
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 57ee1ec..566a18f 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -28,6 +28,7 @@
"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 ec81ed2..3f6e8a5 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.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.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.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 @@
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 @@
@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 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 @@
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 @@
}
}
+ 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 @@
});
}
- 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 @@
/*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 af39b79..45023f9 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 @@
}
/**
+ * 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 @@
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 e65abcf..7c67ce4 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 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 e1bfde1..48b62a6 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 @@
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.
+ 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_TIMEOUT = JobProtoEnums.STOP_REASON_TIMEOUT; // 3.
+ public static final int INTERNAL_STOP_REASON_DEVICE_IDLE =
+ JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4.
/** @hide */
- public static final int 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 @@
* @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 @@
*/
// 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 @@
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 @@
}
/** @hide */
- public int getLegacyStopReason() {
- return mLegacyStopReason;
+ public int getInternalStopReasonCode() {
+ return mInternalStopReason;
}
/**
@@ -502,15 +522,15 @@
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 @@
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 50f9a58..f96fc83 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -231,6 +231,11 @@
* @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 @@
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 @@
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 03d9a96..e63a7c4 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.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.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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
handlePackagesAddedToExactAlarmsDenyListLocked((ArraySet<String>) msg.obj);
}
break;
+ case REFRESH_EXACT_ALARM_CANDIDATES:
+ refreshExactAlarmCandidates();
+ break;
default:
// nope, just ignore it
break;
@@ -4135,6 +4171,7 @@
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 @@
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 @@
}
}
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 e60a5d1..5a13a84 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 @@
// 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 @@
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 02f9129..1f0900d 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 @@
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 @@
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 1eebae8..313cd20 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 @@
// 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 @@
// 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 @@
}
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 @@
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 @@
// 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 @@
}
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 @@
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 @@
* @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 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
&& !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 @@
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 @@
@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 @@
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 @@
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 @@
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 @@
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 44b3e3e..6ab2be3 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 @@
* {@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 @@
/** 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 @@
&& (!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 @@
}
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 @@
}
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 @@
}
@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 @@
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 @@
// 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 @@
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 @@
// 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 @@
}
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 @@
completedJob.startedAsExpeditedJob);
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
- legacyStopReason);
+ internalStopReason);
} catch (RemoteException e) {
// Whatever.
}
@@ -930,10 +945,10 @@
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 @@
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 2bdf656..7b947fd 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 @@
}
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 @@
}
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 2962b10..20df3ea 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 @@
* 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 @@
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 @@
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 8b699e9..3069db3 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 @@
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 97ee0e1..ebf4ed0 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.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.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.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.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 @@
@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);
- int index = uidStates.indexOfKey(ai.uid);
+ final boolean newIdle = currentIdle && isAppIdleFiltered(ai.packageName,
+ UserHandle.getAppId(ai.uid), userId, elapsedRealtime);
+
+ 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);
}
}
+ 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);
+ }
+ }
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[] 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++;
- }
- }
-
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 1e417e9..4f56e5a 100644
--- a/cmds/bootanimation/BootAnimationUtil.cpp
+++ b/cmds/bootanimation/BootAnimationUtil.cpp
@@ -49,7 +49,14 @@
}
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 ce09544..4cdc519 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9,6 +9,7 @@
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 @@
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);
@@ -17705,6 +17706,15 @@
method public void onDynamicSensorDisconnected(android.hardware.Sensor);
}
+ public final class SensorPrivacyManager {
+ method public boolean supportsSensorToggle(int);
+ }
+
+ public static class SensorPrivacyManager.Sensors {
+ field public static final int CAMERA = 2; // 0x2
+ field public static final int MICROPHONE = 1; // 0x1
+ }
+
public final class TriggerEvent {
field public android.hardware.Sensor sensor;
field public long timestamp;
@@ -18037,7 +18047,7 @@
}
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();
@@ -20964,8 +20974,8 @@
}
public class CamcorderProfile {
- method public static android.media.CamcorderProfile get(int);
- method public static android.media.CamcorderProfile get(int, int);
+ method @Deprecated public static android.media.CamcorderProfile get(int);
+ method @Deprecated public static android.media.CamcorderProfile get(int, int);
method @Nullable public static android.media.EncoderProfiles getAll(@NonNull String, int);
method public static boolean hasProfile(int);
method public static boolean hasProfile(int, int);
@@ -21047,14 +21057,14 @@
field @NonNull public final java.util.UUID uuid;
}
- public class EncoderProfiles {
+ public final class EncoderProfiles {
method @NonNull public java.util.List<android.media.EncoderProfiles.AudioProfile> getAudioProfiles();
- method public int getDurationSeconds();
- method public int getFileFormat();
+ method public int getDefaultDurationSeconds();
+ method public int getRecommendedFileFormat();
method @NonNull public java.util.List<android.media.EncoderProfiles.VideoProfile> getVideoProfiles();
}
- public static class EncoderProfiles.AudioProfile {
+ public static final class EncoderProfiles.AudioProfile {
method public int getBitrate();
method public int getChannels();
method public int getCodec();
@@ -21063,7 +21073,7 @@
method public int getSampleRate();
}
- public static class EncoderProfiles.VideoProfile {
+ public static final class EncoderProfiles.VideoProfile {
method public int getBitrate();
method public int getCodec();
method public int getFrameRate();
@@ -32081,6 +32091,7 @@
method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
method public int getId();
+ method @NonNull public int[] getPrimitiveDurations(@NonNull int...);
method public abstract boolean hasAmplitudeControl();
method public abstract boolean hasVibrator();
method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long);
@@ -52802,6 +52813,7 @@
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
}
@@ -52824,7 +52836,8 @@
}
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);
@@ -52832,7 +52845,8 @@
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);
}
@@ -56781,6 +56795,7 @@
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 d0326bb..fede5a5 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3,7 +3,6 @@
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.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 @@
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 @@
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 @@
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 @@
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 @@
}
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";
}
@@ -2735,10 +2734,8 @@
field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
- field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
- field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg";
@@ -3119,12 +3116,7 @@
}
public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
- method public void onSensorPrivacyChanged(boolean);
- }
-
- public static class SensorPrivacyManager.Sensors {
- field public static final int CAMERA = 2; // 0x2
- field public static final int MICROPHONE = 1; // 0x1
+ method public void onSensorPrivacyChanged(int, boolean);
}
}
@@ -8631,6 +8623,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isCloneProfile();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isGuestUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isMediaSharedWithParent();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isPrimaryUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isProfile();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile();
@@ -8642,7 +8635,6 @@
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean sharesMediaWithParent();
field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
@@ -10414,6 +10406,7 @@
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";
}
@@ -10520,6 +10513,7 @@
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);
@@ -10534,6 +10528,7 @@
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);
}
@@ -10541,8 +10536,10 @@
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
@@ -10588,7 +10585,8 @@
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();
}
@@ -12817,10 +12815,11 @@
}
public interface DelegateStateCallback {
+ method public void onConfigurationChanged(@NonNull android.telephony.ims.SipDelegateConfiguration);
method public void onCreated(@NonNull android.telephony.ims.stub.SipDelegate, @Nullable java.util.Set<android.telephony.ims.FeatureTagState>);
method public void onDestroyed(int);
method public void onFeatureTagRegistrationChanged(@NonNull android.telephony.ims.DelegateRegistrationState);
- method public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+ method @Deprecated public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
}
public final class FeatureTagState implements android.os.Parcelable {
@@ -13564,6 +13563,76 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RtpHeaderExtensionType> CREATOR;
}
+ public final class SipDelegateConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getHomeDomain();
+ method @Nullable public String getImei();
+ method @Nullable public android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration getIpSecConfiguration();
+ method @NonNull public java.net.InetSocketAddress getLocalAddress();
+ method public int getMaxUdpPayloadSizeBytes();
+ method @Nullable public java.net.InetSocketAddress getNatSocketAddress();
+ method @Nullable public String getPrivateUserIdentifier();
+ method @Nullable public android.net.Uri getPublicGruuUri();
+ method @Nullable public String getPublicUserIdentifier();
+ method @Nullable public String getSipAssociatedUriHeader();
+ method @Nullable public String getSipAuthenticationHeader();
+ method @Nullable public String getSipAuthenticationNonce();
+ method @Nullable public String getSipCniHeader();
+ method @Nullable public String getSipContactUserParameter();
+ method @Nullable public String getSipPaniHeader();
+ method @Nullable public String getSipPathHeader();
+ method @Nullable public String getSipPlaniHeader();
+ method @NonNull public java.net.InetSocketAddress getSipServerAddress();
+ method @Nullable public String getSipServiceRouteHeader();
+ method @Nullable public String getSipUserAgentHeader();
+ method public int getTransportType();
+ method @IntRange(from=0) public long getVersion();
+ method public boolean isSipCompactFormEnabled();
+ method public boolean isSipKeepaliveEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateConfiguration> CREATOR;
+ field public static final int SIP_TRANSPORT_TCP = 1; // 0x1
+ field public static final int SIP_TRANSPORT_UDP = 0; // 0x0
+ field public static final int UDP_PAYLOAD_SIZE_UNDEFINED = -1; // 0xffffffff
+ }
+
+ public static final class SipDelegateConfiguration.Builder {
+ ctor public SipDelegateConfiguration.Builder(@IntRange(from=0) long, int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress);
+ ctor public SipDelegateConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateConfiguration);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration build();
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setHomeDomain(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setImei(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setIpSecConfiguration(@Nullable android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setMaxUdpPayloadSizeBytes(@IntRange(from=1) int);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setNatSocketAddress(@Nullable java.net.InetSocketAddress);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setPrivateUserIdentifier(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setPublicGruuUri(@Nullable android.net.Uri);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setPublicUserIdentifier(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipAssociatedUriHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipAuthenticationHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipAuthenticationNonce(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipCniHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipCompactFormEnabled(boolean);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipContactUserParameter(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipKeepaliveEnabled(boolean);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipPaniHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipPathHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipPlaniHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipServiceRouteHeader(@Nullable String);
+ method @NonNull public android.telephony.ims.SipDelegateConfiguration.Builder setSipUserAgentHeader(@Nullable String);
+ }
+
+ public static final class SipDelegateConfiguration.IpSecConfiguration {
+ ctor public SipDelegateConfiguration.IpSecConfiguration(int, int, int, int, int, int, @NonNull String);
+ method public int getLastLocalTxPort();
+ method public int getLastRemoteTxPort();
+ method public int getLocalRxPort();
+ method public int getLocalTxPort();
+ method public int getRemoteRxPort();
+ method public int getRemoteTxPort();
+ method @NonNull public String getSipSecurityVerifyHeader();
+ }
+
public interface SipDelegateConnection {
method public default void cleanupSession(@NonNull String);
method @Deprecated public default void closeDialog(@NonNull String);
@@ -13572,65 +13641,65 @@
method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long);
}
- public final class SipDelegateImsConfiguration implements android.os.Parcelable {
- method public boolean containsKey(@NonNull String);
- method @NonNull public android.os.PersistableBundle copyBundle();
- method public int describeContents();
- method public boolean getBoolean(@NonNull String, boolean);
- method public int getInt(@NonNull String, int);
- method @Nullable public String getString(@NonNull String);
- method public long getVersion();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
- field public static final String IPTYPE_IPV4 = "IPV4";
- field public static final String IPTYPE_IPV6 = "IPV6";
- field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
- field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
- field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
- field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
- field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
- field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
- field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
- field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
- field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
- field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
- field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
- field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
- field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
- field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
- field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
- field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
- field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
- field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
- field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
- field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
- field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
- field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
- field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
- field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
- field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
- field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
- field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
- field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
- field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
- field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
- field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
- field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
- field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
- field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
- field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
- field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
- field public static final String SIP_TRANSPORT_TCP = "TCP";
- field public static final String SIP_TRANSPORT_UDP = "UDP";
+ @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable {
+ method @Deprecated public boolean containsKey(@NonNull String);
+ method @Deprecated @NonNull public android.os.PersistableBundle copyBundle();
+ method @Deprecated public int describeContents();
+ method @Deprecated public boolean getBoolean(@NonNull String, boolean);
+ method @Deprecated public int getInt(@NonNull String, int);
+ method @Deprecated @Nullable public String getString(@NonNull String);
+ method @Deprecated public long getVersion();
+ method @Deprecated public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
+ field @Deprecated public static final String IPTYPE_IPV4 = "IPV4";
+ field @Deprecated public static final String IPTYPE_IPV6 = "IPV6";
+ field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
+ field @Deprecated public static final String SIP_TRANSPORT_TCP = "TCP";
+ field @Deprecated public static final String SIP_TRANSPORT_UDP = "UDP";
}
- public static final class SipDelegateImsConfiguration.Builder {
- ctor public SipDelegateImsConfiguration.Builder(int);
- ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
- method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
- method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
- method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
- method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
+ @Deprecated public static final class SipDelegateImsConfiguration.Builder {
+ ctor @Deprecated public SipDelegateImsConfiguration.Builder(int);
+ ctor @Deprecated public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+ method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
+ method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
+ method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
+ method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
}
public class SipDelegateManager {
@@ -13800,10 +13869,11 @@
}
public interface DelegateConnectionStateCallback {
+ method public default void onConfigurationChanged(@NonNull android.telephony.ims.SipDelegateConfiguration);
method public void onCreated(@NonNull android.telephony.ims.SipDelegateConnection);
method public void onDestroyed(int);
method public void onFeatureTagStatusChanged(@NonNull android.telephony.ims.DelegateRegistrationState, @NonNull java.util.Set<android.telephony.ims.FeatureTagState>);
- method public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+ method @Deprecated public default void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
}
public class ImsCallSessionImplBase implements java.lang.AutoCloseable {
@@ -14454,7 +14524,21 @@
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 5d51ff6..4e929a8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -835,10 +835,8 @@
method public void holdLock(android.os.IBinder, int);
method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>);
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
- field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
- field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
@@ -1063,23 +1061,10 @@
package android.hardware {
public final class SensorPrivacyManager {
- method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
- method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
- method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
- method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyForProfileGroup(int, boolean);
}
- public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
- method public void onSensorPrivacyChanged(boolean);
- }
-
- public static class SensorPrivacyManager.Sensors {
- field public static final int CAMERA = 2; // 0x2
- field public static final int MICROPHONE = 1; // 0x1
- }
-
}
package android.hardware.biometrics {
@@ -2117,6 +2102,7 @@
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";
@@ -2794,7 +2780,6 @@
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/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 28d6fbb..6df9f4d 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -400,8 +400,18 @@
*/
public List<ActivityManager.RunningTaskInfo> getTasks(
int maxNum, boolean filterOnlyVisibleRecents) {
+ return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */);
+ }
+
+ /**
+ * @return List of running tasks that can be filtered by visibility in recents and keep intent
+ * extra.
+ * @hide
+ */
+ public List<ActivityManager.RunningTaskInfo> getTasks(
+ int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
try {
- return getService().getTasks(maxNum, filterOnlyVisibleRecents);
+ return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3a1961a..59f794b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -52,6 +52,7 @@
import android.app.servertransaction.TransactionExecutor;
import android.app.servertransaction.TransactionExecutorHelper;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.AttributionSource;
import android.content.AutofillOptions;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
@@ -2298,6 +2299,12 @@
? am.getApplication().getOpPackageName() : null;
}
+ public static AttributionSource currentAttributionSource() {
+ ActivityThread am = currentActivityThread();
+ return (am != null && am.getApplication() != null)
+ ? am.getApplication().getAttributionSource() : null;
+ }
+
@UnsupportedAppUsage
public static String currentPackageName() {
ActivityThread am = currentActivityThread();
@@ -5961,20 +5968,6 @@
// 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 9753b67..656942d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1218,6 +1218,23 @@
}
@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/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 2f1f14e..b75e89c 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -149,7 +149,8 @@
void setFocusedTask(int taskId);
boolean removeTask(int taskId);
void removeAllVisibleRecentTasks();
- List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents);
+ List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents,
+ boolean keepIntentExtra);
void moveTaskToFront(in IApplicationThread app, in String callingPackage, int task,
int flags, in Bundle options);
ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 1765849..232b077 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -74,6 +74,9 @@
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 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 @@
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 759597c..4c1a363 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -531,7 +531,7 @@
}
final PasswordMetrics enteredMetrics = computeForPasswordOrPin(password, isPin);
- return validatePasswordMetrics(adminMetrics, minComplexity, isPin, enteredMetrics);
+ return validatePasswordMetrics(adminMetrics, minComplexity, enteredMetrics);
}
/**
@@ -539,15 +539,13 @@
*
* @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 @@
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 d351683..8038158 100644
--- a/core/java/android/app/people/ConversationStatus.java
+++ b/core/java/android/app/people/ConversationStatus.java
@@ -142,32 +142,52 @@
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 @@
}
+ /**
+ * 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 e645831..de3eeee 100644
--- a/core/java/android/app/people/PeopleSpaceTile.java
+++ b/core/java/android/app/people/PeopleSpaceTile.java
@@ -54,6 +54,7 @@
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 @@
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 @@
return mNotificationContent;
}
+ public CharSequence getNotificationSender() {
+ return mNotificationSender;
+ }
+
public String getNotificationCategory() {
return mNotificationCategory;
}
@@ -170,7 +176,7 @@
/** 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 c271862..1fb7638 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -23,13 +23,13 @@
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 @@
@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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
&& 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 @@
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 @@
* @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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* @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 @@
* @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 @@
* @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 cbbf4c9..c0a2aa3 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -27,6 +27,7 @@
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 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 @@
* 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 @@
* @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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* @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 @@
* @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 @@
* @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 5704c65..8afc557 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -53,7 +53,6 @@
import android.os.Build;
import android.os.IBinder;
import android.os.ParcelUuid;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -75,6 +74,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
@@ -706,14 +706,15 @@
*/
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 Context mContext;
private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
private final Object mLock = new Object();
@@ -751,32 +752,43 @@
/**
* 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) {
- IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
- if (b != null) {
- IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
- sAdapter = new BluetoothAdapter(managerService);
- } else {
- Log.e(TAG, "Bluetooth binder is null");
- }
+ sAdapter = createAdapter(BluetoothManager.resolveAttributionSource(null));
}
return sAdapter;
}
+ /** {@hide} */
+ public static BluetoothAdapter createAdapter(AttributionSource attributionSource) {
+ IBinder binder = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
+ if (binder != null) {
+ return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder),
+ attributionSource);
+ } else {
+ Log.e(TAG, "Bluetooth binder is null");
+ return null;
+ }
+ }
+
/**
* 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");
}
@@ -788,7 +800,8 @@
} finally {
mServiceLock.writeLock().unlock();
}
- mManagerService = managerService;
+ mManagerService = Objects.requireNonNull(managerService);
+ mAttributionSource = Objects.requireNonNull(attributionSource);
mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
mToken = new Binder();
}
@@ -807,7 +820,9 @@
*/
@RequiresNoPermission
public BluetoothDevice getRemoteDevice(String address) {
- return new BluetoothDevice(address, getAttributionSource());
+ final BluetoothDevice res = new BluetoothDevice(address);
+ res.setAttributionSource(mAttributionSource);
+ return res;
}
/**
@@ -826,9 +841,11 @@
if (address == null || address.length != 6) {
throw new IllegalArgumentException("Bluetooth address must have 6 bytes");
}
- return new BluetoothDevice(
+ final BluetoothDevice res = new BluetoothDevice(
String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1],
- address[2], address[3], address[4], address[5]), getAttributionSource());
+ address[2], address[3], address[4], address[5]));
+ res.setAttributionSource(mAttributionSource);
+ return res;
}
/**
@@ -845,11 +862,11 @@
return null;
}
synchronized (mLock) {
- if (sBluetoothLeAdvertiser == null) {
- sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService);
+ if (mBluetoothLeAdvertiser == null) {
+ mBluetoothLeAdvertiser = new BluetoothLeAdvertiser(this);
}
+ return mBluetoothLeAdvertiser;
}
- return sBluetoothLeAdvertiser;
}
/**
@@ -873,11 +890,11 @@
}
synchronized (mLock) {
- if (sPeriodicAdvertisingManager == null) {
- sPeriodicAdvertisingManager = new PeriodicAdvertisingManager(mManagerService);
+ if (mPeriodicAdvertisingManager == null) {
+ mPeriodicAdvertisingManager = new PeriodicAdvertisingManager(this);
}
+ return mPeriodicAdvertisingManager;
}
- return sPeriodicAdvertisingManager;
}
/**
@@ -889,12 +906,11 @@
return null;
}
synchronized (mLock) {
- if (sBluetoothLeScanner == null) {
- sBluetoothLeScanner =
- new BluetoothLeScanner(mManagerService, getAttributionSource());
+ if (mBluetoothLeScanner == null) {
+ mBluetoothLeScanner = new BluetoothLeScanner(this);
}
+ return mBluetoothLeScanner;
}
- return sBluetoothLeScanner;
}
/**
@@ -965,7 +981,7 @@
}
String packageName = ActivityThread.currentPackageName();
try {
- return mManagerService.disableBle(packageName, mToken);
+ return mManagerService.disableBle(mAttributionSource, mToken);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1012,7 +1028,7 @@
}
String packageName = ActivityThread.currentPackageName();
try {
- return mManagerService.enableBle(packageName, mToken);
+ return mManagerService.enableBle(mAttributionSource, mToken);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1177,7 +1193,7 @@
return true;
}
try {
- return mManagerService.enable(ActivityThread.currentPackageName());
+ return mManagerService.enable(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1210,7 +1226,7 @@
@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);
}
@@ -1230,7 +1246,7 @@
public boolean disable(boolean persist) {
try {
- return mManagerService.disable(ActivityThread.currentPackageName(), persist);
+ return mManagerService.disable(mAttributionSource, persist);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1251,7 +1267,7 @@
})
public String getAddress() {
try {
- return mManagerService.getAddress();
+ return mManagerService.getAddress(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1269,7 +1285,7 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public String getName() {
try {
- return mManagerService.getName();
+ return mManagerService.getName(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1281,7 +1297,7 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public int getNameLengthForAdvertise() {
try {
- return mService.getNameLengthForAdvertise();
+ return mService.getNameLengthForAdvertise(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1300,7 +1316,8 @@
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");
@@ -1330,7 +1347,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getUuids(getAttributionSource());
+ return mService.getUuids(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1364,7 +1381,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.setName(name, getAttributionSource());
+ return mService.setName(name, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1392,7 +1409,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getBluetoothClass(getAttributionSource());
+ return mService.getBluetoothClass(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1448,7 +1465,7 @@
if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
try {
mServiceLock.readLock().lock();
- if (mService != null) return mService.getIoCapability(getAttributionSource());
+ if (mService != null) return mService.getIoCapability(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage(), e);
} finally {
@@ -1501,7 +1518,7 @@
if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
try {
mServiceLock.readLock().lock();
- if (mService != null) return mService.getLeIoCapability(getAttributionSource());
+ if (mService != null) return mService.getLeIoCapability(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage(), e);
} finally {
@@ -1563,7 +1580,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getScanMode(getAttributionSource());
+ return mService.getScanMode(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1612,7 +1629,7 @@
mServiceLock.readLock().lock();
if (mService != null) {
int durationSeconds = Math.toIntExact(durationMillis / 1000);
- return mService.setScanMode(mode, durationSeconds, getAttributionSource());
+ return mService.setScanMode(mode, durationSeconds, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1662,7 +1679,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.setScanMode(mode, getDiscoverableTimeout(), getAttributionSource());
+ return mService.setScanMode(mode, getDiscoverableTimeout(), mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1683,7 +1700,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getDiscoverableTimeout(getAttributionSource());
+ return mService.getDiscoverableTimeout(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1704,7 +1721,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- mService.setDiscoverableTimeout(timeout, getAttributionSource());
+ mService.setDiscoverableTimeout(timeout, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1738,31 +1755,6 @@
}
/**
- * Set the context for this BluetoothAdapter (only called from BluetoothManager)
- * @hide
- */
- @RequiresNoPermission
- public void setContext(Context context) {
- mContext = context;
- }
-
- String getOpPackageName() {
- // Workaround for legacy API for getting a BluetoothAdapter not
- // passing a context
- if (mContext != null) {
- return mContext.getOpPackageName();
- }
- return ActivityThread.currentOpPackageName();
- }
-
- AttributionSource getAttributionSource() {
- if (mContext != null) {
- return mContext.getAttributionSource();
- }
- return new AttributionSource(Process.myUid(), ActivityThread.currentOpPackageName(), null);
- }
-
- /**
* Start the remote device discovery process.
* <p>The discovery process usually involves an inquiry scan of about 12
* seconds, followed by a page scan of each new device to retrieve its
@@ -1802,7 +1794,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.startDiscovery(getAttributionSource());
+ return mService.startDiscovery(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1838,7 +1830,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.cancelDiscovery(getAttributionSource());
+ return mService.cancelDiscovery(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1876,7 +1868,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.isDiscovering(getAttributionSource());
+ return mService.isDiscovering(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1919,7 +1911,7 @@
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);
@@ -1971,7 +1963,7 @@
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);
@@ -2004,7 +1996,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.connectAllEnabledProfiles(device);
+ return mService.connectAllEnabledProfiles(device, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -2036,7 +2028,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.disconnectAllEnabledProfiles(device);
+ return mService.disconnectAllEnabledProfiles(device, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -2313,7 +2305,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getMaxConnectedAudioDevices(getAttributionSource());
+ return mService.getMaxConnectedAudioDevices(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e);
@@ -2341,7 +2333,7 @@
// BLE is not supported
return false;
}
- return (iGatt.numHwTrackFiltersAvailable() != 0);
+ return (iGatt.numHwTrackFiltersAvailable(mAttributionSource) != 0);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -2425,7 +2417,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.getMostRecentlyConnectedDevices(getAttributionSource());
+ return mService.getMostRecentlyConnectedDevices(mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -2455,7 +2447,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return toDeviceSet(mService.getBondedDevices(getAttributionSource()));
+ return toDeviceSet(mService.getBondedDevices(mAttributionSource));
}
return toDeviceSet(new BluetoothDevice[0]);
} catch (RemoteException e) {
@@ -2980,50 +2972,51 @@
}
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;
@@ -3177,11 +3170,11 @@
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();
@@ -3227,7 +3220,7 @@
return true;
}
try {
- return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName());
+ return mManagerService.enableNoAutoConnect(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -3270,7 +3263,7 @@
/**
* Provides callback methods for receiving {@link OobData} from the host stack, as well as an
- * error interface in order to allow the caller to determine next steps based on the {@link
+ * error interface in order to allow the caller to determine next steps based on the {@code
* ErrorCode}.
*
* @hide
@@ -3520,11 +3513,17 @@
&& (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 cac676d..0b43e71 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -22,6 +22,7 @@
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 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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 befb1ca..98823b09 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -47,6 +47,7 @@
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
import java.util.UUID;
/**
@@ -1102,11 +1103,11 @@
*/
private static volatile IBluetooth sService;
- private final AttributionSource mAttributionSource;
-
private final String mAddress;
@AddressType private final int mAddressType;
+ private AttributionSource mAttributionSource;
+
/*package*/
@UnsupportedAppUsage
static IBluetooth getService() {
@@ -1157,7 +1158,8 @@
* @throws IllegalArgumentException address is invalid
* @hide
*/
- public BluetoothDevice(String address, AttributionSource attributionSource) {
+ @UnsupportedAppUsage
+ /*package*/ BluetoothDevice(String address) {
getService(); // ensures sService is initialized
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
throw new IllegalArgumentException(address + " is not a valid Bluetooth address");
@@ -1165,12 +1167,27 @@
mAddress = address;
mAddressType = ADDRESS_TYPE_PUBLIC;
+ mAttributionSource = BluetoothManager.resolveAttributionSource(null);
+ }
+
+ void setAttributionSource(AttributionSource attributionSource) {
mAttributionSource = attributionSource;
}
- @UnsupportedAppUsage
- /*package*/ BluetoothDevice(String address) {
- this(address, BluetoothAdapter.getDefaultAdapter().getAttributionSource());
+ 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
@@ -1208,8 +1225,7 @@
public static final @android.annotation.NonNull Parcelable.Creator<BluetoothDevice> CREATOR =
new Parcelable.Creator<BluetoothDevice>() {
public BluetoothDevice createFromParcel(Parcel in) {
- return new BluetoothDevice(
- in.readString(), in.readParcelable(getClass().getClassLoader()));
+ return new BluetoothDevice(in.readString());
}
public BluetoothDevice[] newArray(int size) {
@@ -1220,7 +1236,6 @@
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(mAddress);
- out.writeParcelable(mAttributionSource, 0);
}
/**
@@ -1361,9 +1376,7 @@
return false;
}
try {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- return service.setRemoteAlias(
- this, alias, adapter.getOpPackageName(), mAttributionSource);
+ return service.setRemoteAlias(this, alias, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -2513,7 +2526,8 @@
// 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 9d3eed8..aea8210 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -22,6 +22,7 @@
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 @@
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 @@
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 @@
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 @@
? 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 @@
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 @@
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 @@
}
};
- /*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 @@
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 @@
try {
mCallback = null;
- mService.unregisterClient(mClientIf);
+ mService.unregisterClient(mClientIf, mAttributionSource);
mClientIf = 0;
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -958,7 +964,7 @@
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 @@
@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 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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 865f476..3e799de 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -21,6 +21,7 @@
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 @@
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 @@
/**
* 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 @@
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 @@
try {
mCallback = null;
- mService.unregisterServer(mServerIf);
+ mService.unregisterServer(mServerIf, mAttributionSource);
mServerIf = 0;
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -576,7 +582,8 @@
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 @@
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 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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 51ef3c2..9dc2d8e 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -28,6 +28,7 @@
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 @@
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 @@
/**
* 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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
* @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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 840b4d3..0059cdb 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -23,6 +23,7 @@
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 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 @@
/**
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 fa52eda..3ff2ebd 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -28,6 +28,7 @@
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 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 @@
* 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 @@
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 @@
* @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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
&& 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 @@
* @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 @@
* @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 @@
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 @@
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 @@
* @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 @@
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 @@
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 @@
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 6565ec0..11e5711 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -25,6 +25,7 @@
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 @@
}
}
- 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 @@
}
};
- 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
if (service != null) {
try {
- return service.getUserAppName();
+ return service.getUserAppName(mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -705,7 +711,7 @@
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 @@
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 @@
&& 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 68a9d37..0abe18c 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -26,6 +26,7 @@
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 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 @@
* 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 @@
* @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 @@
* @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 @@
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 @@
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 @@
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 @@
* @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 @@
* @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 @@
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 @@
* @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 @@
* @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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 462c7b7..51bfd04 100644
--- a/core/java/android/bluetooth/BluetoothLeAudio.java
+++ b/core/java/android/bluetooth/BluetoothLeAudio.java
@@ -26,6 +26,7 @@
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 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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
&& 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 @@
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 2374f1c..b13ccaf 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -16,19 +16,18 @@
package android.bluetooth;
-import android.Manifest;
import android.annotation.RequiresFeature;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
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.IBinder;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.Log;
import java.util.ArrayList;
@@ -60,34 +59,41 @@
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) {
- if (context.getAttributionTag() == null) {
- context = context.getApplicationContext();
- if (context == null) {
- throw new IllegalArgumentException(
- "context not associated with any application (using a mock context?)");
- }
+ mAttributionSource = resolveAttributionSource(context);
+ mAdapter = BluetoothAdapter.createAdapter(mAttributionSource);
+ }
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- } else {
- IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
- if (b != null) {
- mAdapter = new BluetoothAdapter(IBluetoothManager.Stub.asInterface(b));
- } else {
- Log.e(TAG, "Bluetooth binder is null");
- mAdapter = null;
+ /** {@hide} */
+ public static AttributionSource resolveAttributionSource(Context context) {
+ AttributionSource res = null;
+ if (context != null) {
+ 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) {
}
}
-
- // Context is not initialized in constructor
- if (mAdapter != null) {
- mAdapter.setContext(context);
+ if (res == null) {
+ throw new IllegalStateException("Failed to resolve AttributionSource");
}
+ return res;
}
/**
@@ -148,24 +154,9 @@
@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
+ });
}
/**
@@ -202,7 +193,9 @@
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);
}
@@ -305,7 +298,8 @@
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 a025d9b..88505b5 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -26,6 +26,7 @@
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 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 @@
/**
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 d72081c..14804db 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -21,12 +21,12 @@
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 @@
/** @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 @@
/**
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 c41c9de..90c94de 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -22,11 +22,12 @@
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 @@
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 @@
* @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 @@
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 @@
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 @@
&& 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 @@
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 @@
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 @@
*/
@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 @@
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 @@
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 ef6fddf..2600029 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -25,6 +25,7 @@
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 @@
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 @@
*
* @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 @@
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 @@
*/
@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 @@
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 @@
&& 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 @@
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 7f48638..3ebd8fe 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -23,6 +23,7 @@
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 @@
/** 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 @@
/**
* 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 @@
*
* @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 @@
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 @@
*
* @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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
* @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 @@
* @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 @@
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 @@
* @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 @@
* @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 @@
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 b86857f..0631abd 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -20,11 +20,11 @@
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 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 @@
/**
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 d7e48ca..caa91fb 100644
--- a/core/java/android/bluetooth/le/AdvertisingSet.java
+++ b/core/java/android/bluetooth/le/AdvertisingSet.java
@@ -18,12 +18,12 @@
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 @@
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 @@
int maxExtendedAdvertisingEvents) {
try {
mGatt.enableAdvertisingSet(mAdvertiserId, enable, duration,
- maxExtendedAdvertisingEvents);
+ maxExtendedAdvertisingEvents, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
@@ -98,7 +99,7 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 ff279d8..5802974 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -26,6 +26,7 @@
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 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 @@
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 @@
* @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 @@
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 @@
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 @@
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 f27f22b..60d4e2d 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -41,6 +41,7 @@
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,12 +81,13 @@
*/
public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
+ private final BluetoothAdapter mBluetoothAdapter;
private final IBluetoothManager mBluetoothManager;
- private final Handler mHandler;
- private BluetoothAdapter mBluetoothAdapter;
- private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
private final AttributionSource mAttributionSource;
+ private final Handler mHandler;
+ private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
+
/**
* Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead.
*
@@ -94,13 +96,12 @@
* @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 @@
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 @@
IBluetoothGatt gatt;
try {
gatt = mBluetoothManager.getBluetoothGatt();
- gatt.stopScanForIntent(callbackIntent);
+ gatt.stopScanForIntent(callbackIntent, mAttributionSource);
} catch (RemoteException e) {
}
}
@@ -420,7 +422,7 @@
// 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 @@
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 @@
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 @@
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 26978e3..47f47bb 100644
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
@@ -25,6 +25,7 @@
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 java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Objects;
/**
* This class provides methods to perform periodic advertising related
@@ -54,8 +56,9 @@
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 @@
* @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 @@
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 @@
}
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 2c155d58..7ab731f 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -31,10 +31,9 @@
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 @@
* 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 @@
* 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>> {
+ private final @NonNull AttributionSourceState mAttributionSourceState;
- @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;
-
- /**
- * 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 @@
@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 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 @@
/*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 @@
* 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 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 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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
@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 @@
* @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 @@
/**
* 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 @@
* @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 @NonNull Builder setPackageName(@Nullable String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2;
- mPackageName = value;
+ mAttributionSourceState.packageName = value;
return this;
}
@@ -545,7 +410,7 @@
public @NonNull Builder setAttributionTag(@Nullable String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
- mAttributionTag = value;
+ mAttributionSourceState.attributionTag = value;
return this;
}
@@ -578,7 +443,8 @@
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 @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 @@
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 @@
}
}
}
-
-
- //@formatter:on
- // End of generated code
-
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 13cc748..6a22491 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2219,6 +2219,26 @@
}
/**
+ * 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 dddcbea..6324d0e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -500,6 +500,13 @@
/** @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/OWNERS b/core/java/android/content/OWNERS
index 1735aa2..8ad1349 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 5089f30..66e0883 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 @@
* @hide
*/
public final class PermissionChecker {
- private static final String LOG_TAG = PermissionChecker.class.getName();
+ /**
+ * The permission is granted.
+ *
+ * @hide
+ */
+ public static final int PERMISSION_GRANTED = IPermissionChecker.PERMISSION_GRANTED;
- private static final String PLATFORM_PACKAGE_NAME = "android";
-
- /** The permission is granted. */
- public static final int PERMISSION_GRANTED = AppOpsManager.MODE_ALLOWED;
-
- /** 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;
-
- /** Returned when:
+ /**
+ * The permission is denied. Applicable only to runtime and app op permissions.
+ *
+ * <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 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_HARD_DENIED = AppOpsManager.MODE_ERRORED;
+ public static final int PERMISSION_SOFT_DENIED = IPermissionChecker.PERMISSION_SOFT_DENIED;
+
+ /**
+ * The permission is denied.
+ *
+ * <p>Returned when:
+ * <ul>
+ * <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 = 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 @@
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionResult {}
+ private static volatile IPermissionChecker sService;
+
private PermissionChecker() {
/* do nothing */
}
@@ -232,7 +242,7 @@
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 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 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 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 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 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 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 @@
@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 @@
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;
- }
+ 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);
}
-
- 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;
- }
- 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 7fe2a41..5e72325 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -796,6 +796,10 @@
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/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 67ecb7c..90105d3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3654,32 +3654,6 @@
public static final String FEATURE_TUNER = "android.hardware.tv.tuner";
/**
- * Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for
- * microphone. When sensory privacy for the microphone is enabled no microphone data is sent to
- * clients, e.g. all audio data is silent.
- *
- * @hide
- */
- @SystemApi
- @TestApi
- @SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
-
- /**
- * Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for
- * camera. When sensory privacy for the camera is enabled no camera data is sent to clients,
- * e.g. the view finder in a camera app would appear blank.
- *
- * @hide
- */
- @SystemApi
- @TestApi
- @SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
-
- /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
* the necessary changes to support app enumeration.
*
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 55a6ab7..84317b3 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -81,6 +81,7 @@
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 @@
if (o.componentLabelIconOverrideMap != null) {
this.componentLabelIconOverrideMap = new ArrayMap<>(o.componentLabelIconOverrideMap);
}
+ splashScreenTheme = o.splashScreenTheme;
}
@Nullable
@@ -242,6 +244,7 @@
return componentLabelIconOverrideMap.get(componentName);
}
+
/**
* Test if this package is installed.
*/
@@ -479,7 +482,11 @@
}
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 @@
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/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl
index dbcd79d..a71bb09 100644
--- a/core/java/android/hardware/ISensorPrivacyManager.aidl
+++ b/core/java/android/hardware/ISensorPrivacyManager.aidl
@@ -24,6 +24,8 @@
// the ones in
// frameworks/native/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
// =============== Beginning of transactions used on native side as well ======================
+ boolean supportsSensorToggle(int sensor);
+
void addSensorPrivacyListener(in ISensorPrivacyListener listener);
void addIndividualSensorPrivacyListener(int userId, int sensor,
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 85e7d77..fc0204a 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -16,6 +16,7 @@
package android.hardware;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -30,6 +31,8 @@
import android.os.ServiceManager;
import android.service.SensorPrivacyIndividualEnabledSensorProto;
import android.util.ArrayMap;
+import android.util.Pair;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -43,24 +46,11 @@
* current state of sensor privacy as well as to register / unregister for notification when
* the sensor privacy state changes.
*
- * @hide
*/
-@SystemApi
-@TestApi
@SystemService(Context.SENSOR_PRIVACY_SERVICE)
public final class SensorPrivacyManager {
/**
- * @hide
- */
- public static final boolean USE_MICROPHONE_TOGGLE = true;
-
- /**
- * @hide
- */
- public static final boolean USE_CAMERA_TOGGLE = true;
-
- /**
* Unique Id of this manager to identify to the service
* @hide
*/
@@ -80,25 +70,23 @@
public static final String EXTRA_ALL_SENSORS = SensorPrivacyManager.class.getName()
+ ".extra.all_sensors";
+ private final SparseArray<Boolean> mToggleSupportCache = new SparseArray<>();
+
/**
* Individual sensors not listed in {@link Sensors}
- * @hide
*/
- @SystemApi
- @TestApi
public static class Sensors {
private Sensors() {}
- /** Microphone
- * @hide */
- @SystemApi
- @TestApi
+ /**
+ * Constant for the microphone
+ */
public static final int MICROPHONE = SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
- /** Camera
- * @hide */
- @SystemApi
- @TestApi
+
+ /**
+ * Constant for the camera
+ */
public static final int CAMERA = SensorPrivacyIndividualEnabledSensorProto.CAMERA;
/**
@@ -122,14 +110,14 @@
* @hide
*/
@SystemApi
- @TestApi
public interface OnSensorPrivacyChangedListener {
/**
* Callback invoked when the sensor privacy state changes.
*
+ * @param sensor the sensor whose state is changing
* @param enabled true if sensor privacy is enabled, false otherwise.
*/
- void onSensorPrivacyChanged(boolean enabled);
+ void onSensorPrivacyChanged(int sensor, boolean enabled);
}
private static final Object sInstanceLock = new Object();
@@ -144,7 +132,11 @@
private final ISensorPrivacyManager mService;
@NonNull
- private final ArrayMap<OnSensorPrivacyChangedListener, ISensorPrivacyListener> mListeners;
+ private final ArrayMap<OnAllSensorPrivacyChangedListener, ISensorPrivacyListener> mListeners;
+
+ @NonNull
+ private final ArrayMap<Pair<OnSensorPrivacyChangedListener, Integer>, ISensorPrivacyListener>
+ mIndividualListeners;
/**
* Private constructor to ensure only a single instance is created.
@@ -153,6 +145,7 @@
mContext = context;
mService = service;
mListeners = new ArrayMap<>();
+ mIndividualListeners = new ArrayMap<>();
}
/**
@@ -176,16 +169,18 @@
}
/**
- * Sets sensor privacy to the specified state.
- *
- * @param enable the state to which sensor privacy should be set.
- *
- * @hide
+ * Checks if the given toggle is supported on this device
+ * @param sensor The sensor to check
+ * @return whether the toggle for the sensor is supported on this device.
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
- public void setSensorPrivacy(boolean enable) {
+ public boolean supportsSensorToggle(@Sensors.Sensor int sensor) {
try {
- mService.setSensorPrivacy(enable);
+ Boolean val = mToggleSupportCache.get(sensor);
+ if (val == null) {
+ val = mService.supportsSensorToggle(sensor);
+ mToggleSupportCache.put(sensor, val);
+ }
+ return val;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -195,37 +190,6 @@
* Registers a new listener to receive notification when the state of sensor privacy
* changes.
*
- * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
- * privacy changes.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
- public void addSensorPrivacyListener(@NonNull final OnSensorPrivacyChangedListener listener) {
- synchronized (mListeners) {
- ISensorPrivacyListener iListener = mListeners.get(listener);
- if (iListener == null) {
- iListener = new ISensorPrivacyListener.Stub() {
- @Override
- public void onSensorPrivacyChanged(boolean enabled) {
- listener.onSensorPrivacyChanged(enabled);
- }
- };
- mListeners.put(listener, iListener);
- }
-
- try {
- mService.addSensorPrivacyListener(iListener);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- /**
- * Registers a new listener to receive notification when the state of sensor privacy
- * changes.
- *
* @param sensor the sensor to listen to changes to
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
* privacy changes.
@@ -233,8 +197,7 @@
* @hide
*/
@SystemApi
- @TestApi
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor,
@NonNull OnSensorPrivacyChangedListener listener) {
addSensorPrivacyListener(sensor, mContext.getUserId(), mContext.getMainExecutor(),
@@ -252,7 +215,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @UserIdInt int userId,
@NonNull OnSensorPrivacyChangedListener listener) {
addSensorPrivacyListener(sensor, userId, mContext.getMainExecutor(), listener);
@@ -270,8 +233,7 @@
* @hide
*/
@SystemApi
- @TestApi
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor,
@NonNull OnSensorPrivacyChangedListener listener) {
addSensorPrivacyListener(sensor, mContext.getUserId(), executor, listener);
@@ -289,19 +251,19 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @UserIdInt int userId,
@NonNull Executor executor, @NonNull OnSensorPrivacyChangedListener listener) {
- synchronized (mListeners) {
- ISensorPrivacyListener iListener = mListeners.get(listener);
+ synchronized (mIndividualListeners) {
+ ISensorPrivacyListener iListener = mIndividualListeners.get(listener);
if (iListener == null) {
iListener = new ISensorPrivacyListener.Stub() {
@Override
public void onSensorPrivacyChanged(boolean enabled) {
- executor.execute(() -> listener.onSensorPrivacyChanged(enabled));
+ executor.execute(() -> listener.onSensorPrivacyChanged(sensor, enabled));
}
};
- mListeners.put(listener, iListener);
+ mIndividualListeners.put(new Pair<>(listener, sensor), iListener);
}
try {
@@ -314,7 +276,7 @@
}
/**
- * Unregisters the specified listener from receiving notifications when the state of sensor
+ * Unregisters the specified listener from receiving notifications when the state of any sensor
* privacy changes.
*
* @param listener the OnSensorPrivacyChangedListener to be unregistered from notifications when
@@ -323,39 +285,24 @@
* @hide
*/
@SystemApi
- @TestApi
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void removeSensorPrivacyListener(@NonNull OnSensorPrivacyChangedListener listener) {
synchronized (mListeners) {
- ISensorPrivacyListener iListener = mListeners.get(listener);
- if (iListener != null) {
- mListeners.remove(iListener);
- try {
- mService.removeSensorPrivacyListener(iListener);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ for (int i = 0; i < mIndividualListeners.size(); i++) {
+ Pair<OnSensorPrivacyChangedListener, Integer> pair = mIndividualListeners.keyAt(i);
+ if (pair.first.equals(listener)) {
+ try {
+ mService.removeSensorPrivacyListener(mIndividualListeners.valueAt(i));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mIndividualListeners.removeAt(i--);
}
}
}
}
/**
- * Returns whether sensor privacy is currently enabled.
- *
- * @return true if sensor privacy is currently enabled, false otherwise.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
- public boolean isSensorPrivacyEnabled() {
- try {
- return mService.isSensorPrivacyEnabled();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Returns whether sensor privacy is currently enabled for a specific sensor.
*
* @return true if sensor privacy is currently enabled, false otherwise.
@@ -363,8 +310,7 @@
* @hide
*/
@SystemApi
- @TestApi
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
return isSensorPrivacyEnabled(sensor, mContext.getUserId());
}
@@ -376,7 +322,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor, @UserIdInt int userId) {
try {
return mService.isIndividualSensorPrivacyEnabled(userId, sensor);
@@ -394,7 +340,7 @@
* @hide
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacy(@Sensors.Sensor int sensor, boolean enable) {
setSensorPrivacy(sensor, enable, mContext.getUserId());
}
@@ -408,7 +354,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacy(@Sensors.Sensor int sensor, boolean enable,
@UserIdInt int userId) {
try {
@@ -428,7 +374,7 @@
* @hide
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacyForProfileGroup(@Sensors.Sensor int sensor,
boolean enable) {
setSensorPrivacyForProfileGroup(sensor, enable, mContext.getUserId());
@@ -444,7 +390,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacyForProfileGroup(@Sensors.Sensor int sensor,
boolean enable, @UserIdInt int userId) {
try {
@@ -463,7 +409,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void suppressSensorPrivacyReminders(@NonNull String packageName,
boolean suppress) {
suppressSensorPrivacyReminders(packageName, suppress, mContext.getUserId());
@@ -478,7 +424,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void suppressSensorPrivacyReminders(@NonNull String packageName,
boolean suppress, @UserIdInt int userId) {
try {
@@ -488,4 +434,109 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * A class implementing this interface can register with the {@link
+ * android.hardware.SensorPrivacyManager} to receive notification when the all-sensor privacy
+ * state changes.
+ *
+ * @hide
+ */
+ public interface OnAllSensorPrivacyChangedListener {
+ /**
+ * Callback invoked when the sensor privacy state changes.
+ *
+ * @param enabled true if sensor privacy is enabled, false otherwise.
+ */
+ void onAllSensorPrivacyChanged(boolean enabled);
+ }
+
+ /**
+ * Sets all-sensor privacy to the specified state.
+ *
+ * @param enable the state to which sensor privacy should be set.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ public void setAllSensorPrivacy(boolean enable) {
+ try {
+ mService.setSensorPrivacy(enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a new listener to receive notification when the state of all-sensor privacy
+ * changes.
+ *
+ * @param listener the OnSensorPrivacyChangedListener to be notified when the state of
+ * all-sensor privacy changes.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public void addAllSensorPrivacyListener(
+ @NonNull final OnAllSensorPrivacyChangedListener listener) {
+ synchronized (mListeners) {
+ ISensorPrivacyListener iListener = mListeners.get(listener);
+ if (iListener == null) {
+ iListener = new ISensorPrivacyListener.Stub() {
+ @Override
+ public void onSensorPrivacyChanged(boolean enabled) {
+ listener.onAllSensorPrivacyChanged(enabled);
+ }
+ };
+ mListeners.put(listener, iListener);
+ }
+
+ try {
+ mService.addSensorPrivacyListener(iListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters the specified listener from receiving notifications when the state of all-sensor
+ * privacy changes.
+ *
+ * @param listener the OnAllSensorPrivacyChangedListener to be unregistered from notifications
+ * when all-sensor privacy changes.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public void removeAllSensorPrivacyListener(
+ @NonNull OnAllSensorPrivacyChangedListener listener) {
+ synchronized (mListeners) {
+ ISensorPrivacyListener iListener = mListeners.get(listener);
+ if (iListener != null) {
+ mListeners.remove(iListener);
+ try {
+ mService.removeSensorPrivacyListener(iListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns whether all-sensor privacy is currently enabled.
+ *
+ * @return true if all-sensor privacy is currently enabled, false otherwise.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public boolean isAllSensorPrivacyEnabled() {
+ try {
+ return mService.isSensorPrivacyEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index 4385b1da..83e273a 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 @@
// 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 @@
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 @@
//
/**
+ * @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 @@
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 30e24d2e..79f716c 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -57,7 +57,10 @@
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 @@
* 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 @@
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 @@
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 @@
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/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 6654c2c..0a12470 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2513,7 +2513,7 @@
* 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 @@
* </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 @@
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 fd10c57..80b5078 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -73,6 +73,9 @@
* {@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 @@
* @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 c1009ff..651f025 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -379,17 +379,36 @@
* <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 @@
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 @@
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 bb3d91d..3af1b5b 100644
--- a/core/java/android/hardware/camera2/MultiResolutionImageReader.java
+++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
@@ -265,8 +265,18 @@
* @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 aa84b02..2e841f5 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1322,7 +1322,10 @@
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 d63683f..8dfc0a7b 100644
--- a/core/java/android/hardware/camera2/params/InputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/InputConfiguration.java
@@ -90,7 +90,6 @@
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 84736dc..0662f16 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -353,7 +353,10 @@
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 0000000..5da55c3
--- /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 0000000..4289860
--- /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 6c2d140..de32adb1 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -418,6 +418,7 @@
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 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 @@
* @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 983a43a..df51734 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -79,6 +79,7 @@
EVENT_DISPLAY_ADDED,
EVENT_DISPLAY_CHANGED,
EVENT_DISPLAY_REMOVED,
+ EVENT_DISPLAY_BRIGHTNESS_CHANGED
})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayEvent {}
@@ -86,6 +87,7 @@
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 @@
}
/**
+ * 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 @@
}
}
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 5ca4e0c..2303353 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -19,6 +19,7 @@
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 @@
// 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 13e2700..5f87899 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -133,11 +133,11 @@
}
@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 @@
* @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 @@
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 @@
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 f0f975d..9e62ca5 100644
--- a/core/java/android/hardware/face/FaceServiceReceiver.java
+++ b/core/java/android/hardware/face/FaceServiceReceiver.java
@@ -66,7 +66,8 @@
}
@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 2ef1430..0ccb395 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -32,7 +32,7 @@
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 be10df1..0b4d9d9 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 6a0772d..88d5ba8 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.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 @@
}
/**
+ * 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 @@
}
/**
+ * 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 4bb3ab6..3bceacb 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -156,6 +156,6 @@
// 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 a4d34af..56dba7e 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 @@
/**
* 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 e55b8c56..5c93672 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 336fbf2..19fb845 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -117,6 +117,11 @@
// 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 b6d2eaf..648fda7 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1293,7 +1293,7 @@
* @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 @@
* 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 @@
}
}
+ /**
+ * 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 33527f8..0b4d9d9 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/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 043a22b..9ec6938 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -6076,7 +6076,7 @@
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/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 34f2c103f..9a81942 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -100,7 +100,7 @@
boolean isProfile(int userId);
boolean isManagedProfile(int userId);
boolean isCloneProfile(int userId);
- boolean sharesMediaWithParent(int userId);
+ boolean isMediaSharedWithParent(int userId);
boolean isDemoUser(int userId);
boolean isPreCreated(int userId);
UserInfo createProfileForUserEvenWhenDisallowedWithThrow(in String name, in String userType, int flags,
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d4de4fa..b8ad068 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4124,9 +4124,9 @@
Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
@UserHandleAware
@SuppressAutoDoc
- public boolean sharesMediaWithParent() {
+ public boolean isMediaSharedWithParent() {
try {
- return mService.sharesMediaWithParent(mUserId);
+ return mService.isMediaSharedWithParent(mUserId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 2876775..95962c8 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -552,6 +552,30 @@
}
/**
+ * Query the estimated durations of the given primitives.
+ *
+ * <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.
+ */
+ @NonNull
+ public int[] getPrimitiveDurations(
+ @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
+ VibratorInfo info = getInfo();
+ int[] durations = new int[primitiveIds.length];
+ for (int i = 0; i < primitiveIds.length; i++) {
+ durations[i] = info.getPrimitiveDuration(primitiveIds[i]);
+ }
+ return durations;
+ }
+
+ /**
* Turn the vibrator off.
*/
@RequiresPermission(android.Manifest.permission.VIBRATE)
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index c7d66f0..d73469c 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -25,6 +25,7 @@
import android.util.MathUtils;
import android.util.Range;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import java.util.ArrayList;
import java.util.Arrays;
@@ -51,7 +52,7 @@
@Nullable
private final SparseBooleanArray mSupportedBraking;
@Nullable
- private final SparseBooleanArray mSupportedPrimitives;
+ private final SparseIntArray mSupportedPrimitives;
private final float mQFactor;
private final FrequencyMapping mFrequencyMapping;
@@ -60,19 +61,37 @@
mCapabilities = in.readLong();
mSupportedEffects = in.readSparseBooleanArray();
mSupportedBraking = in.readSparseBooleanArray();
- mSupportedPrimitives = in.readSparseBooleanArray();
+ mSupportedPrimitives = in.readSparseIntArray();
mQFactor = in.readFloat();
mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
}
- /** @hide */
+ /**
+ * Default constructor.
+ *
+ * @param id The vibrator id.
+ * @param capabilities All capability flags of the vibrator, defined in IVibrator.CAP_*.
+ * @param supportedEffects All supported predefined effects, enum values from {@link
+ * android.hardware.vibrator.Effect}.
+ * @param supportedBraking All supported braking types, enum values from {@link Braking}.
+ * @param supportedPrimitives All supported primitive effects, enum values from {@link
+ * android.hardware.vibrator.CompositePrimitive}.
+ * @param primitiveDurations A mapping of primitive durations, where indexes are enum values
+ * from {@link android.hardware.vibrator.CompositePrimitive} and the
+ * values are estimated durations in milliseconds.
+ * @param qFactor The vibrator quality factor.
+ * @param frequencyMapping The description of the vibrator supported frequencies and max
+ * amplitude mappings.
+ * @hide
+ */
public VibratorInfo(int id, long capabilities, int[] supportedEffects, int[] supportedBraking,
- int[] supportedPrimitives, float qFactor, @NonNull FrequencyMapping frequencyMapping) {
+ int[] supportedPrimitives, int[] primitiveDurations, float qFactor,
+ @NonNull FrequencyMapping frequencyMapping) {
mId = id;
mCapabilities = capabilities;
mSupportedEffects = toSparseBooleanArray(supportedEffects);
mSupportedBraking = toSparseBooleanArray(supportedBraking);
- mSupportedPrimitives = toSparseBooleanArray(supportedPrimitives);
+ mSupportedPrimitives = toSparseIntArray(supportedPrimitives, primitiveDurations);
mQFactor = qFactor;
mFrequencyMapping = frequencyMapping;
}
@@ -100,7 +119,7 @@
dest.writeLong(mCapabilities);
dest.writeSparseBooleanArray(mSupportedEffects);
dest.writeSparseBooleanArray(mSupportedBraking);
- dest.writeSparseBooleanArray(mSupportedPrimitives);
+ dest.writeSparseIntArray(mSupportedPrimitives);
dest.writeFloat(mQFactor);
dest.writeParcelable(mFrequencyMapping, flags);
}
@@ -119,18 +138,41 @@
return false;
}
VibratorInfo that = (VibratorInfo) o;
+ if (mSupportedPrimitives == null || that.mSupportedPrimitives == null) {
+ if (mSupportedPrimitives != that.mSupportedPrimitives) {
+ return false;
+ }
+ } else {
+ if (mSupportedPrimitives.size() != that.mSupportedPrimitives.size()) {
+ return false;
+ }
+ for (int i = 0; i < mSupportedPrimitives.size(); i++) {
+ if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
+ return false;
+ }
+ if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
+ return false;
+ }
+ }
+ }
return mId == that.mId && mCapabilities == that.mCapabilities
&& Objects.equals(mSupportedEffects, that.mSupportedEffects)
&& Objects.equals(mSupportedBraking, that.mSupportedBraking)
- && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives)
&& Objects.equals(mQFactor, that.mQFactor)
&& Objects.equals(mFrequencyMapping, that.mFrequencyMapping);
}
@Override
public int hashCode() {
- return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
- mSupportedPrimitives, mQFactor, mFrequencyMapping);
+ int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
+ mQFactor, mFrequencyMapping);
+ if (mSupportedPrimitives != null) {
+ for (int i = 0; i < mSupportedPrimitives.size(); i++) {
+ hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
+ hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
+ }
+ }
+ return hashCode;
}
@Override
@@ -206,7 +248,19 @@
public boolean isPrimitiveSupported(
@VibrationEffect.Composition.PrimitiveType int primitiveId) {
return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) && mSupportedPrimitives != null
- && mSupportedPrimitives.get(primitiveId);
+ && (mSupportedPrimitives.indexOfKey(primitiveId) >= 0);
+ }
+
+ /**
+ * Query the estimated duration of given primitive.
+ *
+ * @param primitiveId Which primitives to query for.
+ * @return The duration in milliseconds estimated for the primitive, or zero if primitive not
+ * supported.
+ */
+ public int getPrimitiveDuration(
+ @VibrationEffect.Composition.PrimitiveType int primitiveId) {
+ return mSupportedPrimitives.get(primitiveId);
}
/**
@@ -364,14 +418,37 @@
return names;
}
+ /**
+ * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is mapped
+ * to {@code true}.
+ */
@Nullable
- private static SparseBooleanArray toSparseBooleanArray(int[] values) {
- if (values == null) {
+ private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
+ if (supportedKeys == null) {
return null;
}
SparseBooleanArray array = new SparseBooleanArray();
- for (int value : values) {
- array.put(value, true);
+ for (int key : supportedKeys) {
+ array.put(key, true);
+ }
+ return array;
+ }
+
+ /**
+ * Create a {@link SparseIntArray} from given {@code supportedKeys} where each key is mapped
+ * to the value indexed by it.
+ *
+ * <p>If {@code values} is null or does not contain a given key as a index, then zero is stored
+ * to the sparse array so it can still be used to query the supported keys.
+ */
+ @Nullable
+ private static SparseIntArray toSparseIntArray(int[] supportedKeys, int[] values) {
+ if (supportedKeys == null) {
+ return null;
+ }
+ SparseIntArray array = new SparseIntArray();
+ for (int key : supportedKeys) {
+ array.put(key, (values == null || key >= values.length) ? 0 : values[key]);
}
return array;
}
@@ -419,7 +496,20 @@
in.createFloatArray());
}
- /** @hide */
+ /**
+ * Default constructor.
+ *
+ * @param minFrequencyHz Minimum supported frequency, in hertz.
+ * @param resonantFrequencyHz The vibrator resonant frequency, in hertz.
+ * @param frequencyResolutionHz The frequency resolution, in hertz, used by the max
+ * amplitudes mapping.
+ * @param suggestedSafeRangeHz The suggested range, in hertz, for the safe relative
+ * frequency range represented by [-1, 1].
+ * @param maxAmplitudes The max amplitude supported by each supported frequency,
+ * starting at minimum frequency with jumps of frequency
+ * resolution.
+ * @hide
+ */
public FrequencyMapping(float minFrequencyHz, float resonantFrequencyHz,
float frequencyResolutionHz, float suggestedSafeRangeHz, float[] maxAmplitudes) {
mMinFrequencyHz = minFrequencyHz;
@@ -547,8 +637,10 @@
@Override
public int hashCode() {
- return Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz, mFrequencyResolutionHz,
- mSuggestedSafeRangeHz, mMaxAmplitudes);
+ int hashCode = Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz,
+ mFrequencyResolutionHz, mSuggestedSafeRangeHz);
+ hashCode = 31 * hashCode + Arrays.hashCode(mMaxAmplitudes);
+ return hashCode;
}
@Override
@@ -587,6 +679,7 @@
private int[] mSupportedEffects = null;
private int[] mSupportedBraking = null;
private int[] mSupportedPrimitives = null;
+ private int[] mPrimitiveDurations = new int[0];
private float mQFactor = Float.NaN;
private FrequencyMapping mFrequencyMapping =
new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
@@ -627,6 +720,16 @@
return this;
}
+ /** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */
+ @NonNull
+ public Builder setPrimitiveDuration(int primitiveId, int duration) {
+ if (mPrimitiveDurations.length <= primitiveId) {
+ mPrimitiveDurations = Arrays.copyOf(mPrimitiveDurations, primitiveId + 1);
+ }
+ mPrimitiveDurations[primitiveId] = duration;
+ return this;
+ }
+
/** Configure the vibrator quality factor. */
@NonNull
public Builder setQFactor(float qFactor) {
@@ -645,7 +748,7 @@
@NonNull
public VibratorInfo build() {
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
- mSupportedPrimitives, mQFactor, mFrequencyMapping);
+ mSupportedPrimitives, mPrimitiveDurations, mQFactor, mFrequencyMapping);
}
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 17c90d6..64d5d9c 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -20,6 +20,7 @@
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.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.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 f8991ce..55cdb26 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -83,6 +83,7 @@
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 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 297f00a4..e9dd2c3b 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 @@
* @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 17cb3da..e7234cc 100644
--- a/core/java/android/service/translation/TranslationService.java
+++ b/core/java/android/service/translation/TranslationService.java
@@ -41,6 +41,7 @@
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 com.android.internal.os.IResultReceiver;
+import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
@@ -84,15 +86,17 @@
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 @@
@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 3c53e8f..bacc6ec 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -349,7 +349,7 @@
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 f5d796f..e50de1c 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -21,6 +21,7 @@
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 @@
}
/**
+ * 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 @@
/**
* 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 @@
@DataClass.Generated.Member
/* package-private */ HotwordDetectedResult(
@HotwordDetector.HotwordConfidenceLevelValue int confidenceLevel,
+ @Nullable MediaSyncEvent mediaSyncEvent,
int byteOffset,
int score,
int personalizedScore,
@@ -167,6 +177,7 @@
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 @@
}
/**
+ * 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 @@
*
* <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 @@
return "HotwordDetectedResult { " +
"confidenceLevel = " + mConfidenceLevel + ", " +
+ "mediaSyncEvent = " + mMediaSyncEvent + ", " +
"byteOffset = " + mByteOffset + ", " +
"score = " + mScore + ", " +
"personalizedScore = " + mPersonalizedScore + ", " +
@@ -273,6 +298,7 @@
//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 @@
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 @@
// 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 @@
// 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 @@
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 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 @@
}
/**
+ * 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 @@
@DataClass.Generated.Member
public @NonNull Builder setScore(int value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x4;
+ mBuilderFieldsSet |= 0x8;
mScore = value;
return this;
}
@@ -420,7 +468,7 @@
@DataClass.Generated.Member
public @NonNull Builder setPersonalizedScore(int value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x8;
+ mBuilderFieldsSet |= 0x10;
mPersonalizedScore = value;
return this;
}
@@ -433,7 +481,7 @@
@DataClass.Generated.Member
public @NonNull Builder setHotwordPhraseId(int value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x10;
+ mBuilderFieldsSet |= 0x20;
mHotwordPhraseId = value;
return this;
}
@@ -449,11 +497,14 @@
*
* <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 @@
/** 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 @@
}
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 @@
}
@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 ea854e8..7e0117d 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -30,6 +30,7 @@
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 @@
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 @@
}
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 @@
mHandler.sendMessage(obtainMessage(
HotwordDetectionService::onDetect,
HotwordDetectionService.this,
- audioStream,
- audioFormat,
new Callback(callback)));
break;
case AUDIO_SOURCE_EXTERNAL:
@@ -246,13 +246,42 @@
* 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,
+ @NonNull AudioFormat audioFormat,
+ @DurationMillisLong long timeoutMillis,
+ @NonNull Callback callback) {
+ // TODO: Add a helpful error message.
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * 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 ParcelFileDescriptor audioStream,
- @NonNull AudioFormat audioFormat,
+ @NonNull AlwaysOnHotwordDetector.EventPayload eventPayload,
@DurationMillisLong long timeoutMillis,
@NonNull Callback callback) {
// TODO: Add a helpful error message.
@@ -305,7 +334,9 @@
* @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 @@
/**
* 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 2ffe787..7ba0098 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.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 @@
*/
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 2a25227..b5c838b 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -420,9 +420,13 @@
*
* @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 @@
}
/**
+ * 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 9a5e534..cc349c8 100644
--- a/core/java/android/speech/IRecognitionService.aidl
+++ b/core/java/android/speech/IRecognitionService.aidl
@@ -58,7 +58,6 @@
* 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 ad670c8..bb48757 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -34,7 +34,6 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
}
/**
- * 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 @@
* 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 @@
* {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter
*/
public void partialResults(Bundle partialResults) throws RemoteException {
- startRecordAudioOpAttributionToCallerIfNeeded();
mListener.onPartialResults(partialResults);
}
@@ -336,7 +296,6 @@
* {@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 @@
* 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 @@
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");
}
- }
- private @NonNull Context getAttributionContextForCaller() {
- if (mAttributionContext == null) {
+ if (DBG && isProxyingRecordAudioToCaller()) {
+ Log.i(TAG, "Proxying already in progress, not starting the attribution");
+ }
+
+ 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 @@
private final WeakReference<RecognitionService> mServiceRef;
public RecognitionServiceBinder(RecognitionService service) {
- mServiceRef = new WeakReference<RecognitionService>(service);
+ mServiceRef = new WeakReference<>(service);
}
@Override
@@ -445,8 +416,8 @@
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 @@
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/util/PackageUtils.java b/core/java/android/util/PackageUtils.java
index 8061bf3..ff04825 100644
--- a/core/java/android/util/PackageUtils.java
+++ b/core/java/android/util/PackageUtils.java
@@ -19,6 +19,7 @@
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 @@
}
/**
+ * @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 @@
@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 @@
/* ignore - can't happen */
}
}
- return computeSha256Digest(bytes.toByteArray());
+ return computeSha256Digest(bytes.toByteArray(), null);
}
/**
@@ -122,15 +132,34 @@
}
/**
- * Computes the SHA256 digest of some data.
- * @param data The data.
- * @return The digest or null if an error occurs.
+ * @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,
+ @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 b990095..91847f7 100644
--- a/core/java/android/uwb/AdapterStateListener.java
+++ b/core/java/android/uwb/AdapterStateListener.java
@@ -68,8 +68,7 @@
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 @@
mAdapter.unregisterAdapterStateCallbacks(this);
} catch (RemoteException e) {
Log.w(TAG, "Failed to unregister AdapterStateCallback with service");
+ throw e.rethrowFromSystemServer();
}
mIsRegistered = false;
}
@@ -115,24 +115,24 @@
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 29021c1..d879350 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -161,6 +161,18 @@
*/
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 3c99f89..f7406ae 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -178,7 +178,7 @@
* <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 @@
}
/**
+ * 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 c87db65..9cb0d1f 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.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 @@
}
/**
+ * @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 1706799..7125232 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -19,7 +19,6 @@
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 @@
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 88406ff..cd82489 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -857,7 +857,5 @@
*/
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 b2df3a9..11fac05 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4003,6 +4003,16 @@
/**
* @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 @@
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 @@
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 3cfda57..5a738eb 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2787,8 +2787,8 @@
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 @@
return;
}
}
- notifySurfaceCreated();
} else if (surfaceDestroyed) {
// If the surface has been removed, then reset the scroll
// positions.
@@ -2894,10 +2893,6 @@
}
}
- if (!surfaceCreated && surfaceReplaced) {
- notifySurfaceReplaced();
- }
-
if (mDragResizing != dragResizing) {
if (dragResizing) {
mResizeMode = freeformResizing
@@ -3110,7 +3105,14 @@
}
}
- 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 @@
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 60516eb..c32ab3a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -908,20 +908,6 @@
}
/**
- * 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 07eeb03..f8009919 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -328,15 +328,6 @@
}
@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 d6292ca..d15aee0 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -266,6 +266,14 @@
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 @@
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 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 560edec..5fbf228 100644
--- a/core/java/android/view/translation/ITranslationManager.aidl
+++ b/core/java/android/view/translation/ITranslationManager.aidl
@@ -22,6 +22,7 @@
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 @@
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 0000000..eb89b49
--- /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 28b2113..0bea121 100644
--- a/core/java/android/view/translation/TranslationCapability.java
+++ b/core/java/android/view/translation/TranslationCapability.java
@@ -48,6 +48,16 @@
* 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 @@
- // 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 @@
@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 @@
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 @@
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 @@
};
@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 8785d9c..e755774 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 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 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,17 +238,43 @@
}
/**
- * 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,
@NonNull PendingIntent pendingIntent) {
@@ -252,8 +288,8 @@
}
/**
- * @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 @@
}
/**
- * 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 @@
}
/**
- * @deprecated Use {@link #removeOnDeviceTranslationCapabilityUpdateListener(int, int,
- * PendingIntent)}
+ * @deprecated Use {@link #removeOnDeviceTranslationCapabilityUpdateListener(
+ * java.util.function.Consumer)}.
*/
@Deprecated
public void removeTranslationCapabilityUpdateListener(
@@ -308,8 +368,6 @@
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 @@
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 eefc7fd..541b494 100644
--- a/core/java/android/view/translation/UiTranslationManager.java
+++ b/core/java/android/view/translation/UiTranslationManager.java
@@ -119,15 +119,31 @@
}
/**
+ * @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 @@
@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 @@
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 @@
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 @@
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 0000000..7fbeb66
--- /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 0000000..b43dbce
--- /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/window/IRemoteTransition.aidl b/core/java/android/window/IRemoteTransition.aidl
index e71de70..2efb68a 100644
--- a/core/java/android/window/IRemoteTransition.aidl
+++ b/core/java/android/window/IRemoteTransition.aidl
@@ -40,7 +40,23 @@
/**
* Starts a transition animation. Once complete, the implementation should call
* `finishCallback`.
+ *
+ * @param token An identifier for the transition that should be animated.
*/
- void startAnimation(in TransitionInfo info, in SurfaceControl.Transaction t,
+ void startAnimation(in IBinder token, in TransitionInfo info, in SurfaceControl.Transaction t,
+ in IRemoteTransitionFinishedCallback finishCallback);
+
+ /**
+ * Attempts to merge a transition animation into the animation that is currently
+ * being played by this remote. If merge is not possible/supported, this should be a no-op.
+ * If it *is* merged, the implementation should call `finishCallback` immediately.
+ *
+ * @param transition An identifier for the transition that wants to be merged.
+ * @param mergeTarget The transition that is currently being animated by this remote.
+ * If it can be merged, call `finishCallback`; otherwise, do
+ * nothing.
+ */
+ void mergeAnimation(in IBinder transition, in TransitionInfo info,
+ in SurfaceControl.Transaction t, in IBinder mergeTarget,
in IRemoteTransitionFinishedCallback finishCallback);
}
diff --git a/core/java/android/window/ITransitionPlayer.aidl b/core/java/android/window/ITransitionPlayer.aidl
index af37fbc..3eca803 100644
--- a/core/java/android/window/ITransitionPlayer.aidl
+++ b/core/java/android/window/ITransitionPlayer.aidl
@@ -47,9 +47,12 @@
* @param transitionToken An identifying token for the transition that is now ready to animate.
* @param info A collection of all the changes encapsulated by this transition.
* @param t A surface transaction containing the surface state prior to animating.
+ * @param finishT A surface transaction that will reset parenting/layering and generally put
+ * surfaces into their final (post-transition) state. Apply this after playing
+ * the animation but before calling finish.
*/
void onTransitionReady(in IBinder transitionToken, in TransitionInfo info,
- in SurfaceControl.Transaction t);
+ in SurfaceControl.Transaction t, in SurfaceControl.Transaction finishT);
/**
* Called when something in WMCore requires a transition to play -- for example when an Activity
diff --git a/core/java/android/window/PictureInPictureSurfaceTransaction.java b/core/java/android/window/PictureInPictureSurfaceTransaction.java
index bde91d9..96a8ac8 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 @@
+ ")";
}
+ /** 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 18f29ae..7d222db 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 @@
*/
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 @@
* @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 @@
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 f151527..23b8ee4 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -77,8 +77,11 @@
/** 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 @@
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 @@
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 26a6f0d..f93e413 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -109,8 +109,8 @@
}
/**
- * 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(
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 375e503..f34aabb 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 @@
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 0000000..ffade1e
--- /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 efce0a84..06f68e8 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 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 6776c27..bd90890 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 @@
}
final BrightnessSyncObserver brightnessSyncObserver;
- brightnessSyncObserver = new BrightnessSyncObserver(mHandler);
+ brightnessSyncObserver = new BrightnessSyncObserver();
brightnessSyncObserver.startObserving();
final float currentFloatBrightness = getScreenBrightnessFloat();
@@ -232,47 +232,52 @@
}
}
- 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 135c076..4d3f774 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -102,6 +102,7 @@
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 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 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 5ab2a82..0441594 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -79,6 +79,12 @@
/**
* 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/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 93ba037..60a8d80 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_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 @@
/** 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 @@
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 @@
(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 @@
@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 @@
/** 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 @@
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 @@
// 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 @@
}
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 49dbbaa..a61e86b 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -30,4 +30,5 @@
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 93cd4e9..772e344 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -68,6 +68,12 @@
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/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index 940979d..0a2c18f8 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -110,7 +110,7 @@
* #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 @@
/**
* 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/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index db536d3..a9b47aa 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -24,7 +24,6 @@
import android.content.ComponentName;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
-import android.hardware.SensorPrivacyManager;
import android.os.Build;
import android.os.CarrierAssociatedAppEntry;
import android.os.Environment;
@@ -1245,14 +1244,6 @@
addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0);
}
- if (SensorPrivacyManager.USE_MICROPHONE_TOGGLE) {
- addFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE, 0);
- }
-
- if (SensorPrivacyManager.USE_CAMERA_TOGGLE) {
- addFeature(PackageManager.FEATURE_CAMERA_TOGGLE, 0);
- }
-
for (String featureName : mUnavailableFeatures) {
removeFeature(featureName);
}
diff --git a/core/proto/android/providers/OWNERS b/core/proto/android/providers/OWNERS
new file mode 100644
index 0000000..1f5cd9a
--- /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 8ee0e39..c3d1596 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -285,6 +285,7 @@
// 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 bf98a2e..9c65dac 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 445bac5..2a28f0c 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 ee286bd..f5a8547 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 ad295cb..984069a 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 5eac3d0..7d86f6c 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 0a1c880..3dba1f9 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 5a7e890..86f60a8 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 c0915d2c..1f6088a 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 5b8a81c..7f17c2c 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 21cf141..04cdcf5 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 b0a9726..dd00bd0 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 f098fbe..ecdc94f 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 de6debb..d0494bd 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 557484d..39e061d 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 04b5747..a92de5a 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 417b8f2..3e0dda7 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 f8ca0b1..bb7b1fa 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 dc993fe..4af88db 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 5b80a90..f322d05 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 dad96b4..1eb402d 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 2cdf5ba..65b00c8 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 6a1cddc..b361240 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 7519b5a..13ed8ff 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 1e58aed..9d63c3d 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 ce10584..a8a5d1f 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 280c71f..dc0b015 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 2538e86..310ce66 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 a5b1250..cb5e616 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 86cc13a..d68d00d 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 25fb55c..9c08922 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 f8de931..843fe4c 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 4ab4a7a..1419396 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 28fb400..c88fcbd 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 09e56d8..dcfcfb9 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 d4d18c0..67560f7 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 5491214..d85a4ba 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 4ed6fcc..5ae6554 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 23acc8b..0a3e014 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 5269501..6bfb7eb 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 3783411..bed82eb 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 ba7beac..a7d11c2 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 4e10167..402256e 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 81dc891..47946dd 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 02448bd..c4606d6 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 da8f4ab..43963af 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 8feb675..a2b199c 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 caa9154..9426514 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 ed4f15e..b6f6975 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 94a50ea..70b37bd 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 5e90faa..df0afb0 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 5a47e60..d479d30 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 30ab692..6e8abd1 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 9beb59f..44ca94e 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 62298a9..ddb4816 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 9c6593e..d1575b9 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 4720e29..3635fb0 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 458e09b..90e0d24 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 fcc5264..af56ee3 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 55bb315..734a81e 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 bfa5760..a95244a 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 57037ad..8233acc 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 102073e..d0d0522 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 d0c2beb..dd3d1b4 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 102073e..d0d0522 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 e6b7ff6..e5e960f 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 7d87d7c..55d6ac8f 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 c0577d6..0130d1e 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 5c9ddf7..e2da90d 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 e98d0ae..01eaed6 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 30e13a4..387bb4e 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 96fa82d..00fe38d 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 0ddf34d..a3c350b 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 ea069e1..7eb9dda 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 962b9a3..e68a865 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 2fa3710..210ed4f 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 db38765..5b00f73 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 adaa774..c2409c4 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 bd85322..935e489 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 b95aa88..ca3b589 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 f067641b..8878616 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 012f31e..90bbd47 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 ccbcd0a..4424733 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 d7f3165..2ffca20 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 858c179..3061985 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 8770db7..6239b51 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 4a912d4..18481fc 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 c2a7e07..fbc9678 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,18 +4860,36 @@
<!-- 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>
+
+ <!-- Whether this device is supporting the microphone toggle -->
+ <bool name="config_supportsMicToggle">false</bool>
+ <!-- Whether this device is supporting the camera toggle -->
+ <bool name="config_supportsCamToggle">false</bool>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cc347f9..fd33ccd 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 402ea60..9ac9e8e 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" />
@@ -4357,4 +4358,7 @@
<java-symbol type="string" name="view_and_control_notification_content" />
<java-symbol type="layout" name="notification_expand_button"/>
+
+ <java-symbol type="bool" name="config_supportsMicToggle" />
+ <java-symbol type="bool" name="config_supportsCamToggle" />
</resources>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
index d49ab57..52a77a7 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
@@ -25,8 +25,7 @@
<application
android:theme="@style/Theme"
android:label="Battery Stats Viewer">
- <activity android:name=".BatteryStatsViewerActivity"
- android:icon="@mipmap/ic_launcher"
+ <activity android:name=".BatteryConsumerPickerActivity"
android:label="Battery Stats"
android:launchMode="singleTop"
android:exported="true">
@@ -36,7 +35,9 @@
</intent-filter>
</activity>
- <activity android:name=".BatteryConsumerPickerActivity"
- android:label="Select a battery consumer"/>
+ <activity android:name=".BatteryStatsViewerActivity"
+ android:icon="@mipmap/ic_launcher"
+ android:label="Battery Stats"
+ android:parentActivityName=".BatteryConsumerPickerActivity"/>
</application>
</manifest>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java
index 2db848b..63a15d6 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java
@@ -20,9 +20,7 @@
import android.content.Intent;
import android.os.Bundle;
-import androidx.activity.result.contract.ActivityResultContract;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentStatePagerAdapter;
@@ -32,31 +30,14 @@
/**
* Picker, showing a sorted lists of applications and other types of entities consuming power.
- * Returns the selected entity ID or null.
+ * Opens BatteryStatsViewerActivity upon item selection.
*/
public class BatteryConsumerPickerActivity extends FragmentActivity {
-
- public static final ActivityResultContract<Void, String> CONTRACT =
- new ActivityResultContract<Void, String>() {
- @NonNull
- @Override
- public Intent createIntent(@NonNull Context context, Void aVoid) {
- return new Intent(context, BatteryConsumerPickerActivity.class);
- }
-
- @Override
- public String parseResult(int resultCode, @Nullable Intent intent) {
- if (resultCode != RESULT_OK || intent == null) {
- return null;
- }
- return intent.getStringExtra(Intent.EXTRA_RETURN_RESULT);
- }
- };
+ private static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId";
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- getActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.battery_consumer_picker_activity_layout);
@@ -99,18 +80,25 @@
viewPager.setAdapter(adapter);
TabLayout tabLayout = findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager);
+ if (icicle == null) {
+ final String batteryConsumerId = getPreferences(Context.MODE_PRIVATE)
+ .getString(PREF_SELECTED_BATTERY_CONSUMER, null);
+ if (batteryConsumerId != null) {
+ startBatteryStatsActivity(batteryConsumerId);
+ }
+ }
}
public void setSelectedBatteryConsumer(String batteryConsumerId) {
- Intent intent = new Intent();
- intent.putExtra(Intent.EXTRA_RETURN_RESULT, batteryConsumerId);
- setResult(RESULT_OK, intent);
- finish();
+ getPreferences(Context.MODE_PRIVATE).edit()
+ .putString(PREF_SELECTED_BATTERY_CONSUMER, batteryConsumerId)
+ .apply();
+ startBatteryStatsActivity(batteryConsumerId);
}
- @Override
- public boolean onNavigateUp() {
- onBackPressed();
- return true;
+ private void startBatteryStatsActivity(String batteryConsumerId) {
+ final Intent intent = new Intent(this, BatteryStatsViewerActivity.class)
+ .putExtra(BatteryStatsViewerActivity.EXTRA_BATTERY_CONSUMER, batteryConsumerId);
+ startActivity(intent);
}
}
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
index 74d3fb3..03dde04 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
@@ -17,7 +17,6 @@
package com.android.frameworks.core.batterystatsviewer;
import android.content.Context;
-import android.content.SharedPreferences;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
@@ -27,9 +26,9 @@
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.activity.ComponentActivity;
-import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.loader.app.LoaderManager;
@@ -45,14 +44,14 @@
import java.util.Locale;
public class BatteryStatsViewerActivity extends ComponentActivity {
+ public static final String EXTRA_BATTERY_CONSUMER = "batteryConsumerId";
+
private static final int BATTERY_STATS_REFRESH_RATE_MILLIS = 60 * 1000;
private static final int MILLIS_IN_MINUTE = 60000;
- private static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId";
private static final int LOADER_BATTERY_USAGE_STATS = 1;
private BatteryStatsDataAdapter mBatteryStatsDataAdapter;
private final Runnable mBatteryStatsRefresh = this::periodicBatteryStatsRefresh;
- private SharedPreferences mSharedPref;
private String mBatteryConsumerId;
private TextView mTitleView;
private TextView mDetailsView;
@@ -62,21 +61,16 @@
private RecyclerView mBatteryConsumerDataView;
private View mLoadingView;
private View mEmptyView;
- private final ActivityResultLauncher<Void> mStartAppPicker = registerForActivityResult(
- BatteryConsumerPickerActivity.CONTRACT, this::onApplicationSelected);
private List<BatteryUsageStats> mBatteryUsageStats;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mSharedPref = getPreferences(Context.MODE_PRIVATE);
+ mBatteryConsumerId = getIntent().getStringExtra(EXTRA_BATTERY_CONSUMER);
setContentView(R.layout.battery_stats_viewer_layout);
- View appCard = findViewById(R.id.app_card);
- appCard.setOnClickListener((e) -> startAppPicker());
-
mTitleView = findViewById(android.R.id.title);
mDetailsView = findViewById(R.id.details);
mIconView = findViewById(android.R.id.icon);
@@ -91,11 +85,7 @@
mLoadingView = findViewById(R.id.loading_view);
mEmptyView = findViewById(R.id.empty_view);
- mBatteryConsumerId = mSharedPref.getString(PREF_SELECTED_BATTERY_CONSUMER, null);
loadBatteryStats();
- if (mBatteryConsumerId == null) {
- startAppPicker();
- }
}
@Override
@@ -110,25 +100,6 @@
getMainThreadHandler().removeCallbacks(mBatteryStatsRefresh);
}
- private void startAppPicker() {
- mStartAppPicker.launch(null);
- }
-
- private void onApplicationSelected(String batteryConsumerId) {
- if (batteryConsumerId == null) {
- if (mBatteryConsumerId == null) {
- finish();
- }
- } else {
- mBatteryConsumerId = batteryConsumerId;
- mSharedPref.edit()
- .putString(PREF_SELECTED_BATTERY_CONSUMER, mBatteryConsumerId)
- .apply();
- mLoadingView.setVisibility(View.VISIBLE);
- loadBatteryStats();
- }
- }
-
private void periodicBatteryStatsRefresh() {
loadBatteryStats();
getMainThreadHandler().postDelayed(mBatteryStatsRefresh, BATTERY_STATS_REFRESH_RATE_MILLIS);
@@ -200,9 +171,10 @@
BatteryConsumerInfoHelper.BatteryConsumerInfo
batteryConsumerInfo = batteryConsumerData.getBatteryConsumerInfo();
if (batteryConsumerInfo == null) {
- mTitleView.setText("Battery consumer not found");
- mPackagesView.setVisibility(View.GONE);
- mHeadingsView.setVisibility(View.GONE);
+ Toast.makeText(this, "Battery consumer not found: " + mBatteryConsumerId,
+ Toast.LENGTH_SHORT).show();
+ finish();
+ return;
} else {
mTitleView.setText(batteryConsumerInfo.label);
if (batteryConsumerInfo.details != null) {
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 2e2e6bd..6f17ea9 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 @@
}
@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 b2b9ab3..c9a18da 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 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 @@
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, false, none),
+ validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, none),
PasswordValidationError.WEAK_CREDENTIAL_TYPE, 0);
assertValidationErrors(
- validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, false, pattern),
+ validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, pattern),
+ PasswordValidationError.WEAK_CREDENTIAL_TYPE, 0);
+ assertValidationErrors(
+ 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 36da927..3e2c4e9 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 @@
.setIsImportantConversation(true)
.setStatuses(statusList).setNotificationKey("key")
.setNotificationContent("content")
+ .setNotificationSender("sender")
.setNotificationDataUri(Uri.parse("data"))
.setMessagesCount(2)
.setIntent(new Intent())
@@ -256,6 +257,7 @@
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 @@
}
@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 3948eb8..17d2492 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 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 @@
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/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index 3a80464..9880f8c 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -100,6 +100,17 @@
}
@Test
+ public void testGetPrimitiveDuration() {
+ VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .build();
+ assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
+ assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_TICK));
+ }
+
+ @Test
public void testGetDefaultBraking_returnsFirstSupportedBraking() {
assertEquals(Braking.NONE, new VibratorInfo.Builder(
TEST_VIBRATOR_ID).build().getDefaultBraking());
@@ -251,12 +262,14 @@
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.setQFactor(2f)
.setFrequencyMapping(TEST_FREQUENCY_MAPPING);
VibratorInfo complete = completeBuilder.build();
assertEquals(complete, complete);
assertEquals(complete, completeBuilder.build());
+ assertEquals(complete.hashCode(), completeBuilder.build().hashCode());
VibratorInfo completeWithComposeControl = completeBuilder
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
@@ -279,6 +292,11 @@
.build();
assertNotEquals(complete, completeWithUnknownPrimitives);
+ VibratorInfo completeWithDifferentPrimitiveDuration = completeBuilder
+ .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+ assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
+
VibratorInfo completeWithDifferentFrequencyMapping = completeBuilder
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(TEST_MIN_FREQUENCY + 10,
TEST_RESONANT_FREQUENCY + 20, TEST_FREQUENCY_RESOLUTION + 5,
@@ -314,7 +332,8 @@
VibratorInfo original = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
- .setSupportedPrimitives(null)
+ .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.setQFactor(Float.NaN)
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
.build();
@@ -324,5 +343,6 @@
parcel.setDataPosition(0);
VibratorInfo restored = VibratorInfo.CREATOR.createFromParcel(parcel);
assertEquals(original, restored);
+ assertEquals(original.hashCode(), restored.hashCode());
}
}
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java
index 6213285..8f9168b 100644
--- a/core/tests/coretests/src/android/os/VibratorTest.java
+++ b/core/tests/coretests/src/android/os/VibratorTest.java
@@ -80,6 +80,16 @@
}
@Test
+ public void getPrimitivesDurations_returnsArrayOfSameSize() {
+ assertEquals(0, mVibratorSpy.getPrimitiveDurations(new int[0]).length);
+ assertEquals(1, mVibratorSpy.getPrimitiveDurations(
+ new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}).length);
+ assertEquals(2, mVibratorSpy.getPrimitiveDurations(
+ new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE}).length);
+ }
+
+ @Test
public void vibrate_withAudioAttributes_createsVibrationAttributesWithSameUsage() {
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
index cba9cbd..d9e6151 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
@@ -24,6 +24,9 @@
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 cc2d8d2..2d8556f 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
@@ -26,6 +26,9 @@
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 fe7c944..d7af2d9 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
@@ -26,6 +26,9 @@
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_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 69ec5ce..236c740 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.mk
@@ -26,6 +26,9 @@
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 2dc30ea..6f6ccfe 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk
@@ -26,6 +26,9 @@
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_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 a6f87ea..33a46ea 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk
@@ -23,6 +23,9 @@
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 3636c73..efc0688 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
@@ -23,6 +23,9 @@
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 afbcd46..3920fd6 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
@@ -21,6 +21,9 @@
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_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 67f1fa5..2323ad9 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_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 33871e5..79a5906 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_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 1b267ee..521bad0 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_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 @@
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 f3c0abd..b453cde9 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 @@
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 $(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 $(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 878f05d..77fc887 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 @@
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 @@
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 $(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 @@
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 $(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 bdaf630..4cad535 100644
--- a/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java
+++ b/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java
@@ -19,7 +19,6 @@
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 @@
}
@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 @@
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 7ed791d..9b9511b 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 b463dd8..08d1c34 100644
--- a/data/keyboards/Vendor_054c_Product_0268.kl
+++ b/data/keyboards/Vendor_054c_Product_0268.kl
@@ -77,3 +77,11 @@
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 3d93f0f..d281b4b 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 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 3d93f0f..d281b4b 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 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 5fe35f7..3eafea0 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 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 9c55a2f..53308e3 100644
--- a/data/keyboards/Virtual.kcm
+++ b/data/keyboards/Virtual.kcm
@@ -25,12 +25,14 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 13e2692..7f5b752 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -225,7 +225,14 @@
}
}
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 @@
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 @@
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/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 091f579..6d3d84c 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -66,7 +66,8 @@
boolean isCredentialManagementApp(String packageName);
// APIs used by KeyChainActivity
- void setGrant(int uid, String alias, boolean value);
+ // setGrant may fail with value=false when ungrant operation fails in KeyStore.
+ boolean setGrant(int uid, String alias, boolean value);
boolean hasGrant(int uid, String alias);
// API used by Wifi
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index 9d8a5ef..e808c5c 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -579,7 +579,11 @@
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 625f4b8..58b3de4 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.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 @@
/**
* 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 @@
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 @@
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 @@
}
};
+ 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,6 +216,7 @@
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);
OneHandedAnimationController animationController =
@@ -218,7 +235,7 @@
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 @@
OneHandedSettingsUtil settingsUtil,
OneHandedAccessibilityUtil oneHandedAccessibilityUtil,
OneHandedTimeoutHandler timeoutHandler,
+ OneHandedState state,
OneHandedUiEventLogger uiEventsLogger,
IOverlayManager overlayManager,
TaskStackListenerImpl taskStackListener,
@@ -246,6 +264,7 @@
mDisplayAreaOrganizer = displayAreaOrganizer;
mDisplayController = displayController;
mTouchHandler = touchHandler;
+ mState = state;
mTutorialHandler = tutorialHandler;
mGestureHandler = gestureHandler;
mOverlayManager = overlayManager;
@@ -330,26 +349,27 @@
@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 @@
}
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 @@
mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler);
mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler);
mDisplayAreaOrganizer.registerTransitionCallback(mBackgroundPanelOrganizer);
- mDisplayAreaOrganizer.registerTransitionCallback(this);
+ mDisplayAreaOrganizer.registerTransitionCallback(mTransitionCallBack);
if (mTaskChangeToExit) {
mTaskStackListener.addListener(mTaskStackListenerCallback);
}
@@ -523,8 +544,8 @@
}
private void updateOneHandedEnabled() {
- if (mDisplayAreaOrganizer.isInOneHanded()) {
- stopOneHanded();
+ if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) {
+ mMainExecutor.execute(() -> stopOneHanded());
}
mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
@@ -615,8 +636,6 @@
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 +657,10 @@
mTimeoutHandler.dump(pw);
}
+ if (mState != null) {
+ mState.dump(pw);
+ }
+
if (mTutorialHandler != null) {
mTutorialHandler.dump(pw);
}
@@ -663,26 +686,6 @@
}
/**
- * 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 390985c..4b4d934 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 @@
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 @@
@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 @@
}
/**
- * 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 @@
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 0000000..cc87443
--- /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/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 8ac9a7a..ca05ff4 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,35 +427,43 @@
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 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.
+ rotateBounds(rotatedEndRect, initialSourceValue, rotationDelta);
+ // Use the rect that has the same orientation as the hint rect.
+ initialContainerRect = isOutPipDirection ? rotatedEndRect : initialSourceValue;
+ } else {
+ rotatedEndRect = lastEndRect = null;
+ initialContainerRect = initialSourceValue;
+ }
+
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);
+ sourceHintRectInsets = new Rect(sourceHintRect.left - initialContainerRect.left,
+ sourceHintRect.top - initialContainerRect.top,
+ initialContainerRect.right - sourceHintRect.right,
+ initialContainerRect.bottom - sourceHintRect.bottom);
}
- final Rect sourceInsets = new Rect(0, 0, 0, 0);
-
- final Rect rotatedEndRect;
- if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
- // 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);
- } else {
- rotatedEndRect = null;
- }
+ 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,
@@ -472,8 +480,8 @@
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 @@
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 @@
}
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 @@
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 3dd97f5..2b79539 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 @@
* @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 e66be66..4ce6c9e 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_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.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 @@
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 @@
}
}
+ private final Context mContext;
private final SyncTransactionQueue mSyncTransactionQueue;
private final PipBoundsState mPipBoundsState;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
@@ -160,8 +171,20 @@
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 @@
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 @@
*/
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 @@
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
+ mContext = context;
mSyncTransactionQueue = syncTransactionQueue;
mPipBoundsState = pipBoundsState;
mPipBoundsAlgorithm = boundsHandler;
@@ -261,10 +295,6 @@
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 void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
mOneShotAnimationType = animationType;
+ if (animationType == ANIM_TYPE_ALPHA) {
+ mLastOneShotAlphaAnimationTime = SystemClock.uptimeMillis();
+ }
}
/**
@@ -297,9 +330,6 @@
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 @@
: 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 @@
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 @@
// 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 @@
}
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 @@
}
}
+ 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 @@
}, 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 @@
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 @@
Log.wtf(TAG, "Unrecognized token: " + token);
return;
}
- mWaitForFixedRotation = false;
+ clearWaitForFixedRotation();
mInSwipePipToHomeTransition = false;
mPictureInPictureParams = null;
mState = State.UNDEFINED;
@@ -617,8 +675,10 @@
@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 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 @@
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 @@
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 @@
// 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 @@
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 @@
.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 f505e60..b881fea 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 @@
*/
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/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 2a1fe60..6d96312 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -872,7 +872,8 @@
mMotionHelper.flingToSnapTarget(vel.x, vel.y,
this::flingEndAction /* endAction */);
}
- } else if (mTouchState.isDoubleTap() && !mPipBoundsState.isStashed()) {
+ } else if (mTouchState.isDoubleTap() && !mPipBoundsState.isStashed()
+ && mMenuState != MENU_STATE_FULL) {
// If using pinch to zoom, double-tap functions as resizing between max/min size
if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
final boolean toExpand = mPipBoundsState.getBounds().width()
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 e336287..cb7afc7 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 @@
*/
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 @@
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 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 @@
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 0000000..de221ed
--- /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/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index e9ce2ad..b468462 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -38,6 +38,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
@@ -52,8 +53,10 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -68,7 +71,6 @@
import android.view.InputChannel;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
@@ -122,11 +124,9 @@
private static final Point TMP_SURFACE_SIZE = new Point();
private final Window mWindow;
- private final Surface mSurface;
private final Runnable mClearWindowHandler;
private final ShellExecutor mSplashScreenExecutor;
- private SurfaceControl mSurfaceControl;
- private SurfaceControl mChildSurfaceControl;
+ private final SurfaceControl mSurfaceControl;
private final IWindowSession mSession;
private final Rect mTaskBounds;
private final Rect mFrame = new Rect();
@@ -179,7 +179,7 @@
// Setting as trusted overlay to let touches pass through. This is safe because this
// window is controlled by the system.
layoutParams.privateFlags = (windowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS)
- | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ | PRIVATE_FLAG_TRUSTED_OVERLAY | PRIVATE_FLAG_USE_BLAST;
layoutParams.token = appToken;
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
@@ -259,7 +259,6 @@
int currentOrientation, int activityType, InsetsState topWindowInsetsState,
Runnable clearWindowHandler, ShellExecutor splashScreenExecutor) {
mSplashScreenExecutor = splashScreenExecutor;
- mSurface = new Surface();
mSession = WindowManagerGlobal.getWindowSession();
mWindow = new Window();
mWindow.setSession(mSession);
@@ -336,7 +335,6 @@
}
private void drawSnapshot() {
- mSurface.copyFrom(mSurfaceControl);
if (DEBUG) {
Slog.d(TAG, "Drawing snapshot surface sizeMismatch= " + mSizeMismatch);
}
@@ -357,15 +355,14 @@
}
private void drawSizeMatchSnapshot() {
- mSurface.attachAndQueueBufferWithColorSpace(mSnapshot.getHardwareBuffer(),
- mSnapshot.getColorSpace());
- mSurface.release();
+ GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
+ mSnapshot.getHardwareBuffer());
+ mTransaction.setBuffer(mSurfaceControl, graphicBuffer)
+ .setColorSpace(mSurfaceControl, mSnapshot.getColorSpace())
+ .apply();
}
private void drawSizeMismatchSnapshot() {
- if (!mSurface.isValid()) {
- throw new IllegalStateException("mSurface does not hold a valid surface.");
- }
final HardwareBuffer buffer = mSnapshot.getHardwareBuffer();
final SurfaceSession session = new SurfaceSession();
@@ -376,26 +373,24 @@
- ((float) mFrame.width() / mFrame.height())) > 0.01f;
// Keep a reference to it such that it doesn't get destroyed when finalized.
- mChildSurfaceControl = new SurfaceControl.Builder(session)
+ SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
.setName(mTitle + " - task-snapshot-surface")
- .setBufferSize(buffer.getWidth(), buffer.getHeight())
+ .setBLASTLayer()
.setFormat(buffer.getFormat())
.setParent(mSurfaceControl)
.setCallsite("TaskSnapshotWindow.drawSizeMismatchSnapshot")
.build();
- Surface surface = new Surface();
- surface.copyFrom(mChildSurfaceControl);
final Rect frame;
// We can just show the surface here as it will still be hidden as the parent is
// still hidden.
- mTransaction.show(mChildSurfaceControl);
+ mTransaction.show(childSurfaceControl);
if (aspectRatioMismatch) {
// Clip off ugly navigation bar.
final Rect crop = calculateSnapshotCrop();
frame = calculateSnapshotFrame(crop);
- mTransaction.setWindowCrop(mChildSurfaceControl, crop);
- mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top);
+ mTransaction.setWindowCrop(childSurfaceControl, crop);
+ mTransaction.setPosition(childSurfaceControl, frame.left, frame.top);
mTmpSnapshotSize.set(crop);
mTmpDstFrame.set(frame);
} else {
@@ -407,18 +402,23 @@
// Scale the mismatch dimensions to fill the task bounds
mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
- mTransaction.setMatrix(mChildSurfaceControl, mSnapshotMatrix, mTmpFloat9);
-
- mTransaction.apply();
- surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace());
- surface.release();
+ mTransaction.setMatrix(childSurfaceControl, mSnapshotMatrix, mTmpFloat9);
+ GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
+ mSnapshot.getHardwareBuffer());
+ mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
+ mTransaction.setBuffer(childSurfaceControl, graphicBuffer);
if (aspectRatioMismatch) {
- final Canvas c = mSurface.lockCanvas(null);
+ GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(),
+ PixelFormat.RGBA_8888,
+ GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
+ | GraphicBuffer.USAGE_SW_WRITE_RARELY);
+ final Canvas c = background.lockCanvas();
drawBackgroundAndBars(c, frame);
- mSurface.unlockCanvasAndPost(c);
- mSurface.release();
+ background.unlockCanvasAndPost(c);
+ mTransaction.setBuffer(mSurfaceControl, background);
}
+ mTransaction.apply();
}
/**
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 0000000..9948e7d
--- /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 0000000..6e7dec5
--- /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 b29b18b..c6fb5af 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 @@
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 @@
/** 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 @@
// 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 @@
}
@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/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 71fd917..4da6664 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -82,18 +82,39 @@
if (mRemote.asBinder() != null) {
mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
- mRemote.startAnimation(info, t, cb);
+ mRemote.startAnimation(transition, info, t, cb);
} catch (RemoteException e) {
+ Log.e(Transitions.TAG, "Error running remote transition.", e);
if (mRemote.asBinder() != null) {
mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
- Log.e(Transitions.TAG, "Error running remote transition.", e);
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
}
return true;
}
@Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
+ + " transition %s for %s.", mRemote, transition);
+
+ IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction wct) {
+ mMainExecutor.execute(
+ () -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
+ }
+ };
+ try {
+ mRemote.mergeAnimation(transition, info, t, mergeTarget, cb);
+ } catch (RemoteException e) {
+ Log.e(Transitions.TAG, "Error merging remote transition.", e);
+ }
+ }
+
+ @Override
@Nullable
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@Nullable TransitionRequestInfo request) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 8876e5f..9bfb261 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.transition;
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
@@ -52,7 +50,7 @@
private final ShellExecutor mMainExecutor;
/** Includes remotes explicitly requested by, eg, ActivityOptions */
- private final ArrayMap<IBinder, IRemoteTransition> mPendingRemotes = new ArrayMap<>();
+ private final ArrayMap<IBinder, IRemoteTransition> mRequestedRemotes = new ArrayMap<>();
/** Ordered by specificity. Last filters will be checked first */
private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters =
@@ -63,9 +61,7 @@
@Override
@BinderThread
public void binderDied() {
- mMainExecutor.execute(() -> {
- mFilters.clear();
- });
+ mMainExecutor.execute(() -> mFilters.clear());
}
};
@@ -97,10 +93,15 @@
}
@Override
+ public void onTransitionMerged(@NonNull IBinder transition) {
+ mRequestedRemotes.remove(transition);
+ }
+
+ @Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- IRemoteTransition pendingRemote = mPendingRemotes.remove(transition);
+ IRemoteTransition pendingRemote = mRequestedRemotes.get(transition);
if (pendingRemote == null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s doesn't have "
+ "explicit remote, search filters for match for %s", transition, info);
@@ -110,6 +111,8 @@
mFilters.get(i));
if (mFilters.get(i).first.matches(info)) {
pendingRemote = mFilters.get(i).second;
+ // Add to requested list so that it can be found for merge requests.
+ mRequestedRemotes.put(transition, pendingRemote);
break;
}
}
@@ -122,8 +125,10 @@
final IRemoteTransition remote = pendingRemote;
final IBinder.DeathRecipient remoteDied = () -> {
Log.e(Transitions.TAG, "Remote transition died, finishing");
- mMainExecutor.execute(
- () -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
+ mMainExecutor.execute(() -> {
+ mRequestedRemotes.remove(transition);
+ finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ });
};
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
@@ -131,20 +136,23 @@
if (remote.asBinder() != null) {
remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
- mMainExecutor.execute(
- () -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
+ mMainExecutor.execute(() -> {
+ mRequestedRemotes.remove(transition);
+ finishCallback.onTransitionFinished(wct, null /* wctCB */);
+ });
}
};
try {
if (remote.asBinder() != null) {
remote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
- remote.startAnimation(info, t, cb);
+ remote.startAnimation(transition, info, t, cb);
} catch (RemoteException e) {
+ Log.e(Transitions.TAG, "Error running remote transition.", e);
if (remote.asBinder() != null) {
remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
- Log.e(Transitions.TAG, "Error running remote transition.", e);
+ mRequestedRemotes.remove(transition);
mMainExecutor.execute(
() -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
}
@@ -152,12 +160,42 @@
}
@Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ final IRemoteTransition remote = mRequestedRemotes.get(mergeTarget);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt merge %s into %s",
+ transition, remote);
+ if (remote == null) return;
+
+ IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
+ @Override
+ public void onTransitionFinished(WindowContainerTransaction wct) {
+ mMainExecutor.execute(() -> {
+ if (!mRequestedRemotes.containsKey(mergeTarget)) {
+ Log.e(TAG, "Merged transition finished after it's mergeTarget (the "
+ + "transition it was supposed to merge into). This usually means "
+ + "that the mergeTarget's RemoteTransition impl erroneously "
+ + "accepted/ran the merge request after finishing the mergeTarget");
+ }
+ finishCallback.onTransitionFinished(wct, null /* wctCB */);
+ });
+ }
+ };
+ try {
+ remote.mergeAnimation(transition, info, t, mergeTarget, cb);
+ } catch (RemoteException e) {
+ Log.e(Transitions.TAG, "Error attempting to merge remote transition.", e);
+ }
+ }
+
+ @Override
@Nullable
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@Nullable TransitionRequestInfo request) {
IRemoteTransition remote = request.getRemoteTransition();
if (remote == null) return null;
- mPendingRemotes.put(transition, remote);
+ mRequestedRemotes.put(transition, remote);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "RemoteTransition directly requested"
+ " for %s: %s", transition, remote);
return new WindowContainerTransaction();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 64dc47f..3251abc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -35,7 +35,6 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.util.ArrayMap;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -91,11 +90,16 @@
private float mTransitionAnimationScaleSetting = 1.0f;
private static final class ActiveTransition {
- TransitionHandler mFirstHandler = null;
+ IBinder mToken = null;
+ TransitionHandler mHandler = null;
+ boolean mMerged = false;
+ TransitionInfo mInfo = null;
+ SurfaceControl.Transaction mStartT = null;
+ SurfaceControl.Transaction mFinishT = null;
}
- /** Keeps track of currently tracked transitions and all the animations associated with each */
- private final ArrayMap<IBinder, ActiveTransition> mActiveTransitions = new ArrayMap<>();
+ /** Keeps track of currently playing transitions in the order of receipt. */
+ private final ArrayList<ActiveTransition> mActiveTransitions = new ArrayList<>();
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
@NonNull Context context, @NonNull ShellExecutor mainExecutor,
@@ -226,7 +230,7 @@
* type, their transit mode, and their destination z-order.
*/
private static void setupStartState(@NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t) {
+ @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
boolean isOpening = isOpeningType(info.getType());
if (info.getRootLeash().isValid()) {
t.show(info.getRootLeash());
@@ -270,6 +274,8 @@
t.setAlpha(leash, 1.f);
} else {
t.setAlpha(leash, 0.f);
+ // fix alpha in finish transaction in case the animator itself no-ops.
+ finishT.setAlpha(leash, 1.f);
}
} else {
// put on bottom and leave it visible
@@ -290,16 +296,24 @@
}
}
+ private int findActiveTransition(IBinder token) {
+ for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
+ if (mActiveTransitions.get(i).mToken == token) return i;
+ }
+ return -1;
+ }
+
@VisibleForTesting
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t) {
+ @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
transitionToken, info);
- final ActiveTransition active = mActiveTransitions.get(transitionToken);
- if (active == null) {
+ final int activeIdx = findActiveTransition(transitionToken);
+ if (activeIdx < 0) {
throw new IllegalStateException("Got transitionReady for non-active transition "
+ transitionToken + ". expecting one of "
- + Arrays.toString(mActiveTransitions.keySet().toArray()));
+ + Arrays.toString(mActiveTransitions.stream().map(
+ activeTransition -> activeTransition.mToken).toArray()));
}
if (!info.getRootLeash().isValid()) {
// Invalid root-leash implies that the transition is empty/no-op, so just do
@@ -307,30 +321,62 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Invalid root leash (%s): %s",
transitionToken, info);
t.apply();
- onFinish(transitionToken, null /* wct */, null /* wctCB */);
+ onAbort(transitionToken);
return;
}
- setupStartState(info, t);
+ final ActiveTransition active = mActiveTransitions.get(activeIdx);
+ active.mInfo = info;
+ active.mStartT = t;
+ active.mFinishT = finishT;
+ if (activeIdx > 0) {
+ // This is now playing at the same time as an existing animation, so try merging it.
+ attemptMergeTransition(mActiveTransitions.get(0), active);
+ return;
+ }
+ // The normal case, just play it.
+ playTransition(active);
+ }
- final TransitionFinishCallback finishCb = (wct, cb) -> onFinish(transitionToken, wct, cb);
- // If a handler chose to uniquely run this animation, try delegating to it.
- if (active.mFirstHandler != null) {
+ /**
+ * Attempt to merge by delegating the transition start to the handler of the currently
+ * playing transition.
+ */
+ void attemptMergeTransition(@NonNull ActiveTransition playing,
+ @NonNull ActiveTransition merging) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
+ + " another transition %s is still animating. Notify the animating transition"
+ + " in case they can be merged", merging.mToken, playing.mToken);
+ playing.mHandler.mergeAnimation(merging.mToken, merging.mInfo, merging.mStartT,
+ playing.mToken, (wct, cb) -> onFinish(merging.mToken, wct, cb));
+ }
+
+ boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) {
+ return handler.startAnimation(active.mToken, active.mInfo, active.mStartT,
+ (wct, cb) -> onFinish(active.mToken, wct, cb));
+ }
+
+ void playTransition(@NonNull ActiveTransition active) {
+ setupStartState(active.mInfo, active.mStartT, active.mFinishT);
+
+ // If a handler already chose to run this animation, try delegating to it first.
+ if (active.mHandler != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
- active.mFirstHandler);
- if (active.mFirstHandler.startAnimation(transitionToken, info, t, finishCb)) {
+ active.mHandler);
+ if (startAnimation(active, active.mHandler)) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
return;
}
}
// Otherwise give every other handler a chance (in order)
for (int i = mHandlers.size() - 1; i >= 0; --i) {
- if (mHandlers.get(i) == active.mFirstHandler) continue;
+ if (mHandlers.get(i) == active.mHandler) continue;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
mHandlers.get(i));
- if (mHandlers.get(i).startAnimation(transitionToken, info, t, finishCb)) {
+ if (startAnimation(active, mHandlers.get(i))) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
mHandlers.get(i));
+ active.mHandler = mHandlers.get(i);
return;
}
}
@@ -338,23 +384,107 @@
"This shouldn't happen, maybe the default handler is broken.");
}
- private void onFinish(IBinder transition, @Nullable WindowContainerTransaction wct,
+ /** Special version of finish just for dealing with no-op/invalid transitions. */
+ private void onAbort(IBinder transition) {
+ final int activeIdx = findActiveTransition(transition);
+ if (activeIdx < 0) return;
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Transition animation aborted due to no-op, notifying core %s", transition);
+ mActiveTransitions.remove(activeIdx);
+ mOrganizer.finishTransition(transition, null /* wct */, null /* wctCB */);
+ }
+
+ private void onFinish(IBinder transition,
+ @Nullable WindowContainerTransaction wct,
@Nullable WindowContainerTransactionCallback wctCB) {
- if (!mActiveTransitions.containsKey(transition)) {
- Log.e(TAG, "Trying to finish a non-running transition. Maybe remote crashed?");
+ int activeIdx = findActiveTransition(transition);
+ if (activeIdx < 0) {
+ Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
+ + " a handler didn't properly deal with a merge.", new RuntimeException());
+ return;
+ } else if (activeIdx > 0) {
+ // This transition was merged.
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s",
+ transition);
+ final ActiveTransition active = mActiveTransitions.get(activeIdx);
+ active.mMerged = true;
+ if (active.mHandler != null) {
+ active.mHandler.onTransitionMerged(active.mToken);
+ }
return;
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Transition animations finished, notifying core %s", transition);
- mActiveTransitions.remove(transition);
+ "Transition animation finished, notifying core %s", transition);
+ // Merge all relevant transactions together
+ SurfaceControl.Transaction fullFinish = mActiveTransitions.get(activeIdx).mFinishT;
+ for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
+ final ActiveTransition toMerge = mActiveTransitions.get(iA);
+ if (!toMerge.mMerged) break;
+ // Include start. It will be a no-op if it was already applied. Otherwise, we need it
+ // to maintain consistent state.
+ fullFinish.merge(mActiveTransitions.get(iA).mStartT);
+ fullFinish.merge(mActiveTransitions.get(iA).mFinishT);
+ }
+ fullFinish.apply();
+ // Now perform all the finishes.
+ mActiveTransitions.remove(activeIdx);
mOrganizer.finishTransition(transition, wct, wctCB);
+ while (activeIdx < mActiveTransitions.size()) {
+ if (!mActiveTransitions.get(activeIdx).mMerged) break;
+ ActiveTransition merged = mActiveTransitions.remove(activeIdx);
+ mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */);
+ }
+ if (mActiveTransitions.size() <= activeIdx) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
+ + "finished");
+ return;
+ }
+ // Start animating the next active transition
+ final ActiveTransition next = mActiveTransitions.get(activeIdx);
+ if (next.mInfo == null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Pending transition after one"
+ + " finished, but it isn't ready yet.");
+ return;
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Pending transitions after one"
+ + " finished, so start the next one.");
+ playTransition(next);
+ // Now try to merge the rest of the transitions (re-acquire activeIdx since next may have
+ // finished immediately)
+ activeIdx = findActiveTransition(next.mToken);
+ if (activeIdx < 0) {
+ // This means 'next' finished immediately and thus re-entered this function. Since
+ // that is the case, just return here since all relevant logic has already run in the
+ // re-entered call.
+ return;
+ }
+
+ // This logic is also convoluted because 'next' may finish immediately in response to any of
+ // the merge requests (eg. if it decided to "cancel" itself).
+ int mergeIdx = activeIdx + 1;
+ while (mergeIdx < mActiveTransitions.size()) {
+ ActiveTransition mergeCandidate = mActiveTransitions.get(mergeIdx);
+ if (mergeCandidate.mMerged) {
+ throw new IllegalStateException("Can't merge a transition after not-merging"
+ + " a preceding one.");
+ }
+ attemptMergeTransition(next, mergeCandidate);
+ mergeIdx = findActiveTransition(mergeCandidate.mToken);
+ if (mergeIdx < 0) {
+ // This means 'next' finished immediately and thus re-entered this function. Since
+ // that is the case, just return here since all relevant logic has already run in
+ // the re-entered call.
+ return;
+ }
+ ++mergeIdx;
+ }
}
void requestStartTransition(@NonNull IBinder transitionToken,
@Nullable TransitionRequestInfo request) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s",
transitionToken, request);
- if (mActiveTransitions.containsKey(transitionToken)) {
+ if (findActiveTransition(transitionToken) >= 0) {
throw new RuntimeException("Transition already started " + transitionToken);
}
final ActiveTransition active = new ActiveTransition();
@@ -362,23 +492,23 @@
for (int i = mHandlers.size() - 1; i >= 0; --i) {
wct = mHandlers.get(i).handleRequest(transitionToken, request);
if (wct != null) {
- active.mFirstHandler = mHandlers.get(i);
+ active.mHandler = mHandlers.get(i);
break;
}
}
- IBinder transition = mOrganizer.startTransition(
+ active.mToken = mOrganizer.startTransition(
request.getType(), transitionToken, wct);
- mActiveTransitions.put(transition, active);
+ mActiveTransitions.add(active);
}
/** Start a new transition directly. */
public IBinder startTransition(@WindowManager.TransitionType int type,
@NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
final ActiveTransition active = new ActiveTransition();
- active.mFirstHandler = handler;
- IBinder transition = mOrganizer.startTransition(type, null /* token */, wct);
- mActiveTransitions.put(transition, active);
- return transition;
+ active.mHandler = handler;
+ active.mToken = mOrganizer.startTransition(type, null /* token */, wct);
+ mActiveTransitions.add(active);
+ return active.mToken;
}
/**
@@ -415,6 +545,32 @@
@NonNull TransitionFinishCallback finishCallback);
/**
+ * Attempts to merge a different transition's animation into an animation that this handler
+ * is currently playing. If a merge is not possible/supported, this should be a no-op.
+ *
+ * This gets called if another transition becomes ready while this handler is still playing
+ * an animation. This is called regardless of whether this handler claims to support that
+ * particular transition or not.
+ *
+ * When this happens, there are 2 options:
+ * 1. Do nothing. This effectively rejects the merge request. This is the "safest" option.
+ * 2. Merge the incoming transition into this one. The implementation is up to this
+ * handler. To indicate that this handler has "consumed" the merge transition, it
+ * must call the finishCallback immediately, or at-least before the original
+ * transition's finishCallback is called.
+ *
+ * @param transition This is the transition that wants to be merged.
+ * @param info Information about what is changing in the transition.
+ * @param t Contains surface changes that resulted from the transition.
+ * @param mergeTarget This is the transition that we are attempting to merge with (ie. the
+ * one this handler is currently already animating).
+ * @param finishCallback Call this if merged. This MUST be called on main thread.
+ */
+ default void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull TransitionFinishCallback finishCallback) { }
+
+ /**
* Potentially handles a startTransition request.
*
* @param transition The transition whose start is being requested.
@@ -427,6 +583,12 @@
@NonNull TransitionRequestInfo request);
/**
+ * Called when a transition which was already "claimed" by this handler has been merged
+ * into another animation. Gives this handler a chance to clean-up any expectations.
+ */
+ default void onTransitionMerged(@NonNull IBinder transition) { }
+
+ /**
* Sets transition animation scale settings value to handler.
*
* @param scale The setting value of transition animation scale.
@@ -438,10 +600,10 @@
private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
@Override
public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
- SurfaceControl.Transaction transaction) throws RemoteException {
- mMainExecutor.execute(() -> {
- Transitions.this.onTransitionReady(iBinder, transitionInfo, transaction);
- });
+ SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
+ throws RemoteException {
+ mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
+ iBinder, transitionInfo, t, finishT));
}
@Override
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 2609258..a7e1d0f 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 @@
}
}
+ @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 105bd82..c1282c9 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 @@
OneHandedAccessibilityUtil mOneHandedAccessibilityUtil;
OneHandedController mSpiedOneHandedController;
OneHandedTimeoutHandler mSpiedTimeoutHandler;
+ OneHandedState mSpiedTransitionState;
@Mock
DisplayController mMockDisplayController;
@@ -99,9 +103,9 @@
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 @@
mMockSettingsUitl,
mOneHandedAccessibilityUtil,
mSpiedTimeoutHandler,
+ mSpiedTransitionState,
mMockUiEventLogger,
mMockOverlayManager,
mMockTaskStackListener,
@@ -139,18 +144,13 @@
@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 @@
@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 @@
@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 @@
@Test
public void testStopOneHandedShouldRemoveTimer() {
- when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(true);
+ mSpiedTransitionState.setState(STATE_ENTERING);
mSpiedOneHandedController.stopOneHanded();
verify(mSpiedTimeoutHandler, atLeastOnce()).removeTimer();
@@ -280,7 +280,7 @@
@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 @@
@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 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 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 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 0000000..89aae65
--- /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 06a6671..b82a8ca 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 @@
public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
OneHandedTimeoutHandler mTimeoutHandler;
OneHandedController mOneHandedController;
+ OneHandedState mSpiedTransitionState;
@Mock
OneHandedGestureHandler mMockGestureHandler;
@@ -73,6 +74,7 @@
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 @@
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 63b9413..882d382 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 @@
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 @@
@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/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 18642fc..08ac2a6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -332,11 +332,18 @@
final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction();
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
- IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
+ throws RemoteException {
mCalled = true;
finishCallback.onTransitionFinished(mRemoteFinishWCT);
}
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index c1733de..2d2ab2c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -107,7 +107,8 @@
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
- transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class));
+ transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
assertEquals(1, mDefaultHandler.activeCount());
mDefaultHandler.finishAll();
mMainExecutor.flushAll();
@@ -155,7 +156,8 @@
transitions.requestStartTransition(transitToken,
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), isNull());
- transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class));
+ transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
assertEquals(1, mDefaultHandler.activeCount());
assertEquals(0, testHandler.activeCount());
mDefaultHandler.finishAll();
@@ -168,7 +170,8 @@
new TransitionRequestInfo(TRANSIT_OPEN, mwTaskInfo, null /* remote */));
verify(mOrganizer, times(1)).startTransition(
eq(TRANSIT_OPEN), eq(transitToken), eq(handlerWCT));
- transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class));
+ transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
assertEquals(1, mDefaultHandler.activeCount());
assertEquals(0, testHandler.activeCount());
mDefaultHandler.finishAll();
@@ -185,7 +188,8 @@
eq(TRANSIT_CHANGE), eq(transitToken), eq(handlerWCT));
TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE)
.addChange(TRANSIT_CHANGE).build();
- transitions.onTransitionReady(transitToken, change, mock(SurfaceControl.Transaction.class));
+ transitions.onTransitionReady(transitToken, change, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
assertEquals(0, mDefaultHandler.activeCount());
assertEquals(1, testHandler.activeCount());
assertEquals(0, topHandler.activeCount());
@@ -203,11 +207,18 @@
final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction();
IRemoteTransition testRemote = new IRemoteTransition.Stub() {
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ public void startAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
finishCallback.onTransitionFinished(remoteFinishWCT);
}
+
+ @Override
+ public void mergeAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ }
};
IBinder transitToken = new Binder();
transitions.requestStartTransition(transitToken,
@@ -215,7 +226,8 @@
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
- transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class));
+ transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
assertEquals(0, mDefaultHandler.activeCount());
assertTrue(remoteCalled[0]);
mDefaultHandler.finishAll();
@@ -269,11 +281,18 @@
final boolean[] remoteCalled = new boolean[]{false};
IRemoteTransition testRemote = new IRemoteTransition.Stub() {
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ public void startAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
finishCallback.onTransitionFinished(null /* wct */);
}
+
+ @Override
+ public void mergeAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ }
};
TransitionFilter filter = new TransitionFilter();
@@ -290,7 +309,8 @@
verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
- transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class));
+ transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
assertEquals(0, mDefaultHandler.activeCount());
assertTrue(remoteCalled[0]);
mDefaultHandler.finishAll();
@@ -308,11 +328,18 @@
final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction();
IRemoteTransition testRemote = new IRemoteTransition.Stub() {
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ public void startAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
finishCallback.onTransitionFinished(remoteFinishWCT);
}
+
+ @Override
+ public void mergeAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ }
};
final int transitType = TRANSIT_FIRST_CUSTOM + 1;
@@ -336,6 +363,86 @@
mock(SurfaceControl.Transaction.class), testFinish));
}
+ @Test
+ public void testTransitionQueueing() {
+ Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
+ mMainExecutor, mAnimExecutor);
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ IBinder transitToken1 = new Binder();
+ transitions.requestStartTransition(transitToken1,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ transitions.onTransitionReady(transitToken1, info1, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+ assertEquals(1, mDefaultHandler.activeCount());
+
+ IBinder transitToken2 = new Binder();
+ transitions.requestStartTransition(transitToken2,
+ new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */));
+ TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ transitions.onTransitionReady(transitToken2, info2, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+ // default handler doesn't merge by default, so it shouldn't increment active count.
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertEquals(0, mDefaultHandler.mergeCount());
+ verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any(), any());
+ verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any());
+
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+ // first transition finished
+ verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any(), any());
+ verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any());
+ // But now the "queued" transition is running
+ assertEquals(1, mDefaultHandler.activeCount());
+
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+ verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any(), any());
+ }
+
+ @Test
+ public void testTransitionMerging() {
+ Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
+ mMainExecutor, mAnimExecutor);
+ mDefaultHandler.setSimulateMerge(true);
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ IBinder transitToken1 = new Binder();
+ transitions.requestStartTransition(transitToken1,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ transitions.onTransitionReady(transitToken1, info1, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+ assertEquals(1, mDefaultHandler.activeCount());
+
+ IBinder transitToken2 = new Binder();
+ transitions.requestStartTransition(transitToken2,
+ new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */));
+ TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ transitions.onTransitionReady(transitToken2, info2, mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class));
+ // it should still only have 1 active, but then show 1 merged
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertEquals(1, mDefaultHandler.mergeCount());
+ verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any(), any());
+ // We don't tell organizer it is finished yet (since we still want to maintain ordering)
+ verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any(), any());
+
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+ // transition + merged all finished.
+ verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any(), any());
+ verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any(), any());
+ // Make sure nothing was queued
+ assertEquals(0, mDefaultHandler.activeCount());
+ }
+
class TransitionInfoBuilder {
final TransitionInfo mInfo;
@@ -364,7 +471,9 @@
}
class TestTransitionHandler implements Transitions.TransitionHandler {
- final ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>();
+ ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>();
+ final ArrayList<IBinder> mMerged = new ArrayList<>();
+ boolean mSimulateMerge = false;
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@@ -374,6 +483,15 @@
return true;
}
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (!mSimulateMerge) return;
+ mMerged.add(transition);
+ finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ }
+
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@@ -381,16 +499,25 @@
return null;
}
+ void setSimulateMerge(boolean sim) {
+ mSimulateMerge = sim;
+ }
+
void finishAll() {
- for (int i = mFinishes.size() - 1; i >= 0; --i) {
- mFinishes.get(i).onTransitionFinished(null /* wct */, null /* wctCB */);
+ final ArrayList<Transitions.TransitionFinishCallback> finishes = mFinishes;
+ mFinishes = new ArrayList<>();
+ for (int i = finishes.size() - 1; i >= 0; --i) {
+ finishes.get(i).onTransitionFinished(null /* wct */, null /* wctCB */);
}
- mFinishes.clear();
}
int activeCount() {
return mFinishes.size();
}
+
+ int mergeCount() {
+ return mMerged.size();
+ }
}
private static SurfaceControl createMockSurface(boolean valid) {
diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp
index f322bff..547d719 100644
--- a/libs/incident/Android.bp
+++ b/libs/incident/Android.bp
@@ -104,7 +104,7 @@
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 307fb87..51586d7 100644
--- a/location/java/android/location/Geocoder.java
+++ b/location/java/android/location/Geocoder.java
@@ -45,6 +45,10 @@
* 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 @@
}
/**
- * 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 @@
}
/**
- * 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.
+ * 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> 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.
+ * <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 @@
}
/**
- * 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.
+ * 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> 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.
+ * <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/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index 2059f02..f6f0a59 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -442,6 +442,7 @@
* camera on the device. If the device has no back-facing camera, this returns null.
* @param quality the target quality level for the camcorder profile
* @see #get(int, int)
+ * @deprecated Use {@link #getAll} instead
*/
public static CamcorderProfile get(int quality) {
int numberOfCameras = Camera.getNumberOfCameras();
@@ -508,6 +509,7 @@
* @see #QUALITY_HIGH_SPEED_720P
* @see #QUALITY_HIGH_SPEED_1080P
* @see #QUALITY_HIGH_SPEED_2160P
+ * @deprecated Use {@link #getAll} instead
* @throws IllegalArgumentException if quality is not one of the defined QUALITY_ values.
*/
public static CamcorderProfile get(int cameraId, int quality) {
@@ -549,9 +551,9 @@
* resolution and higher audio sampling rate, etc, than those with lower quality
* level.
*
- * @param cameraId the id for the camera. Numeric camera ids parsed from the list received by
- * invoking {@link CameraManager#getCameraIdList} can be used as long as they
- * are {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE}
+ * @param cameraId the id for the camera. Numeric camera ids from the list received by invoking
+ * {@link CameraManager#getCameraIdList} can be used as long as they are
+ * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE}
* and not
* {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}.
* @param quality the target quality level for the camcorder profile.
diff --git a/media/java/android/media/EncoderProfiles.java b/media/java/android/media/EncoderProfiles.java
index ca3daef..d9eabbd4 100644
--- a/media/java/android/media/EncoderProfiles.java
+++ b/media/java/android/media/EncoderProfiles.java
@@ -51,14 +51,14 @@
* <li> Number of audio channels for recording.
* </ul>
*/
-public class EncoderProfiles
+public final class EncoderProfiles
{
/**
* Default recording duration in seconds before the session is terminated.
- * This is useful for applications like MMS has limited file size requirement.
+ * This is useful for applications like MMS that have a limited file size requirement.
* This could be 0 if there is no default recording duration.
*/
- public int getDurationSeconds() {
+ public int getDefaultDurationSeconds() {
return durationSecs;
}
@@ -66,19 +66,19 @@
* Recommended output file format
* @see android.media.MediaRecorder.OutputFormat
*/
- public int getFileFormat() {
+ public @MediaRecorder.OutputFormatValues int getRecommendedFileFormat() {
return fileFormat;
}
/**
* Configuration for a video encoder.
*/
- public static class VideoProfile {
+ public final static class VideoProfile {
/**
* The video encoder being used for the video track
* @see android.media.MediaRecorder.VideoEncoder
*/
- public int getCodec() {
+ public @MediaRecorder.VideoEncoderValues int getCodec() {
return codec;
}
@@ -238,12 +238,12 @@
/**
* Configuration for an audio encoder.
*/
- public static class AudioProfile {
+ public final static class AudioProfile {
/**
* The audio encoder being used for the audio track.
* @see android.media.MediaRecorder.AudioEncoder
*/
- public int getCodec() {
+ public @MediaRecorder.AudioEncoderValues int getCodec() {
return codec;
}
@@ -326,11 +326,6 @@
private int bitrate;
}
- //static {
- // System.loadLibrary("media_jni");
- //native_init();
- //}
-
private int durationSecs;
private int fileFormat;
// non-modifiable lists
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 5eb57da..499034e 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -609,6 +609,25 @@
};
/**
+ * @hide
+ */
+ @IntDef({
+ OutputFormat.DEFAULT,
+ OutputFormat.THREE_GPP,
+ OutputFormat.MPEG_4,
+ OutputFormat.AMR_NB,
+ OutputFormat.AMR_WB,
+ OutputFormat.AAC_ADIF,
+ OutputFormat.AAC_ADTS,
+ OutputFormat.MPEG_2_TS,
+ OutputFormat.WEBM,
+ OutputFormat.HEIF,
+ OutputFormat.OGG,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OutputFormatValues {}
+
+ /**
* Defines the audio encoding. These constants are used with
* {@link MediaRecorder#setAudioEncoder(int)}.
*/
@@ -635,6 +654,22 @@
}
/**
+ * @hide
+ */
+ @IntDef({
+ AudioEncoder.DEFAULT,
+ AudioEncoder.AMR_NB,
+ AudioEncoder.AMR_WB,
+ AudioEncoder.AAC,
+ AudioEncoder.HE_AAC,
+ AudioEncoder.AAC_ELD,
+ AudioEncoder.VORBIS,
+ AudioEncoder.OPUS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AudioEncoderValues {}
+
+ /**
* Defines the video encoding. These constants are used with
* {@link MediaRecorder#setVideoEncoder(int)}.
*/
@@ -652,6 +687,20 @@
}
/**
+ * @hide
+ */
+ @IntDef({
+ VideoEncoder.DEFAULT,
+ VideoEncoder.H263,
+ VideoEncoder.H264,
+ VideoEncoder.MPEG_4_SP,
+ VideoEncoder.VP8,
+ VideoEncoder.HEVC,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VideoEncoderValues {}
+
+ /**
* Sets the audio source to be used for recording. If this method is not
* called, the output file will not contain an audio track. The source needs
* to be specified before setting recording-parameters or encoders. Call
@@ -887,7 +936,7 @@
* setAudioSource()/setVideoSource().
* @see android.media.MediaRecorder.OutputFormat
*/
- public native void setOutputFormat(int output_format)
+ public native void setOutputFormat(@OutputFormatValues int output_format)
throws IllegalStateException;
/**
@@ -970,7 +1019,7 @@
* setOutputFormat() or after prepare().
* @see android.media.MediaRecorder.AudioEncoder
*/
- public native void setAudioEncoder(int audio_encoder)
+ public native void setAudioEncoder(@AudioEncoderValues int audio_encoder)
throws IllegalStateException;
/**
@@ -983,7 +1032,7 @@
* setOutputFormat() or after prepare()
* @see android.media.MediaRecorder.VideoEncoder
*/
- public native void setVideoEncoder(int video_encoder)
+ public native void setVideoEncoder(@VideoEncoderValues int video_encoder)
throws IllegalStateException;
/**
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 5bb4fbc..24c93ce 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -64,7 +64,7 @@
"surface_texture.cpp",
"system_fonts.cpp",
"trace.cpp",
- "thermal.cpp"
+ "thermal.cpp",
],
shared_libs: [
@@ -77,7 +77,7 @@
"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 @@
"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 @@
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 @@
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 8b0698b..0030015 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 c8b04a3..a8e2517 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -11,7 +11,7 @@
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 @@
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 3ca7475..52673c9 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -240,7 +240,7 @@
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 5dfa932..164b984 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -4473,7 +4473,7 @@
@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 @@
@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 518d3f3..adcf338 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -892,11 +892,11 @@
* 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 efd3363..4071c9a 100644
--- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -40,10 +40,10 @@
@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 @@
*/
@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 @@
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 @@
@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 515498e..f491cc7 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/Android.bp
+++ b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp
@@ -31,6 +31,10 @@
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
new file mode 100644
index 0000000..bfdc28b
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/resources-certs/com.android.connectivity.resources.pk8
Binary files differ
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 0000000..70eca1c
--- /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 0000000..38771c2
--- /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 9b735fe..d385101 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 a492ec0..eccf6c7 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 6560a18..ed447f8 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 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 @@
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 @@
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 0a8570b..782b483 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 @@
*/
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 9c7aac1..4558a8a 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.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.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 @@
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 @@
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 @@
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 @@
}
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 @@
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 42fb5d0..a9ad00d 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 @@
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 53a99ab..38172f7 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 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 @@
// 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 c0e4df5..8f7f1fa 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 @@
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 635434b..2f54e21 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 @@
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 150d10d..22e38f4 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -231,6 +231,7 @@
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 4fe48c9..8ead0e1 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 @@
* 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 4142e51..b75252b 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 @@
/** 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 @@
/** 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 fffcafb..4d4c909 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 @@
void setQsExpansion(float qsExpansionFraction, float headerTranslation);
void setHeaderListening(boolean listening);
void notifyCustomizeChanged();
-
void setContainer(ViewGroup container);
void setExpandClickListener(OnClickListener onClickListener);
@@ -75,6 +73,16 @@
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 3345e6e..0000000
--- 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 1127d3c..0000000
--- 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/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index 1125092..ea644cf 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 c90fc31..a5e7f5d 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 2402bd7..abb771b 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 343b398..93a4715 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 781c015..87a1bbb 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 12c864c..bea50e8 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 0577d46e..fff4a1b 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 8a3ba4b..c117069 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 885227b..5da75a1 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 68e6ca8..cce27e7 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 1a4e2d1..2b35bcd 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 @@
}
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 dde20c1..1cc488f 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 @@
}
/**
- * 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 2f38f0a..22d934e 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,8 @@
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;
import android.view.IRemoteAnimationFinishedCallback;
@@ -153,7 +155,8 @@
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteTransition.Stub() {
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ public void startAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) {
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
@@ -239,10 +242,16 @@
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"
@@ -256,6 +265,14 @@
appsCompat, wallpapersCompat, nonAppsCompat,
animationFinishedCallback);
}
+
+ @Override
+ public void mergeAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) {
+ // TODO: hook up merge to recents onTaskAppeared if applicable. Until then, ignore
+ // any incoming merges.
+ }
};
}
}
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 26ef145..351dfd5 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,7 +24,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.graphics.Rect;
+import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
@@ -65,7 +67,8 @@
@NonNull Executor executor) {
mTransition = new IRemoteTransition.Stub() {
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishedCallback) {
final Runnable finishAdapter = () -> {
try {
@@ -74,7 +77,22 @@
Log.e(TAG, "Failed to call transition finished callback", e);
}
};
- executor.execute(() -> runner.startAnimation(info, t, finishAdapter));
+ executor.execute(() -> runner.startAnimation(transition, info, t, finishAdapter));
+ }
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishedCallback) {
+ final Runnable finishAdapter = () -> {
+ try {
+ finishedCallback.onTransitionFinished(null /* wct */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call transition finished callback", e);
+ }
+ };
+ executor.execute(() -> runner.mergeAnimation(transition, info, t, mergeTarget,
+ finishAdapter));
}
};
}
@@ -84,7 +102,8 @@
RecentsAnimationControllerCompat controller) {
mTransition = new IRemoteTransition.Stub() {
@Override
- public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishedCallback) {
final RemoteAnimationTargetCompat[] apps =
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
@@ -109,10 +128,17 @@
}
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());
}
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishedCallback) {
+ // TODO: hook up merge to onTaskAppeared. Until then, just ignore incoming merges.
+ }
};
}
@@ -136,10 +162,12 @@
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;
}
@@ -160,14 +188,16 @@
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
@@ -182,6 +212,11 @@
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/shared/src/com/android/systemui/shared/system/RemoteTransitionRunner.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionRunner.java
index 6002bca..accc456 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionRunner.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionRunner.java
@@ -16,6 +16,7 @@
package com.android.systemui.shared.system;
+import android.os.IBinder;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -25,6 +26,17 @@
* Starts a transition animation. Once complete, the implementation should call
* `finishCallback`.
*/
- void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
+ void startAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
Runnable finishCallback);
+
+ /**
+ * Attempts to merge a transition into the currently-running animation. If merge is not
+ * possible/supported, this should do nothing. Otherwise, the implementation should call
+ * `finishCallback` immediately to indicate that it merged the transition.
+ *
+ * @param transition The transition that wants to be merged into the running animation.
+ * @param mergeTarget The transition to merge into (that this runner is currently animating).
+ */
+ default void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget, Runnable finishCallback) { }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index f2bebce..5559a18 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -19,10 +19,12 @@
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.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 @@
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 @@
@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 @@
mBatteryController = batteryController;
mConfigurationController = configurationController;
mSystemUIFactory = systemUIFactory;
+ mActivityStarter = activityStarter;
+ mFalsingManager = falsingManager;
}
/**
@@ -200,6 +211,16 @@
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 fc80dbe..588f4bb 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 @@
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 @@
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/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 68a4194..321c6b7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2112,13 +2112,13 @@
&& mBiometricEnabledForUser.get(getCurrentUser());
final boolean shouldListenBouncerState =
- isUdfps ? !mBouncer
- : !(mFingerprintLockedOut && mBouncer && mCredentialAttempted);
+ !(mFingerprintLockedOut && mBouncer && mCredentialAttempted);
final boolean shouldListenUdfpsState = !isUdfps
|| (!getUserCanSkipBouncer(getCurrentUser())
&& !isEncryptedOrLockdown(getCurrentUser())
&& mStrongAuthTracker.hasUserAuthenticatedSinceBoot());
+
return shouldListenKeyguardState && shouldListenUserState && shouldListenBouncerState
&& shouldListenUdfpsState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 0c7b55d..3d86034 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -265,7 +265,7 @@
/**
* @return where the UDFPS exists on the screen in pixels in portrait mode.
*/
- public RectF getUdfpsRegion() {
+ @Nullable public RectF getUdfpsRegion() {
return mUdfpsController == null
? null
: mUdfpsController.getSensorLocation();
@@ -274,7 +274,7 @@
/**
* @return where the UDFPS exists on the screen in pixels in portrait mode.
*/
- public PointF getUdfpsSensorLocation() {
+ @Nullable public PointF getUdfpsSensorLocation() {
if (mUdfpsController == null) {
return null;
}
@@ -286,7 +286,7 @@
* @return where the face authentication sensor exists relative to the screen in pixels in
* portrait mode.
*/
- public PointF getFaceAuthSensorLocation() {
+ @Nullable public PointF getFaceAuthSensorLocation() {
if (mFaceProps == null || mFaceAuthSensorLocation == null) {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS b/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS
index 8765c9a..947466f 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 47c4a63..7ebfb72 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 @@
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() {
@@ -260,10 +262,36 @@
}
};
- @SuppressLint("ClickableViewAccessibility")
- private final UdfpsView.OnTouchListener mOnTouchListener = this::onTouch;
+ /**
+ * Forwards touches to the udfps controller / view
+ */
+ public boolean onTouch(MotionEvent event) {
+ if (mView == null) {
+ return false;
+ }
+ return onTouch(mView, event, false);
+ }
- private boolean onTouch(View view, MotionEvent event) {
+ @SuppressLint("ClickableViewAccessibility")
+ private final UdfpsView.OnTouchListener mOnTouchListener = (view, event) ->
+ onTouch(view, event, true);
+
+ /**
+ * @param x coordinate
+ * @param y coordinate
+ * @param relativeToUdfpsView true if the coordinates are relative to the udfps view; else,
+ * calculate from the display dimensions in portrait orientation
+ */
+ private boolean isWithinSensorArea(UdfpsView udfpsView, float x, float y,
+ boolean relativeToUdfpsView) {
+ if (relativeToUdfpsView) {
+ // TODO: move isWithinSensorArea to UdfpsController.
+ return udfpsView.isWithinSensorArea(x, y);
+ }
+ return getSensorLocation().contains(x, y);
+ }
+
+ private boolean onTouch(View view, MotionEvent event, boolean fromUdfpsView) {
UdfpsView udfpsView = (UdfpsView) view;
final boolean isFingerDown = udfpsView.isIlluminationRequested();
boolean handled = false;
@@ -281,8 +309,7 @@
// ACTION_DOWN, in that case we should just reuse the old instance.
mVelocityTracker.clear();
}
- // TODO: move isWithinSensorArea to UdfpsController.
- if (udfpsView.isWithinSensorArea(event.getX(), event.getY())) {
+ if (isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView)) {
Trace.beginAsyncSection(
"UdfpsController.mOnTouchListener#isWithinSensorArea", 1);
// The pointer that causes ACTION_DOWN is always at index 0.
@@ -290,37 +317,23 @@
// data for many other pointers because of multi-touch support.
mActivePointerId = event.getPointerId(0);
mVelocityTracker.addMovement(event);
-
- // TODO: (b/185124905) these settings are for ux testing purposes and should
- // be removed (or cached) before going into production
- final ContentResolver contentResolver = mContext.getContentResolver();
- int startEnabled = Settings.Global.getInt(contentResolver,
- "udfps_start", 0);
- if (startEnabled > 0) {
- String startEffectSetting = Settings.Global.getString(contentResolver,
- "udfps_start_type");
- mVibrator.vibrate(getVibration(startEffectSetting, mEffectClick),
- VIBRATION_SONIFICATION_ATTRIBUTES);
- }
-
- int acquiredEnabled = Settings.Global.getInt(contentResolver,
- "udfps_acquired", 0);
- if (acquiredEnabled > 0) {
- int delay = Settings.Global.getInt(contentResolver,
- "udfps_acquired_delay", 500);
- mMainHandler.removeCallbacks(mAcquiredVibration);
- mMainHandler.postDelayed(mAcquiredVibration, delay);
- }
handled = true;
}
break;
case MotionEvent.ACTION_MOVE:
- final int idx = event.findPointerIndex(mActivePointerId);
+ final int idx = mActivePointerId == -1
+ ? event.getPointerId(0)
+ : event.findPointerIndex(mActivePointerId);
if (idx == event.getActionIndex()) {
final float x = event.getX(idx);
final float y = event.getY(idx);
- if (udfpsView.isWithinSensorArea(x, y)) {
+ if (isWithinSensorArea(udfpsView, x, y, fromUdfpsView)) {
+ if (mVelocityTracker == null) {
+ // touches could be injected, so the velocity tracker may not have
+ // been initialized (via ACTION_DOWN).
+ mVelocityTracker = VelocityTracker.obtain();
+ }
mVelocityTracker.addMovement(event);
// Compute pointer velocity in pixels per second.
mVelocityTracker.computeCurrentVelocity(1000);
@@ -328,10 +341,12 @@
final float v = computePointerSpeed(mVelocityTracker, mActivePointerId);
final float minor = event.getTouchMinor(idx);
final float major = event.getTouchMajor(idx);
- final String touchInfo = String.format("minor: %.1f, major: %.1f, v: %.1f",
- minor, major, v);
+ final boolean exceedsVelocityThreshold = v > 750f;
+ final String touchInfo = String.format(
+ "minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
+ minor, major, v, exceedsVelocityThreshold);
final long sinceLastLog = SystemClock.elapsedRealtime() - mTouchLogTime;
- if (!isFingerDown) {
+ if (!isFingerDown && !exceedsVelocityThreshold) {
Trace.endAsyncSection(
"UdfpsController.mOnTouchListener#isWithinSensorArea", 1);
onFingerDown((int) x, (int) y, minor, major);
@@ -339,6 +354,26 @@
mTouchLogTime = SystemClock.elapsedRealtime();
mPowerManager.userActivity(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
+
+ // TODO: this should eventually be removed after ux testing
+ final ContentResolver contentResolver = mContext.getContentResolver();
+ int startEnabled = Settings.Global.getInt(contentResolver,
+ "udfps_start", 0);
+ if (startEnabled > 0) {
+ String startEffectSetting = Settings.Global.getString(
+ contentResolver, "udfps_start_type");
+ mVibrator.vibrate(getVibration(startEffectSetting, mEffectClick),
+ VIBRATION_SONIFICATION_ATTRIBUTES);
+ }
+
+ int acquiredEnabled = Settings.Global.getInt(contentResolver,
+ "udfps_acquired", 0);
+ if (acquiredEnabled > 0) {
+ int delay = Settings.Global.getInt(contentResolver,
+ "udfps_acquired_delay", 500);
+ mMainHandler.removeCallbacks(mAcquiredVibration);
+ mMainHandler.postDelayed(mAcquiredVibration, delay);
+ }
handled = true;
} else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) {
Log.v(TAG, "onTouch | finger move: " + touchInfo);
@@ -353,6 +388,7 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
+ mActivePointerId = -1;
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
@@ -577,7 +613,8 @@
mKeyguardUpdateMonitor,
mFgExecutor,
mDumpManager,
- mKeyguardViewMediator
+ mKeyguardViewMediator,
+ this
);
case IUdfpsOverlayController.REASON_AUTH_BP:
// note: empty controller, currently shows no visual affordance
@@ -679,6 +716,7 @@
// This method can be called from the UI thread.
private void onFingerUp() {
+ mActivePointerId = -1;
mMainHandler.removeCallbacks(mAcquiredVibration);
if (mView == null) {
Log.w(TAG, "Null view in onFingerUp");
@@ -714,6 +752,8 @@
return mEffectTextureTick;
case "tick":
return mEffectTick;
+ case "double_tap":
+ return mDoubleClick;
default:
return defaultEffect;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index 565e65f..37ea251 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -48,6 +48,7 @@
private ImageView mBgProtection;
private AnimatorSet mAnimatorSet;
+ private int mAlpha; // 0-255
public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -97,9 +98,20 @@
}
}
+ /**
+ * @param alpha between 0 and 255
+ */
+ void setUnpausedAlpha(int alpha) {
+ mAlpha = alpha;
+ updateAlpha();
+ }
+
@Override
int calculateAlpha() {
- return mPauseAuth ? 0 : 255;
+ if (mPauseAuth) {
+ return 0;
+ }
+ return mAlpha;
}
void onDozeAmountChanged(float linear, float eased) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 63af6aa..4d2f809 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -20,6 +20,8 @@
import android.annotation.NonNull;
import android.hardware.biometrics.BiometricSourceType;
+import android.util.MathUtils;
+import android.view.MotionEvent;
import androidx.annotation.Nullable;
@@ -29,6 +31,7 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -50,6 +53,7 @@
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final DelayableExecutor mExecutor;
@NonNull private final KeyguardViewMediator mKeyguardViewMediator;
+ @NonNull private final UdfpsController mUdfpsController;
@Nullable private Runnable mCancelRunnable;
private boolean mShowingUdfpsBouncer;
@@ -60,6 +64,13 @@
private int mStatusBarState;
private boolean mKeyguardIsVisible;
+ /**
+ * hidden amount of pin/pattern/password bouncer
+ * {@link KeyguardBouncer#EXPANSION_VISIBLE} (0f) to
+ * {@link KeyguardBouncer#EXPANSION_HIDDEN} (1f)
+ */
+ private float mInputBouncerHiddenAmount;
+
protected UdfpsKeyguardViewController(
@NonNull UdfpsKeyguardView view,
@NonNull StatusBarStateController statusBarStateController,
@@ -68,12 +79,14 @@
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@NonNull DelayableExecutor mainDelayableExecutor,
@NonNull DumpManager dumpManager,
- @NonNull KeyguardViewMediator keyguardViewMediator) {
+ @NonNull KeyguardViewMediator keyguardViewMediator,
+ @NonNull UdfpsController udfpsController) {
super(view, statusBarStateController, statusBar, dumpManager);
mKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mExecutor = mainDelayableExecutor;
mKeyguardViewMediator = keyguardViewMediator;
+ mUdfpsController = udfpsController;
}
@Override
@@ -89,11 +102,14 @@
updateFaceDetectRunning(mKeyguardUpdateMonitor.isFaceDetectionRunning());
final float dozeAmount = mStatusBarStateController.getDozeAmount();
- mStatusBarStateController.addCallback(mStateListener);
mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
+ mStatusBarStateController.addCallback(mStateListener);
+
mStatusBarState = mStatusBarStateController.getState();
mQsExpanded = mKeyguardViewManager.isQsExpanded();
mKeyguardIsVisible = mKeyguardUpdateMonitor.isKeyguardVisible();
+ mInputBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN;
+ updateAlpha();
updatePauseAuth();
mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
@@ -124,6 +140,8 @@
pw.println("mStatusBarState" + StatusBarState.toShortString(mStatusBarState));
pw.println("mQsExpanded=" + mQsExpanded);
pw.println("mKeyguardVisible=" + mKeyguardIsVisible);
+ pw.println("mInputBouncerHiddenAmount=" + mInputBouncerHiddenAmount);
+ pw.println("mAlpha=" + mView.getAlpha());
}
/**
@@ -140,7 +158,6 @@
if (mShowingUdfpsBouncer) {
mView.animateUdfpsBouncer();
} else {
- // TODO: beverlyt, we not always want to cancelPostAuthActions
mView.animateAwayUdfpsBouncer(() -> mKeyguardViewManager.cancelPostAuthActions());
}
return true;
@@ -172,6 +189,10 @@
return true;
}
+ if (mInputBouncerHiddenAmount < .4f) {
+ return true;
+ }
+
return false;
}
@@ -228,6 +249,14 @@
}
}
+ private void updateAlpha() {
+ // fade icon on transition to showing bouncer
+ int alpha = mShowingUdfpsBouncer ? 255
+ : Math.abs((int) MathUtils.map(.4f, 0f, .7f, 255f,
+ mInputBouncerHiddenAmount));
+ mView.setUnpausedAlpha(alpha);
+ }
+
private final StatusBarStateController.StateListener mStateListener =
new StatusBarStateController.StateListener() {
@Override
@@ -315,6 +344,18 @@
}
@Override
+ public boolean onTouch(MotionEvent event) {
+ return mUdfpsController.onTouch(event);
+ }
+
+ @Override
+ public void setBouncerExpansionChanged(float expansion) {
+ mInputBouncerHiddenAmount = expansion;
+ updateAlpha();
+ updatePauseAuth();
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println(getTag());
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 01d5959..9e621b3 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 @@
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 @@
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 @@
}
@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 206af31..94e5c8a 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 @@
@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 e557773..e8445d4 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 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 1723291..6b819fb 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 @@
}
@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 d8ade2b..4196465 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 @@
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 5cea31b..39adabb 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 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 @@
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 ee55965..c45eb35 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.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.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 @@
/** 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 @@
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 @@
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 @@
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 @@
mBroadcastDispatcher = broadcastDispatcher;
mAuthController = authController;
mMainExecutor = mainExecutor;
+ mUiEventLogger = uiEventLogger;
}
@Override
@@ -328,11 +325,8 @@
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 @@
}
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 @@
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 @@
}, !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 e44e305..19edeb8 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.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.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 767d7ab..411e0f0 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.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/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 495461e..94cb2dc 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.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;
@@ -519,7 +520,8 @@
mediaCoverImageView.setImageIcon(recommendation.getIcon());
// Set up the click listener if applicable.
- setSmartspaceOnClickListener(mediaCoverImageView, recommendation, callback);
+ setSmartspaceOnClickListener(mediaCoverImageView, recommendation,
+ target.getSmartspaceTargetId(), callback);
setVisibleAndAlpha(expandedSet, mediaCoverItemsResIds.get(i), true);
setVisibleAndAlpha(expandedSet, mediaLogoItemsResIds.get(i), true);
@@ -614,6 +616,7 @@
private void setSmartspaceOnClickListener(
@NonNull View view,
@NonNull SmartspaceAction action,
+ @NonNull String targetId,
@Nullable View.OnClickListener callback) {
if (view == null || action == null || action.getIntent() == null) {
Log.e(TAG, "No tap action can be set up");
@@ -621,6 +624,16 @@
}
view.setOnClickListener(v -> {
+ // When media recommendation card is shown, there could be only one card.
+ SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
+ 760, // SMARTSPACE_CARD_CLICK
+ targetId.hashCode(),
+ SysUiStatsLog
+ .SMART_SPACE_CARD_REPORTED__CARD_TYPE__HEADPHONE_MEDIA_RECOMMENDATIONS,
+ getSurfaceForSmartspaceLogging(mMediaViewController.getCurrentEndLocation()),
+ /* rank */ 1,
+ /* cardinality */ 1);
+
mActivityStarter.postStartActivityDismissingKeyguard(
action.getIntent(),
0 /* delay */,
@@ -630,4 +643,14 @@
}
});
}
+
+ 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 0ed96ee..6ef29d6 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 @@
/**
* 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 a274eab..5f73ae0 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.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 @@
// 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 @@
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 @@
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,7 +187,8 @@
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()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 5ba04a0..7807176 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 @@
* 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
@@ -470,12 +470,13 @@
}
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 +587,9 @@
}
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 +599,8 @@
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 +726,7 @@
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 80d1371..b0be576 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 @@
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 ce72991..8bfe94b 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 @@
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 @@
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 @@
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 @@
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 7cfe4c4..e848e2f 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 @@
* 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
diff --git a/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java b/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java
index 68a829c..0efef02 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 @@
/** 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 @@
}
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 ff14abe..98d8866 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 @@
/** 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 eefe5ca..99a17ffb 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 @@
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 @@
// Reset notification content.
.setNotificationKey(null)
.setNotificationContent(null)
+ .setNotificationSender(null)
.setNotificationDataUri(null)
.setMessagesCount(0)
// Reset missed calls category.
@@ -245,9 +247,9 @@
* {@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 @@
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 @@
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 @@
}
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 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 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 145fee5..81df107 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.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 @@
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 @@
TypedValue typedValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
mAccentColor = context.getColor(typedValue.resourceId);
+ mContext = context;
}
@@ -69,7 +73,7 @@
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 @@
@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 dd89f74..d4ddc65 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.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;
@@ -94,7 +95,7 @@
private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL = 6 + 4 + 8;
private static final int FIXED_WIDTH_DIMENS_FOR_SMALL = 4 + 4;
- private static final int MESSAGES_COUNT_OVERFLOW = 7;
+ private static final int MESSAGES_COUNT_OVERFLOW = 6;
private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
@@ -148,6 +149,8 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
private RemoteViews createNotificationRemoteViews() {
RemoteViews views = getViewForContentLayout();
+ CharSequence sender = mTile.getNotificationSender();
Uri image = mTile.getNotificationDataUri();
if (image != null) {
// TODO: Use NotificationInlineImageCache
@@ -364,7 +373,7 @@
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,13 +389,16 @@
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;
}
- // Some messaging apps only include up to 7 messages in their notifications.
+ // Some messaging apps only include up to 6 messages in their notifications.
private String getMessagesCountText(int count) {
if (count >= MESSAGES_COUNT_OVERFLOW) {
return mContext.getResources().getString(
@@ -412,7 +424,7 @@
}
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 7254eec..73c43eb 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 @@
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 f11c1bd..64a6509 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 @@
@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 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 @@
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 @@
/** 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 @@
* 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 @@
}
/** 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 @@
}
}
- 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 @@
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 @@
}
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 @@
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 @@
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 309b32f..cd36091 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 @@
private final int mTouchSlop;
private float mDownY;
+ private boolean mScrollEnabled = true;
public NonInterceptingScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -85,6 +86,16 @@
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 @@
}
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 cefcd4a..294d765 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 @@
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 @@
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 @@
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 @@
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 6b09e2e..e89803d 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 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 @@
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 @@
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 @@
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 @@
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 void setExpansion(float expansion) {
mQsExpansion = expansion;
+ mQSPanelContainer.setScrollingEnabled(expansion > 0.0f);
updateExpansion();
}
@@ -318,4 +336,46 @@
}
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 b95194a..d5cb777 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 @@
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 @@
}
}
+ @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 @@
}
@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/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index e055585..45c7174 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles;
-import static android.content.pm.PackageManager.FEATURE_CAMERA_TOGGLE;
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -63,7 +62,7 @@
@Override
public boolean isAvailable() {
- return getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE)
+ return mSensorPrivacyController.supportsSensorToggle(CAMERA)
&& whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
"camera_toggle_enabled",
true));
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 2168b1f..71f42d8 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.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 @@
) {
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 @@
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 @@
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/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index 9c01bb9..48a1ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles;
-import static android.content.pm.PackageManager.FEATURE_MICROPHONE_TOGGLE;
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -63,8 +62,7 @@
@Override
public boolean isAvailable() {
- return getHost().getContext().getPackageManager()
- .hasSystemFeature(FEATURE_MICROPHONE_TOGGLE)
+ return mSensorPrivacyController.supportsSensorToggle(MICROPHONE)
&& whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
"mic_toggle_enabled",
true));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 3d9c272..f13576c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -49,7 +49,7 @@
IndividualSensorPrivacyController.Callback {
private final KeyguardStateController mKeyguard;
- private IndividualSensorPrivacyController mSensorPrivacyController;
+ protected IndividualSensorPrivacyController mSensorPrivacyController;
/**
* @return Id of the sensor that will be toggled
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 6d23739..850f8a5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -524,6 +524,8 @@
startingwindow.createExternalInterface().asBinder()));
try {
+ Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.onInitialize: curUser="
+ + mCurrentBoundedUserId);
mOverviewProxy.onInitialize(params);
} catch (RemoteException e) {
mCurrentBoundedUserId = -1;
@@ -554,6 +556,7 @@
@Override
public void onServiceDisconnected(ComponentName name) {
+ Log.w(TAG_OPS, "Service disconnected");
// Do nothing
mCurrentBoundedUserId = -1;
}
@@ -606,6 +609,7 @@
// Listen for nav bar mode changes
mNavBarMode = navModeController.addListener(this);
+ Log.d(TAG_OPS + " b/182478748", "OverviewProxyService: mode=" + mNavBarMode);
// Listen for launcher package changes
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
@@ -765,6 +769,7 @@
mOverviewServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
UserHandle.of(getCurrentUserId()));
+ Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.connect: bound=" + mBound);
} catch (SecurityException e) {
Log.e(TAG_OPS, "Unable to bind because of security error", e);
}
@@ -817,6 +822,9 @@
private void disconnectFromLauncherService() {
if (mBound) {
+ Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.disconnect: curUser="
+ + mCurrentBoundedUserId);
+
// Always unbind the service (ie. if called through onNullBinding or onBindingDied)
mContext.unbindService(mOverviewServiceConnection);
mBound = false;
@@ -917,27 +925,38 @@
}
private void updateEnabledState() {
+ final int currentUser = ActivityManagerWrapper.getInstance().getCurrentUserId();
mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
- MATCH_SYSTEM_ONLY,
- ActivityManagerWrapper.getInstance().getCurrentUserId()) != null;
+ MATCH_SYSTEM_ONLY, currentUser) != null;
+ Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.updateEnabledState: curUser="
+ + currentUser + " enabled=" + mIsEnabled);
}
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
+ Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.onNavModeChanged: mode=" + mode);
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(TAG_OPS + " state:");
- pw.print(" recentsComponentName="); pw.println(mRecentsComponentName);
pw.print(" isConnected="); pw.println(mOverviewProxy != null);
- pw.print(" connectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
-
- pw.print(" quickStepIntent="); pw.println(mQuickStepIntent);
- pw.print(" quickStepIntentResolved="); pw.println(isEnabled());
+ pw.print(" mIsEnabled="); pw.println(isEnabled());
+ pw.print(" mRecentsComponentName="); pw.println(mRecentsComponentName);
+ pw.print(" mQuickStepIntent="); pw.println(mQuickStepIntent);
+ pw.print(" mBound="); pw.println(mBound);
+ pw.print(" mCurrentBoundedUserId="); pw.println(mCurrentBoundedUserId);
+ pw.print(" mConnectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
+ pw.print(" mInputFocusTransferStarted="); pw.println(mInputFocusTransferStarted);
+ pw.print(" mInputFocusTransferStartY="); pw.println(mInputFocusTransferStartY);
+ pw.print(" mInputFocusTransferStartMillis="); pw.println(mInputFocusTransferStartMillis);
+ pw.print(" mWindowCornerRadius="); pw.println(mWindowCornerRadius);
+ pw.print(" mSupportsRoundedCornersOnWindows="); pw.println(mSupportsRoundedCornersOnWindows);
+ pw.print(" mNavBarButtonAlpha="); pw.println(mNavBarButtonAlpha);
+ pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion);
+ pw.print(" mNavBarMode="); pw.println(mNavBarMode);
mSysUiState.dump(fd, pw, args);
- pw.print(" mInputFocusTransferStarted="); pw.println(mInputFocusTransferStarted);
}
public interface OverviewProxyListener {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 0106f43..b5e51c6 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_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 @@
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 @@
mContext = context;
mScreenshotSmartActions = screenshotSmartActions;
mImageData = new ScreenshotController.SavedImageData();
+ mQuickShareData = new ScreenshotController.QuickShareData();
mSharedElementTransition = sharedElementTransition;
mImageExporter = exporter;
@@ -127,6 +131,13 @@
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 @@
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 @@
smartActions.addAll(buildSmartActions(
mScreenshotSmartActions.getSmartActions(
mScreenshotId, smartActionsFuture, timeoutMs,
- mSmartActionsProvider),
+ mSmartActionsProvider, REGULAR_SMART_ACTIONS),
mContext));
}
@@ -157,6 +168,8 @@
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 @@
}
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 @@
// 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 @@
.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 9d01986..7c2d476 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 Bitmap image;
public Consumer<Uri> finisher;
public ScreenshotController.ActionsReadyListener mActionsReadyListener;
+ public ScreenshotController.QuickShareActionReadyListener mQuickShareActionsReadyListener;
void clearImage() {
image = null;
@@ -129,6 +130,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 @@
}
/**
+ * 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 9bd7923..99238cd 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 @@
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 @@
? 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 @@
@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 @@
} 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 @@
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 d15f1ff..70b1133 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 @@
private ScreenshotActionChip mShareChip;
private ScreenshotActionChip mEditChip;
private ScreenshotActionChip mScrollChip;
+ private ScreenshotActionChip mQuickShareChip;
private UiEventLogger mUiEventLogger;
private ScreenshotViewCallback mCallbacks;
@@ -151,7 +152,8 @@
private enum PendingInteraction {
PREVIEW,
EDIT,
- SHARE
+ SHARE,
+ QUICK_SHARE
}
public ScreenshotView(Context context) {
@@ -505,6 +507,9 @@
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 @@
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 @@
mScreenshotPreview.setOnClickListener(v -> {
mShareChip.setIsPending(false);
mEditChip.setIsPending(false);
+ if (mQuickShareChip != null) {
+ mQuickShareChip.setIsPending(false);
+ }
mPendingInteraction = PendingInteraction.PREVIEW;
});
@@ -582,6 +593,13 @@
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 @@
case EDIT:
mEditChip.callOnClick();
break;
+ case QUICK_SHARE:
+ mQuickShareChip.callOnClick();
+ break;
}
} else {
LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -615,6 +636,25 @@
}
}
+ 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 @@
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
similarity index 77%
rename from core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java
rename to packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index 1fc126e..0a55fbe 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 @@
private float mCornerRadius;
private Rect mBounds;
private ConcaveInfo mConcaveInfo;
+ private int mBottomEdgePosition;
public ScrimDrawable() {
mPaint = new Paint();
@@ -143,21 +144,43 @@
* 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 @@
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
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
rename to packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index a537299..0d9ade6d 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 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 @@
});
}
+ /**
+ * 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 void setDrawable(Drawable drawable) {
+ @VisibleForTesting
+ void setDrawable(Drawable drawable) {
executeOnExecutor(() -> {
mDrawable = drawable;
mDrawable.setCallback(this);
@@ -144,16 +147,24 @@
});
}
+ /**
+ * 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 @@
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 @@
} 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 @@
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 @@
* 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 @@
}
/**
+ * 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/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 6f6bf82..a4bb095 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -111,9 +111,10 @@
}
sensorPrivacyController.addCallback(sensorPrivacyListener)
- if (!sensorPrivacyController.isSensorBlocked(sensor)) {
- finish()
- return
+ sensorPrivacyController.addCallback { _, isBlocked ->
+ if (!isBlocked) {
+ dismiss()
+ }
}
}
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 357256c..1ad253e 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.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 @@
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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
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 @@
final float minBacklight;
final float maxBacklight;
final int metric;
- final String settingToChange;
if (mIsVrModeEnabled) {
metric = MetricsEvent.ACTION_BRIGHTNESS_FOR_VR;
@@ -347,12 +347,12 @@
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 @@
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 @@
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 f4266a2..8264a9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -692,7 +692,6 @@
private void setHideBackground(boolean hideBackground) {
if (mHideBackground != hideBackground) {
mHideBackground = hideBackground;
- updateBackground();
updateOutline();
}
}
@@ -702,10 +701,6 @@
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) {
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 48e28f7..3bf1ff2 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.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 @@
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 @@
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 @@
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/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index f8543f7..b237f6f 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.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 @@
*/
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 @@
private int mNormalRippleColor;
private Gefingerpoken mTouchHandler;
- private boolean mDimmed;
-
int mBgTint = NO_COLOR;
/**
@@ -115,7 +108,6 @@
private Interpolator mCurrentAlphaInterpolator;
NotificationBackgroundView mBackgroundNormal;
- private NotificationBackgroundView mBackgroundDimmed;
private ObjectAnimator mBackgroundAnimator;
private RectF mAppearAnimationRect = new RectF();
private float mAnimationTranslationY;
@@ -129,13 +121,11 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
}
void makeActive() {
- startActivateAnimation(false /* reverse */);
mActivated = true;
if (mOnActivatedListener != null) {
mOnActivatedListener.onActivated(this);
@@ -296,106 +263,18 @@
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 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 @@
if (lastInSection != mLastInSection) {
super.setLastInSection(lastInSection);
mBackgroundNormal.setLastInSection(lastInSection);
- mBackgroundDimmed.setLastInSection(lastInSection);
}
}
@@ -467,7 +344,6 @@
if (firstInSection != mFirstInSection) {
super.setFirstInSection(firstInSection);
mBackgroundNormal.setFirstInSection(firstInSection);
- mBackgroundDimmed.setFirstInSection(firstInSection);
}
}
@@ -486,13 +362,6 @@
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 @@
mBackgroundColorAnimator.cancel();
}
int rippleColor = getRippleColor();
- mBackgroundDimmed.setRippleColor(rippleColor);
mBackgroundNormal.setRippleColor(rippleColor);
int color = calculateBgColor();
if (!animated) {
@@ -537,110 +405,12 @@
// 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 @@
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 @@
}
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 @@
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 edd97af..0a63e19 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 @@
mExpandableOutlineViewController.init();
mView.setOnTouchListener(mTouchHandler);
mView.setTouchHandler(mTouchHandler);
- mView.setOnDimmedListener(dimmed -> mNeedsDimming = dimmed);
mView.setAccessibilityManager(mAccessibilityManager);
}
@@ -116,14 +115,8 @@
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 @@
@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 d21af11..b5d2ea5 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 @@
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 @@
mNotificationParent = isChildInGroup ? parent : null;
mPrivateLayout.setIsChildInGroup(isChildInGroup);
- resetBackgroundAlpha();
updateBackgroundForGroupState();
updateClickAndFocus();
if (mNotificationParent != null) {
@@ -895,11 +883,6 @@
}
@Override
- protected boolean shouldHideBackground() {
- return super.shouldHideBackground() || mShowNoBackground;
- }
-
- @Override
public boolean isSummaryWithChildren() {
return mIsSummaryWithChildren;
}
@@ -2873,7 +2856,6 @@
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 a90e76b..884ad8d 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 @@
endColor = NotificationUtils.interpolateColors(startColor, endColor,
transformationAmount);
}
- mContainingNotification.updateBackgroundAlpha(transformationAmount);
mContainingNotification.setContentBackground(endColor, false, this);
}
@@ -868,7 +867,6 @@
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 4fc49ed..36a370c 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 @@
return mView.isLayoutRtl();
}
- public float getLeft() {
+ public int getLeft() {
return mView.getLeft();
}
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 a4ee9ee..5399094 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 @@
Trace.endSection();
break;
case MODE_UNLOCK_COLLAPSING:
- case MODE_SHOW_BOUNCER:
- Trace.beginSection("MODE_UNLOCK_COLLAPSING or MODE_SHOW_BOUNCER");
+ Trace.beginSection("MODE_UNLOCK_COLLAPSING");
if (!wasDeviceInteractive) {
mPendingShowBouncer = true;
} else {
showBouncer();
- mKeyguardViewController.notifyKeyguardAuthenticated(false /* strongAuth */);
+ mKeyguardViewController.notifyKeyguardAuthenticated(
+ false /* strongAuth */);
+ }
+ Trace.endSection();
+ break;
+ case MODE_SHOW_BOUNCER:
+ Trace.beginSection("MODE_SHOW_BOUNCER");
+ if (!wasDeviceInteractive) {
+ mPendingShowBouncer = true;
+ } else {
+ showBouncer();
}
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 48c7b89..76657ad 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 @@
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 @@
private OngoingCallController mOngoingCallController;
private final SystemStatusAnimationScheduler mAnimationScheduler;
private final PrivacyDotViewController mDotViewController;
+ private NotificationIconAreaController mNotificationIconAreaController;
private List<String> mBlockedIcons = new ArrayList<>();
@@ -106,13 +108,11 @@
@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 CollapsedStatusBarFragment(
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
- PrivacyDotViewController dotViewController
+ PrivacyDotViewController dotViewController,
+ NotificationIconAreaController notificationIconAreaController
) {
mOngoingCallController = ongoingCallController;
mAnimationScheduler = animationScheduler;
mDotViewController = dotViewController;
+ mNotificationIconAreaController = notificationIconAreaController;
}
@Override
@@ -168,6 +170,7 @@
showClock(false);
initEmergencyCryptkeeperText();
initOperatorName();
+ initNotificationIconArea();
mAnimationScheduler.addCallback(this);
}
@@ -204,11 +207,11 @@
}
}
- 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 @@
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 @@
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 @@
state |= DISABLE_CLOCK;
}
- if (mOngoingCallController.hasOngoingCall()) {
- state |= DISABLE_NOTIFICATION_ICONS;
- }
-
if (!mKeyguardStateController.isLaunchTransitionFadingAway()
&& !mKeyguardStateController.isKeyguardFadingAway()
&& shouldHideNotificationIcons()
@@ -304,9 +304,40 @@
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 @@
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 @@
@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 8cef23f..cabfbca 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 @@
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/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 5ff9b703..ae68462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -59,8 +59,8 @@
private static final String TAG = "KeyguardBouncer";
static final long BOUNCER_FACE_DELAY = 1200;
public static final float ALPHA_EXPANSION_THRESHOLD = 0.95f;
- static final float EXPANSION_HIDDEN = 1f;
- static final float EXPANSION_VISIBLE = 0f;
+ public static final float EXPANSION_HIDDEN = 1f;
+ public static final float EXPANSION_VISIBLE = 0f;
protected final Context mContext;
protected final ViewMediatorCallback mCallback;
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 40680dc..9449836 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.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.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 @@
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 @@
private boolean mDelayShowingKeyguardStatusBar;
private boolean mAnimatingQS;
+ private final Rect mKeyguardStatusAreaClipBounds = new Rect();
private int mOldLayoutDirection;
private NotificationShelfController mNotificationShelfController;
@@ -522,6 +526,7 @@
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 @@
}
};
+ 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 @@
statusBarKeyguardViewManager, latencyTracker, flingAnimationUtilsBuilder.get(),
statusBarTouchableRegionManager, ambientState);
mView = view;
+ mVibratorHelper = vibratorHelper;
mMetricsLogger = metricsLogger;
mActivityManager = activityManager;
mConfigurationController = configurationController;
@@ -628,6 +642,7 @@
mDozeParameters = dozeParameters;
mBiometricUnlockController = biometricUnlockController;
mScrimController = scrimController;
+ mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade);
mUserManager = userManager;
mMediaDataManager = mediaDataManager;
mQuickAccessWalletClient = quickAccessWalletClient;
@@ -852,10 +867,16 @@
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 @@
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 @@
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 @@
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 @@
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(
@@ -3439,9 +3485,6 @@
return new TouchHandler() {
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) {
- return true;
- }
if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) {
return false;
}
@@ -3928,7 +3971,10 @@
// 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);
@@ -3981,6 +4027,7 @@
// window, so
// force a call to onThemeChanged
mConfigurationListener.onThemeChanged();
+ mFalsingManager.addTapListener(mFalsingTapListener);
}
@Override
@@ -3989,6 +4036,7 @@
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mConfigurationController.removeCallback(mConfigurationListener);
mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+ mFalsingManager.removeTapListener(mFalsingTapListener);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 5b09c72..4d70237 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -87,6 +87,7 @@
private final ShadeController mShadeController;
private final NotificationShadeDepthController mDepthController;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
+ private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private GestureDetector mGestureDetector;
private View mBrightnessMirror;
@@ -135,7 +136,8 @@
NotificationShadeWindowView notificationShadeWindowView,
NotificationPanelViewController notificationPanelViewController,
SuperStatusBarViewFactory statusBarViewFactory,
- NotificationStackScrollLayoutController notificationStackScrollLayoutController) {
+ NotificationStackScrollLayoutController notificationStackScrollLayoutController,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
mInjectionInflationController = injectionInflationController;
mCoordinator = coordinator;
mPulseExpansionHandler = pulseExpansionHandler;
@@ -159,6 +161,7 @@
mDepthController = depthController;
mStatusBarViewFactory = statusBarViewFactory;
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -239,6 +242,7 @@
}
mFalsingCollector.onTouchEvent(ev);
mGestureDetector.onTouchEvent(ev);
+ mStatusBarKeyguardViewManager.onTouch(ev);
if (mBrightnessMirror != null
&& mBrightnessMirror.getVisibility() == View.VISIBLE) {
// Disallow new pointers while the brightness mirror is visible. This is so that
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 064086a..5a2a6f2 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 @@
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 @@
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 5e9c758..0d96ead 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.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 @@
* 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 @@
// 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 @@
private int mInFrontTint;
private int mBehindTint;
+ private int mNotificationsTint;
private int mBubbleTint;
private boolean mWallpaperVisibilityTimedOut;
@@ -265,6 +268,7 @@
mScrimForBubble = scrimForBubble;
updateThemeColors();
+ behindScrim.enableBottomEdgeConcave(mClipsQsScrim);
mNotificationsScrim.enableRoundedCorners();
if (mScrimBehindChangeRunnable != null) {
@@ -331,14 +335,17 @@
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 @@
scheduleUpdate();
}
- dispatchScrimState(mScrimBehind.getViewAlpha());
+ dispatchBackScrimState(mScrimBehind.getViewAlpha());
}
private boolean shouldFadeAwayWallpaper() {
@@ -491,21 +498,25 @@
*/
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,22 +528,26 @@
}
}
- 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) {
@@ -541,7 +556,8 @@
}
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 @@
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 @@
// 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 @@
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 @@
&& !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 @@
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 @@
} 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 @@
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 @@
}
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 a9774d8..66cc26f 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 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 @@
}
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 @@
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 @@
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 @@
@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 @@
mBubbleTint = Color.BLACK;
mBlankScreen = true;
}
+
+ if (mClipQsScrim) {
+ updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
+ }
}
},
@@ -279,12 +297,14 @@
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 @@
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 @@
return mBehindAlpha;
}
+ public float getNotifAlpha() {
+ return mNotifAlpha;
+ }
+
public float getBubbleAlpha() {
return mBubbleAlpha;
}
@@ -337,6 +362,10 @@
return mBehindTint;
}
+ public int getNotifTint() {
+ return mNotifTint;
+ }
+
public int getBubbleTint() {
return mBubbleTint;
}
@@ -406,4 +435,8 @@
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 026072b..9d0285a 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.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.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.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 @@
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 @@
new CollapsedStatusBarFragment(
mOngoingCallController,
mAnimationScheduler,
- mDotViewController),
+ mDotViewController,
+ mNotificationIconAreaController),
CollapsedStatusBarFragment.TAG)
.commit();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 1980d97..ed63a227 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -29,6 +29,7 @@
import android.os.Bundle;
import android.os.SystemClock;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
@@ -119,6 +120,13 @@
@Override
public void onFullyHidden() {
+ }
+
+ @Override
+ public void onExpansionChanged(float expansion) {
+ if (mAlternateAuthInterceptor != null) {
+ mAlternateAuthInterceptor.setBouncerExpansionChanged(expansion);
+ }
updateStates();
}
};
@@ -1072,6 +1080,17 @@
|| mAlternateAuthInterceptor.isAnimating());
}
+ /**
+ * Forward touches to any alternate authentication affordances.
+ */
+ public boolean onTouch(MotionEvent event) {
+ if (mAlternateAuthInterceptor == null) {
+ return false;
+ }
+
+ return mAlternateAuthInterceptor.onTouch(event);
+ }
+
/** Update keyguard position based on a tapped X coordinate. */
public void updateKeyguardPosition(float x) {
if (mBouncer != null) {
@@ -1130,5 +1149,18 @@
* Set whether qs is currently expanded.
*/
void setQsExpanded(boolean expanded);
+
+ /**
+ * Forward potential touches to authentication interceptor
+ * @return true if event was handled
+ */
+ boolean onTouch(MotionEvent event);
+
+ /**
+ * Update pin/pattern/password bouncer expansion amount where 0 is visible and 1 is fully
+ * hidden
+ */
+ void setBouncerExpansionChanged(float expansion);
+
}
-}
+}
\ No newline at end of file
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 088f947..2b5caf9 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 @@
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 96473c2..51bb643 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 @@
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/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
index 4e6db40..d7d1e73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
@@ -22,6 +22,8 @@
CallbackController<IndividualSensorPrivacyController.Callback> {
void init();
+ boolean supportsSensorToggle(@Sensor int sensor);
+
boolean isSensorBlocked(@Sensor int sensor);
void setSensorBlocked(@Sensor int sensor, boolean blocked);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index beb4b44..f58a7c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -45,13 +45,18 @@
public void init() {
for (int sensor : SENSORS) {
mSensorPrivacyManager.addSensorPrivacyListener(sensor,
- (enabled) -> onSensorPrivacyChanged(sensor, enabled));
+ (s, enabled) -> onSensorPrivacyChanged(sensor, enabled));
mState.put(sensor, mSensorPrivacyManager.isSensorPrivacyEnabled(sensor));
}
}
@Override
+ public boolean supportsSensorToggle(int sensor) {
+ return mSensorPrivacyManager.supportsSensorToggle(sensor);
+ }
+
+ @Override
public boolean isSensorBlocked(@Sensor int sensor) {
return mState.get(sensor, false);
}
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 a3fd92f..d86ef32 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 @@
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 @@
private void sendRemoteInput(Intent intent) {
if (mBouncerChecker != null && mBouncerChecker.showBouncerIfNecessary()) {
mEditText.hideIme();
+ for (OnSendRemoteInputListener listener : mOnSendListeners) {
+ listener.onSendRequestBounced();
+ }
return;
}
@@ -370,6 +374,10 @@
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 @@
}
}
+ /** 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/statusbar/policy/SensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java
index a2334f3..6f659c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.policy;
-import android.content.Context;
import android.hardware.SensorPrivacyManager;
import androidx.annotation.NonNull;
@@ -26,14 +25,12 @@
import java.util.ArrayList;
import java.util.List;
-import javax.inject.Inject;
-
/**
* Controls sensor privacy state and notification.
*/
@SysUISingleton
public class SensorPrivacyControllerImpl implements SensorPrivacyController,
- SensorPrivacyManager.OnSensorPrivacyChangedListener {
+ SensorPrivacyManager.OnAllSensorPrivacyChangedListener {
private SensorPrivacyManager mSensorPrivacyManager;
private final List<OnSensorPrivacyChangedListener> mListeners = new ArrayList<>(1);
private Object mLock = new Object();
@@ -48,8 +45,8 @@
@Override
public void init() {
- mSensorPrivacyEnabled = mSensorPrivacyManager.isSensorPrivacyEnabled();
- mSensorPrivacyManager.addSensorPrivacyListener(this);
+ mSensorPrivacyEnabled = mSensorPrivacyManager.isAllSensorPrivacyEnabled();
+ mSensorPrivacyManager.addAllSensorPrivacyListener(this);
}
/**
@@ -83,7 +80,7 @@
/**
* Callback invoked by the SensorPrivacyService when sensor privacy state changes.
*/
- public void onSensorPrivacyChanged(boolean enabled) {
+ public void onAllSensorPrivacyChanged(boolean enabled) {
synchronized (mLock) {
mSensorPrivacyEnabled = enabled;
for (OnSensorPrivacyChangedListener listener : mListeners) {
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index fdd929c..9bdd8c0 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 @@
+ 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? "
@@ -166,6 +170,11 @@
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())
|| Intent.ACTION_MANAGED_PROFILE_ADDED.equals(intent.getAction())) {
+ if (!mDeviceProvisionedController.isCurrentUserSetup()) {
+ Log.i(TAG, "User setup not finished when " + intent.getAction()
+ + " was received. Deferring...");
+ return;
+ }
if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
reevaluateSystemTheme(true /* forceReload */);
} else if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction())) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index b823534..b955455 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.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 141b9f7..39a8bd9 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.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 @@
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 f96d344..4c9ba66 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.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 @@
@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 743dd46..36fd9be 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.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 @@
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 38da21e..ff5165d 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -22,6 +22,9 @@
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 263a75c..39ebe68 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.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 @@
SmartspaceView mSmartspaceView;
@Mock
SystemUIFactory mSystemUIFactory;
+ @Mock
+ ActivityStarter mActivityStarter;
+ @Mock
+ FalsingManager mFalsingManager;
private KeyguardClockSwitchController mController;
private View mStatusArea;
@@ -143,7 +150,9 @@
mExecutor,
mBatteryController,
mConfigurationController,
- mSystemUIFactory
+ mSystemUIFactory,
+ mActivityStarter,
+ mFalsingManager
);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
@@ -152,7 +161,6 @@
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 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/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 437d901..77582bd 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -869,21 +869,6 @@
}
@Test
- public void testStartUdfpsServiceOnBouncerNotVisible() {
- // GIVEN
- // - status bar state is on the keyguard
- // - user has authenticated since boot
- mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
- when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
-
- // WHEN the bouncer is showing
- setKeyguardBouncerVisibility(true /* isVisible */);
-
- // THEN we shouldn't listen for udfps
- assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false);
- }
-
- @Test
public void testShouldNotListenForUdfps_whenTrustEnabled() {
// GIVEN a "we should listen for udfps" state
setKeyguardBouncerVisibility(false /* isVisible */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index ffb6bba..3a657c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -68,6 +68,8 @@
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private KeyguardViewMediator mKeyguardViewMediator;
+ @Mock
+ private UdfpsController mUdfpsController;
private UdfpsKeyguardViewController mController;
@@ -98,7 +100,8 @@
mKeyguardUpdateMonitor,
mExecutor,
mDumpManager,
- mKeyguardViewMediator);
+ mKeyguardViewMediator,
+ mUdfpsController);
}
@Test
@@ -153,6 +156,17 @@
}
@Test
+ public void testShouldPauseAuthBouncerShowing() {
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ captureExpansionListener();
+
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD);
+
+ assertFalse(mController.shouldPauseAuth());
+ }
+
+ @Test
public void testShouldNotPauseAuthOnKeyguard() {
mController.onViewAttached();
captureStatusBarStateListeners();
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 f077190..bc24445 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 @@
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 fe2103c..724f8a3 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 @@
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 8eb0e9c..4a9d66c 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 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 @@
private ProximitySensor.ProximityCheck mProximityCheck;
@Mock
private AuthController mAuthController;
+ @Mock
+ private UiEventLogger mUiEventLogger;
private DozeTriggers mTriggers;
private FakeSensorManager mSensors;
@@ -107,7 +111,7 @@
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 @@
}
@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 609b847..4a487be 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 @@
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 36b6527..a9d256b 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 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 @@
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 @@
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 @@
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 96eb4b0..678f89a 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 @@
@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 @@
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 @@
}
@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 @@
@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 @@
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 @@
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 59c2b17..96d1d94 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 @@
}
@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 f397959..0a573cd6 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 @@
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 7cddc3f..1b713dd 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 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.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 @@
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 c929073..cc322620 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 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 @@
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 @@
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 @@
.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 @@
.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 3cc55f2..d353d52 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.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 @@
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 @@
.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 @@
}
@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 725e5d4..411fb02 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 @@
.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 @@
.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 51aeb15..6cf3434 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 @@
@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 8c7d762..3d658ec 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 @@
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 @@
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 @@
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 @@
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 @@
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
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/scrim/ScrimViewTest.java
index c2e58ef..a345f78 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 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/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index 4183508..94e273b 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 @@
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 f1c8ece..a63d509 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 @@
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 @@
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 0e3e0cc..a01e0b1 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.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.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 @@
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 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 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 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,15 +136,81 @@
}
@Test
+ public void disable_noOngoingCall_chipHidden() {
+ mFragments.dispatchResume();
+ processAllMessages();
+ CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+
+ when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.GONE,
+ mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+ }
+
+ @Test
+ public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
+ mFragments.dispatchResume();
+ processAllMessages();
+ CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+
+ when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
+
+ 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 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;
+
+ 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);
+
+ 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.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));
reset(mStatusBarStateController);
when(mStatusBarStateController.isDozing()).thenReturn(true);
@@ -171,56 +220,35 @@
Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
}
- @Test
- public void onOngoingCallStarted_notificationsHiddenAndOngoingCallChipDisplayed() {
- 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);
-
- 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() {
- 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(false);
- listener.onOngoingCallEnded(/* animate= */ false);
-
- assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
- Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
- }
-
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
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/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index e0fa9bab..0a3eec4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -88,6 +88,7 @@
@Mock private SuperStatusBarViewFactory mStatusBarViewFactory;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
+ @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Before
public void setUp() {
@@ -128,7 +129,8 @@
mView,
mNotificationPanelViewController,
mStatusBarViewFactory,
- mNotificationStackScrollLayoutController);
+ mNotificationStackScrollLayoutController,
+ mStatusBarKeyguardViewManager);
mController.setupExpandedStatusBar();
mController.setService(mStatusBar, mNotificationShadeWindowController);
mController.setDragDownHelper(mDragDownHelper);
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 8633eb4..559ee6f 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.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 java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -91,7 +92,7 @@
@Mock
private AlarmManager mAlarmManager;
@Mock
- private DozeParameters mDozeParamenters;
+ private DozeParameters mDozeParameters;
@Mock
LightBarController mLightBarController;
@Mock
@@ -200,8 +201,8 @@
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 @@
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 @@
@After
public void tearDown() {
finishAnimationsImmediately();
+ Arrays.stream(ScrimState.values()).forEach((scrim) -> {
+ scrim.setAodFrontScrimAlpha(0f);
+ scrim.setClipQsScrim(false);
+ });
DejankUtils.setImmediate(false);
}
@@ -259,9 +264,30 @@
@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 @@
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 @@
// 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 @@
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 @@
mScrimController.setPanelExpansion(0.5f);
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, SEMI_TRANSPARENT,
mScrimBehind, SEMI_TRANSPARENT));
}
@@ -617,6 +666,32 @@
}
@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 @@
@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 @@
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 @@
}
@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 @@
}
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 @@
}
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 ddf39d1..2d9d715 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -203,6 +203,22 @@
}
@Test
+ public void onProfileAdded_setsTheme() {
+ mBroadcastReceiver.getValue().onReceive(null,
+ new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
+ verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ }
+
+ @Test
+ public void onProfileAdded_ignoresUntilSetupComplete() {
+ reset(mDeviceProvisionedController);
+ mBroadcastReceiver.getValue().onReceive(null,
+ new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
+ verify(mThemeOverlayApplier, never())
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ }
+
+ @Test
public void onWallpaperColorsChanged_firstEventBeforeUserSetup_shouldBeAccepted() {
// By default, on setup() we make this controller return that the user finished setup
// wizard. This test on the other hand, is testing the setup flow.
@@ -212,6 +228,18 @@
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/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 57e5bb2..f1dcdff 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 @@
return;
}
mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
- maybeRequestFillLocked();
+ maybeRequestFillFromServiceLocked();
viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
} : null;
@@ -462,7 +462,7 @@
mPendingInlineSuggestionsRequest = inlineRequest;
}
- void maybeRequestFillLocked() {
+ void maybeRequestFillFromServiceLocked() {
if (mPendingFillRequest == null) {
return;
}
@@ -472,9 +472,12 @@
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 @@
/*inlineSuggestionsRequest=*/null);
mPendingFillRequest = request;
- maybeRequestFillLocked();
+ maybeRequestFillFromServiceLocked();
}
if (mActivityToken != null) {
@@ -3188,6 +3191,17 @@
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 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 @@
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 08c9c2a..c5246c7 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -18,16 +18,20 @@
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.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.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 @@
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 @@
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 @@
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 @@
}
@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 @@
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 @@
}
@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 @@
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 @@
}
- 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 @@
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 @@
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 @@
}
}
- public String getAddress() {
- if (!checkConnectPermissionForPreflight(mContext)) {
+ public String getAddress(AttributionSource attributionSource) {
+ if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getAddress")) {
return null;
}
@@ -1730,8 +1745,8 @@
return mAddress;
}
- public String getName() {
- if (!checkConnectPermissionForPreflight(mContext)) {
+ public String getName(AttributionSource attributionSource) {
+ if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getName")) {
return null;
}
@@ -2497,7 +2512,7 @@
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 @@
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 @@
}
}
+ 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 @@
* <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 667a99d..bfe51c2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -8765,7 +8765,7 @@
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 @@
// 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 @@
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 08bff81..8561042 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -935,9 +935,7 @@
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/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index e8bc812..cd3dca9 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -743,6 +743,16 @@
}
}
+ @Override
+ public boolean supportsSensorToggle(int sensor) {
+ if (sensor == MICROPHONE) {
+ return mContext.getResources().getBoolean(R.bool.config_supportsMicToggle);
+ } else if (sensor == CAMERA) {
+ return mContext.getResources().getBoolean(R.bool.config_supportsCamToggle);
+ }
+ throw new IllegalArgumentException("Unable to find value " + sensor);
+ }
+
/**
* Registers a listener to be notified when the sensor privacy state changes.
*/
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index ee3530a..edf832f 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2072,7 +2072,8 @@
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 915517a..d8af01e 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -1779,7 +1779,7 @@
// 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 10cf184..206f135 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.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 @@
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 @@
active.mNumActive++;
}
r.isForeground = true;
+ enterForeground = true;
r.mStartForegroundCount++;
r.mFgsEnterTime = SystemClock.uptimeMillis();
if (!stopProcStatsOp) {
@@ -1851,16 +1854,23 @@
} 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 @@
+ ", 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 @@
* @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 c8363dd..bf57452 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 @@
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 @@
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 @@
*/
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 @@
case KEY_FGS_START_FOREGROUND_TIMEOUT:
updateFgsStartForegroundTimeout();
break;
+ case KEY_FGS_ATOM_SAMPLE_RATE:
+ updateFgsAtomSamplePercent();
+ break;
default:
break;
}
@@ -933,6 +945,13 @@
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 @@
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 ed86710..9568fa5 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 @@
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);
@@ -5768,7 +5768,7 @@
@Override
public List<RunningTaskInfo> getTasks(int maxNum) {
- return mActivityTaskManager.getTasks(maxNum, false /* filterForVisibleRecents */);
+ return mActivityTaskManager.getTasks(maxNum);
}
@Override
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 0000000..dd24148
--- /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 fae941d..774825e 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.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 @@
}
});
}
+
+ // 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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a55605e..098ce7c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -846,8 +846,8 @@
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
- mSupportsMicPrivacyToggle = mContext.getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE);
+ mSupportsMicPrivacyToggle = context.getSystemService(SensorPrivacyManager.class)
+ .supportsSensorToggle(SensorPrivacyManager.Sensors.MICROPHONE);
// Initialize volume
// Priority 1 - Android Property
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 f744374..fbf2492 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.SystemClock;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.text.TextUtils;
import android.util.Slog;
/**
@@ -43,6 +45,15 @@
.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 @@
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 @@
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 cf545f3..8668828 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.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.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 @@
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 @@
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 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 f8a2156..aa50790 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 @@
* 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 25b7add..d82847c 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 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 769c47a..5d713f3 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 @@
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;
+ }
+ }
+
+ 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 AuthenticationFrame convert(@NonNull FaceAuthenticationFrame frame) {
- final AuthenticationFrame convertedFrame = new AuthenticationFrame();
- convertedFrame.data = convert(frame.getData());
- return convertedFrame;
+ public static FaceAuthenticationFrame toFrameworkAuthenticationFrame(
+ @NonNull AuthenticationFrame frame) {
+ return new FaceAuthenticationFrame(toFrameworkBaseFrame(frame.data));
}
@NonNull
- public static FaceEnrollFrame convert(@NonNull EnrollmentFrame frame) {
- return new FaceEnrollFrame(convert(frame.cell), frame.stage, convert(frame.data));
+ public static FaceEnrollFrame toFrameworkEnrollmentFrame(@NonNull EnrollmentFrame frame) {
+ return new FaceEnrollFrame(toFrameworkCell(frame.cell), frame.stage,
+ 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;
- }
-
- @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 @@
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 ca9be67..8726923 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 @@
}
@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 0c883b0..d04c17c 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 @@
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.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 @@
@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 @@
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 @@
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 0000000..12f3e87
--- /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 b8bac40..23be50e 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 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 0000000..c3abfc2
--- /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 82b1271d..724531e 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 @@
return;
}
((FaceAuthenticationClient) client).onAuthenticationFrame(
- AidlConversionUtils.convert(frame));
+ AidlConversionUtils.toFrameworkAuthenticationFrame(frame));
});
}
@@ -204,7 +204,8 @@
+ Utils.getClientName(client));
return;
}
- ((FaceEnrollClient) client).onEnrollmentFrame(AidlConversionUtils.convert(frame));
+ ((FaceEnrollClient) client).onEnrollmentFrame(
+ AidlConversionUtils.toFrameworkEnrollmentFrame(frame));
});
}
@@ -223,7 +224,7 @@
}
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 @@
@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 f1bfd53..bf3f7b4 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 @@
}
@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 e8668ed..f806767 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 @@
}
@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 ff06a4a..c4bdb32 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.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 @@
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 @@
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 @@
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 b1083d4..7821601 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 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 @@
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 deb251b..b780222 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.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.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.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.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 @@
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 @@
: 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 @@
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 0000000..5f998d8
--- /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 e610448..701b9a7 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 @@
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 @@
@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 0000000..66142bf
--- /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 66bd73c..e34afc0 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 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 @@
@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 @@
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 f1e37e0..c23c113 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.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 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 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 @@
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 @@
@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 49c454f..cf915ad 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.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 @@
}
final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(info, vendorCode);
+ acquisitionClient.onAcquired(AidlConversionUtils.toFrameworkAcquiredInfo(info),
+ vendorCode);
});
}
@@ -198,7 +199,7 @@
}
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 @@
}
@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 4ff6a8f..ad4f679 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 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 @@
@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 @@
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 ed681c9..ebfd534 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.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 @@
@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 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 @@
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 @@
@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/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 33224f4..ddac19b 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2698,19 +2698,21 @@
// prevent the NetworkManagementEventObserver from killing this VPN based on the
// interface going down (which we expect).
mInterface = null;
- mConfig.interfaze = null;
+ if (mConfig != null) {
+ mConfig.interfaze = null;
- // Set as unroutable to prevent traffic leaking while the interface is down.
- if (mConfig != null && mConfig.routes != null) {
- final List<RouteInfo> oldRoutes = new ArrayList<>(mConfig.routes);
+ // Set as unroutable to prevent traffic leaking while the interface is down.
+ if (mConfig.routes != null) {
+ final List<RouteInfo> oldRoutes = new ArrayList<>(mConfig.routes);
- mConfig.routes.clear();
- for (final RouteInfo route : oldRoutes) {
- mConfig.routes.add(new RouteInfo(route.getDestination(), null /*gateway*/,
- null /*iface*/, RTN_UNREACHABLE));
- }
- if (mNetworkAgent != null) {
- mNetworkAgent.sendLinkProperties(makeLinkProperties());
+ mConfig.routes.clear();
+ for (final RouteInfo route : oldRoutes) {
+ mConfig.routes.add(new RouteInfo(route.getDestination(),
+ null /*gateway*/, null /*iface*/, RTN_UNREACHABLE));
+ }
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendLinkProperties(makeLinkProperties());
+ }
}
}
}
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index 1f46061..1da7f0c 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 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 @@
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 @@
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/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 5447ee3..2562c26 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1500,6 +1500,10 @@
if (minDelay < 0) {
minDelay = 0;
+ } else if (minDelay > 0) {
+ // We can't apply a delay to an EJ. If we want to delay it, we must demote it to a
+ // regular job.
+ syncOperation.scheduleEjAsRegularJob = true;
}
// Check if duplicate syncs are pending. If found, keep one with least expected run time.
@@ -1711,6 +1715,8 @@
}
operation.enableBackoff();
+ // Never run a rescheduled requested-EJ-sync as an EJ.
+ operation.scheduleEjAsRegularJob = true;
if (operation.hasDoNotRetry() && !syncResult.syncAlreadyInProgress) {
// syncAlreadyInProgress flag is set by AbstractThreadedSyncAdapter. The sync adapter
@@ -3120,7 +3126,7 @@
scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
} else {
// mSyncJobService.callJobFinished is async, so cancel the job to ensure we don't
- // find the this job in the pending jobs list while looking for duplicates
+ // find this job in the pending jobs list while looking for duplicates
// before scheduling it at a later time.
cancelJob(op, "deferSyncH()");
scheduleSyncOperationH(op, delay);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 0071b2f..2d7145f 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.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 @@
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 @@
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 @@
+ ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
+ ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
+ ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
+ + ", mAmbientLightSensor=" + mAmbientLightSensor
+ "}";
return str;
}
@@ -318,7 +327,7 @@
private static DisplayDeviceConfig getConfigFromPmValues(Context context) {
DisplayDeviceConfig config = new DisplayDeviceConfig(context);
- config.initFromPmValues();
+ config.initFromDefaultValues();
return config;
}
@@ -342,6 +351,7 @@
loadHighBrightnessModeData(config);
loadQuirks(config);
loadBrightnessRamps(config);
+ loadAmbientLightSensorFromDdc(config);
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
@@ -357,9 +367,10 @@
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 @@
mBrightnessRampSlowDecrease = PowerManager.BRIGHTNESS_MAX;
mBrightnessRampSlowIncrease = PowerManager.BRIGHTNESS_MAX;
setSimpleMappingStrategyValues();
+ loadAmbientLightSensorFromConfigXml();
}
private void loadBrightnessDefaultFromDdcXml(DisplayConfiguration config) {
@@ -637,6 +649,33 @@
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 @@
/** 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 393a4eb..789f08f 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.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 @@
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 @@
return overriddenInfo;
}
-
-
return info;
}
@@ -1269,7 +1268,7 @@
DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
- dpc.onDisplayChangedLocked();
+ dpc.onDisplayChanged();
}
}
@@ -1305,12 +1304,23 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ @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 @@
}
}
};
+
+ /**
+ * 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 5cd0534..3340e3c 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.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.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 @@
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 @@
// 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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
+ "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 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
mAutomaticBrightnessController.dump(pw);
}
+ if (mHbmController != null) {
+ mHbmController.dump(pw);
+ }
+
pw.println();
if (mDisplayWhiteBalanceController != null) {
mDisplayWhiteBalanceController.dump(pw);
@@ -2411,4 +2488,11 @@
}
}
}
+
+ 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 2e5561d..e6486bd 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 @@
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 @@
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 @@
}
mIsAutoBrightnessEnabled = isEnabled;
mIsInAllowedAmbientRange = false; // reset when auto-brightness switches
+ recalculateTimeAllowance();
}
float getCurrentBrightnessMin() {
@@ -137,7 +141,7 @@
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 @@
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 @@
* 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 @@
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 1589419..9acb4c8 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 @@
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 @@
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 @@
return old;
}
- /**
- * 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.
- */
- public void setEnabled(boolean isEnabled) {
- mIsEnabled = isEnabled;
+ public void setPhase(@DisplayPhase int phase) {
+ mPhase = phase;
}
/**
- * @return {@code true} iff the LogicalDisplay is enabled or {@code false}
- * if disabled indicating that the display has been forced to be OFF.
+ * Returns the currently set phase for this LogicalDisplay. Phases are used when transitioning
+ * from one device state to another. {@see LogicalDisplayMapper}.
+ */
+ @DisplayPhase
+ public int getPhase() {
+ return mPhase;
+ }
+
+ /**
+ * @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 fcfa674..4c9a2d7 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 @@
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 @@
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 @@
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 @@
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 @@
if (DEBUG) {
Slog.d(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked());
}
+ finishStateTransitionLocked(false /*force*/);
updateLogicalDisplaysLocked();
break;
@@ -166,7 +197,7 @@
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 @@
}
}
+ @VisibleForTesting
public int getDisplayGroupIdFromDisplayIdLocked(int displayId) {
final LogicalDisplay display = getDisplayLocked(displayId);
if (display == null) {
@@ -225,7 +257,6 @@
ipw.increaseIndent();
ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
-
ipw.println("mCurrentLayout=" + mCurrentLayout);
final int logicalDisplayCount = mLogicalDisplays.size();
@@ -244,19 +275,78 @@
}
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 @@
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 @@
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 @@
}
}
- 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 @@
// 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 @@
}
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 @@
}
/**
- * 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 @@
if (newDisplay != oldDisplay) {
newDisplay.swapDisplaysLocked(oldDisplay);
}
- enableDisplayLocked(newDisplay, displayLayout.isEnabled());
+
+ if (!displayLayout.isEnabled()) {
+ setDisplayPhase(newDisplay, LogicalDisplay.DISPLAY_PHASE_DISABLED);
+ }
}
+
}
@@ -540,23 +692,23 @@
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 @@
}
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 @@
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 ef33667..e53aec1 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 @@
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 @@
*
* @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 9158199..7c013e0 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.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 @@
// 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 @@
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 b39bd7d..38d1aab 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.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import java.util.function.Supplier;
/**
@@ -117,12 +118,12 @@
* 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 @@
*/
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 @@
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 @@
return;
}
FontFileInfo fontFileInfo = validateFontFile(files[0]);
- addFileToMapIfSameOrNewer(fontFileInfo, true /* deleteOldFile */);
+ addFileToMapIfSameOrNewer(fontFileInfo, fontConfig, true /* deleteOldFile */);
}
success = true;
} catch (Throwable t) {
@@ -302,7 +306,7 @@
* 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 @@
"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 @@
* 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 @@
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 @@
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 2bf74c9..5802e53 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 @@
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 754fa25..77de187 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 @@
// 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 @@
pw.println("mProhibitMode: " + mProhibitMode);
pw.println("mPowerStatus: " + mPowerStatusController.getPowerStatus());
+ pw.println("mIsCecAvailable: " + mIsCecAvailable);
pw.println("mCecVersion: " + mCecVersion);
// System settings
@@ -2450,7 +2454,7 @@
if (hdmiCecEnabled != HdmiControlManager.HDMI_CEC_CONTROL_ENABLED) {
return false;
}
- return true;
+ return mIsCecAvailable;
}
@ServiceThreadOnly
@@ -2835,24 +2839,24 @@
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;
}
- invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled, false);
+ if (!listeners.isEmpty()) {
+ invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled, mIsCecAvailable);
+ }
}
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 0a800e9..c51571a 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 @@
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 @@
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 @@
@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 @@
@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 @@
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 @@
* 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 @@
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 3ac95d7..bfb9db8 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 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 @@
@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 3ba17f5..c7c681b 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 @@
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 @@
}
}
+ private void updateImeWindowStatus() {
+ synchronized (mMethodMap) {
+ updateSystemUiLocked();
+ }
+ }
+
void updateSystemUiLocked() {
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
}
@@ -3348,68 +3355,100 @@
@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.");
+ 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;
+ }
+ } 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);
+ }
+ }
+ 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;
}
- 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);
- }
- }
- 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);
- }
- });
+ return result;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
}
@NonNull
@@ -4537,6 +4576,12 @@
}
return true;
}
+ case MSG_UPDATE_IME_WINDOW_STATUS: {
+ synchronized (mMethodMap) {
+ updateSystemUiLocked();
+ }
+ return true;
+ }
// ---------------------------------------------------------
case MSG_START_INPUT: {
@@ -5202,6 +5247,12 @@
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 e25b03481..403187b 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 @@
*/
@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 885093d..0fc91ba 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 void removeImeSurface() {
reportNotSupported();
}
+
+ @Override
+ public void updateImeWindowStatus() {
+ }
});
}
@@ -1623,6 +1627,33 @@
@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 @@
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 6cded50..d110839 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 @@
}
@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 @@
}
@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 6a5c2d89..a73c8e0 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 @@
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/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 4f527f2..cd352b5 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.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.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.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 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 6b7e729..bf114d8 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 @@
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 @@
"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 @@
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 @@
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 @@
}
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 @@
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 d2cbcf0..8d6c145 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 @@
Integer filteringAppId = setting == null ? null : setting.appId;
mAppsFilter.dumpQueries(
pw, filteringAppId, dumpState, mUserManager.getUserIds(),
- this::getPackagesForUid);
+ this::getPackagesForUidInternalBody);
break;
}
@@ -21627,7 +21627,8 @@
null /*disabledComponents*/,
PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.UNINSTALL_REASON_UNKNOWN,
- null /*harmfulAppWarning*/);
+ null /*harmfulAppWarning*/,
+ null /*splashScreenTheme*/);
}
mSettings.writeKernelMappingLPr(ps);
}
@@ -27701,6 +27702,23 @@
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 19b56b7..731d41c 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 @@
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 @@
state.instantApp = instantApp;
state.virtualPreload = virtualPreload;
state.harmfulAppWarning = harmfulAppWarning;
+ state.splashScreenTheme = splashScreenTheme;
onChanged();
}
@@ -522,7 +524,8 @@
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 @@
}
/**
+ * @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 e409019..b6d4a5b 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 @@
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 @@
null /*disabledComponents*/,
PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.UNINSTALL_REASON_UNKNOWN,
- null /*harmfulAppWarning*/);
+ null, /*harmfulAppWarning*/
+ null /*splashscreenTheme*/
+ );
}
}
}
@@ -1578,7 +1581,8 @@
null /*disabledComponents*/,
PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.UNINSTALL_REASON_UNKNOWN,
- null /*harmfulAppWarning*/);
+ null /*harmfulAppWarning*/,
+ null /* splashScreenTheme*/);
}
return;
}
@@ -1666,6 +1670,8 @@
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 @@
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 @@
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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2a0257d..cf18156 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1516,13 +1516,13 @@
}
@Override
- public boolean sharesMediaWithParent(@UserIdInt int userId) {
+ public boolean isMediaSharedWithParent(@UserIdInt int userId) {
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
- "sharesMediaWithParent");
+ "isMediaSharedWithParent");
synchronized (mUsersLock) {
UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
return userTypeDetails != null ? userTypeDetails.isProfile()
- && userTypeDetails.sharesMediaWithParent() : false;
+ && userTypeDetails.isMediaSharedWithParent() : false;
}
}
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 6824f7d..92ec31b 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -154,7 +154,7 @@
*
* <p> Default value is false
*/
- private final boolean mSharesMediaWithParent;
+ private final boolean mIsMediaSharedWithParent;
private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
@UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
@@ -166,7 +166,7 @@
@Nullable Bundle defaultSystemSettings,
@Nullable Bundle defaultSecureSettings,
@Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
- boolean sharesMediaWithParent) {
+ boolean isMediaSharedWithParent) {
this.mName = name;
this.mEnabled = enabled;
this.mMaxAllowed = maxAllowed;
@@ -185,7 +185,7 @@
this.mBadgeLabels = badgeLabels;
this.mBadgeColors = badgeColors;
this.mDarkThemeBadgeColors = darkThemeBadgeColors;
- this.mSharesMediaWithParent = sharesMediaWithParent;
+ this.mIsMediaSharedWithParent = isMediaSharedWithParent;
}
/**
@@ -303,8 +303,8 @@
/**
* Returns true if the user has shared media with parent user or false otherwise.
*/
- public boolean sharesMediaWithParent() {
- return mSharesMediaWithParent;
+ public boolean isMediaSharedWithParent() {
+ return mIsMediaSharedWithParent;
}
/** Returns a {@link Bundle} representing the default user restrictions. */
@@ -398,7 +398,7 @@
private @DrawableRes int mIconBadge = Resources.ID_NULL;
private @DrawableRes int mBadgePlain = Resources.ID_NULL;
private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
- private boolean mSharesMediaWithParent = false;
+ private boolean mIsMediaSharedWithParent = false;
public Builder setName(String name) {
mName = name;
@@ -491,10 +491,10 @@
/**
* Sets shared media property for the user.
- * @param sharesMediaWithParent the value to be set, true or false
+ * @param isMediaSharedWithParent the value to be set, true or false
*/
- public Builder setSharesMediaWithParent(boolean sharesMediaWithParent) {
- mSharesMediaWithParent = sharesMediaWithParent;
+ public Builder setIsMediaSharedWithParent(boolean isMediaSharedWithParent) {
+ mIsMediaSharedWithParent = isMediaSharedWithParent;
return this;
}
@@ -527,7 +527,7 @@
mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings,
- mDefaultCrossProfileIntentFilters, mSharesMediaWithParent);
+ mDefaultCrossProfileIntentFilters, mIsMediaSharedWithParent);
}
private boolean hasBadge() {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index e8421a5..30cb40c 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -121,7 +121,7 @@
.setMaxAllowedPerParent(1)
.setLabel(0)
.setDefaultRestrictions(null)
- .setSharesMediaWithParent(true);
+ .setIsMediaSharedWithParent(true);
}
/**
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 2d1178a..0147790 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.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.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.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 @@
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 @@
}
}
}
+
+ /**
+ * 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 a8a6a72..a5ba82f 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.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 @@
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 @@
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 @@
}
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 @@
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 @@
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 39ed488..adf8f0d 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.DomainVerificationStateMap;
import java.util.Arrays;
+import java.util.List;
import java.util.function.Function;
@SuppressWarnings("PointlessBooleanExpression")
@@ -100,6 +101,70 @@
}
}
+ /**
+ * @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 @@
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 0f99e19..5aed367 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 @@
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 @@
* 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 @@
@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 a3e1a9c..3a4b849 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 @@
});
}
+ @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 @@
});
}
+ /**
+ * @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 @@
}
}
+ @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 @@
// 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 @@
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 @@
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 @@
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 @@
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 ea71b28..da2d162 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 @@
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 @@
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 @@
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 @@
*/
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 81a51e2..24337f3 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 @@
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 @@
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 @@
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 @@
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/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 5f5e6a3..8d79a81 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -80,11 +80,11 @@
public int getConnectionUserIdForVolume(VolumeInfo vol) {
final Context volumeUserContext = mContext.createContextAsUser(
UserHandle.of(vol.mountUserId), 0);
- boolean sharesMediaWithParent = volumeUserContext.getSystemService(
- UserManager.class).sharesMediaWithParent();
+ boolean isMediaSharedWithParent = volumeUserContext.getSystemService(
+ UserManager.class).isMediaSharedWithParent();
UserInfo userInfo = mUserManager.getUserInfo(vol.mountUserId);
- if (userInfo != null && sharesMediaWithParent) {
+ if (userInfo != null && isMediaSharedWithParent) {
// Clones use the same connection as their parent
return userInfo.profileGroupId;
} else {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 9acbdcc..c888e54 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 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 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 e6d49e4..a97c080 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.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.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 @@
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 @@
}
}
+ 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 @@
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 @@
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 @@
/* 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 @@
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 @@
}
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 4c392f0..3e8bc5d 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 @@
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 @@
}
// 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 @@
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 @@
}
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 @@
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 @@
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 @@
// 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 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 @@
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 1158a9c..9be973b 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 @@
|| 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 @@
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();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7607285..0570f6c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2104,8 +2104,16 @@
}
}
- List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
- return getTasks(maxNum, false /* filterForVisibleRecents */);
+ /**
+ * Gets info of running tasks up to the given number.
+ *
+ * @param maxNum the maximum number of task info returned by this method. If the total number of
+ * running tasks is larger than it then there is no guarantee which task will be
+ * left out.
+ * @return a list of {@link ActivityManager.RunningTaskInfo} with up to {@code maxNum} items
+ */
+ public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
+ return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */);
}
/**
@@ -2114,10 +2122,14 @@
*/
@Override
public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
- boolean filterOnlyVisibleRecents) {
+ boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
+
+ int flags = filterOnlyVisibleRecents ? RunningTasks.FLAG_FILTER_ONLY_VISIBLE_RECENTS : 0;
+ flags |= (keepIntentExtra ? RunningTasks.FLAG_KEEP_INTENT_EXTRA : 0);
final boolean crossUser = isCrossUserAllowed(callingPid, callingUid);
+ flags |= (crossUser ? RunningTasks.FLAG_CROSS_USERS : 0);
final int[] profileIds = getUserManager().getProfileIds(
UserHandle.getUserId(callingUid), true);
ArraySet<Integer> callingProfileIds = new ArraySet<>();
@@ -2130,8 +2142,9 @@
if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum);
final boolean allowed = isGetTasksAllowed("getTasks", callingPid, callingUid);
- mRootWindowContainer.getRunningTasks(maxNum, list, filterOnlyVisibleRecents, callingUid,
- allowed, crossUser, callingProfileIds);
+ flags |= (allowed ? RunningTasks.FLAG_ALLOWED : 0);
+ mRootWindowContainer.getRunningTasks(
+ maxNum, list, flags, callingUid, callingProfileIds);
}
return list;
@@ -5607,10 +5620,6 @@
mTaskSupervisor.endDeferResume();
}
- if (wpc.isInstrumenting()) {
- finishInstrumentationCallback.run();
- }
-
if (!restarting && hasVisibleActivities) {
deferWindowLayout();
try {
@@ -5627,6 +5636,9 @@
}
}
}
+ if (wpc.isInstrumenting()) {
+ finishInstrumentationCallback.run();
+ }
}
@Override
@@ -6126,6 +6138,7 @@
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;
@@ -6134,15 +6147,18 @@
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 9d225e1..df1fec9 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 @@
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 @@
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 394ae7e..43326df 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 @@
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 d920267..ff10168 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.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 @@
@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,31 +92,22 @@
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();
+ final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurDisabledSetting
+ && !mInPowerSaveMode;
+ if (mBlurEnabled == newEnabled) {
+ return;
+ }
+ mBlurEnabled = newEnabled;
+ notifyBlurEnabledChangedLocked(newEnabled);
}
}
- private void updateBlurEnabledLocked() {
- final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurForceDisabled
- && !mInBatterySaverMode;
- if (mBlurEnabled == newEnabled) {
- return;
- }
- mBlurEnabled = newEnabled;
- notifyBlurEnabledChangedLocked(newEnabled);
- }
-
private void notifyBlurEnabledChangedLocked(boolean enabled) {
int i = mBlurEnabledListeners.beginBroadcast();
while (i > 0) {
@@ -117,4 +121,9 @@
}
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 c44f4e3..0c4e1a2 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.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 @@
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 @@
}
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 @@
// 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 @@
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 @@
forAllWindows(w -> {
w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
}, true /* traverseTopToBottom */);
+ mPinnedTaskController.startSeamlessRotationIfNeeded(transaction);
}
mWmService.mDisplayManagerInternal.performTraversal(transaction);
@@ -2333,7 +2336,7 @@
}
PinnedTaskController getPinnedTaskController() {
- return mPinnedTaskControllerLocked;
+ return mPinnedTaskController;
}
/**
@@ -2392,12 +2395,11 @@
@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 @@
}
}
- /**
- * 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 @@
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 @@
}
pw.println();
- mPinnedTaskControllerLocked.dump(prefix, pw);
+ mPinnedTaskController.dump(prefix, pw);
pw.println();
mDisplayFrames.dump(prefix, pw);
@@ -3821,7 +3810,7 @@
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 @@
// 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 e1fc75e..0e73d79 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 @@
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 @@
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 724747d..02a7db1 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_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 @@
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 @@
}
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/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 510c62d..da47328 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -257,8 +257,10 @@
void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) {
final long token = proto.start(fieldId);
super.dumpDebug(proto, INSETS_SOURCE_PROVIDER, logLevel);
- if (mImeRequester != null) {
- mImeRequester.getWindow().dumpDebug(proto, IME_TARGET_FROM_IME, logLevel);
+ final WindowState imeRequesterWindow =
+ mImeRequester != null ? mImeRequester.getWindow() : null;
+ if (imeRequesterWindow != null) {
+ imeRequesterWindow.dumpDebug(proto, IME_TARGET_FROM_IME, logLevel);
}
proto.write(IS_IME_LAYOUT_DRAWN, mIsImeLayoutDrawn);
proto.end(token);
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 8ff9fba..df4f2a9 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.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 @@
// 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 15e078b..dea83f0 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 @@
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 @@
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 @@
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 @@
@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 @@
*/
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 @@
&& 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 Task pinnedTask = mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask();
- return pinnedTask != null && pinnedTask.hasChild();
+ 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));
}
- /** Sets whether a visible task is changing from or to pinned mode. */
- void setPipWindowingModeChanging(boolean isPipWindowingModeChanging) {
- mIsPipWindowingModeChanging = isPipWindowingModeChanging;
+ /** 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;
+ }
+
+ 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();
+ }
+
+ /** 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 @@
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 @@
}
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 19c2e73..dec6460 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 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 @@
}
@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 @@
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 @@
}
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 @@
}
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 b9fc8b1..c81f31e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1686,6 +1686,11 @@
return false;
}
+ if (!taskDisplayArea.canHostHomeTask()) {
+ // Can't launch home on a TaskDisplayArea that does not support root home task
+ return false;
+ }
+
if (taskDisplayArea.getDisplayId() != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
// Can't launch home on secondary display if device does not support multi-display.
return false;
@@ -2132,9 +2137,12 @@
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.
@@ -2143,7 +2151,7 @@
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
@@ -3474,10 +3482,9 @@
@VisibleForTesting
void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
- boolean filterOnlyVisibleRecents, int callingUid, boolean allowed, boolean crossUser,
- ArraySet<Integer> profileIds) {
- mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, filterOnlyVisibleRecents, this,
- callingUid, allowed, crossUser, profileIds);
+ int flags, int callingUid, ArraySet<Integer> profileIds) {
+ mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, this, callingUid,
+ profileIds);
}
void startPowerModeLaunchIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index e8c1c8d..7ba772c 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -36,6 +36,11 @@
*/
class RunningTasks {
+ static final int FLAG_FILTER_ONLY_VISIBLE_RECENTS = 1;
+ static final int FLAG_ALLOWED = 1 << 1;
+ static final int FLAG_CROSS_USERS = 1 << 2;
+ static final int FLAG_KEEP_INTENT_EXTRA = 1 << 3;
+
// Comparator to sort by last active time (descending)
private static final Comparator<Task> LAST_ACTIVE_TIME_COMPARATOR =
(o1, o2) -> Long.signum(o2.lastActiveTime - o1.lastActiveTime);
@@ -50,10 +55,10 @@
private boolean mFilterOnlyVisibleRecents;
private Task mTopDisplayFocusRootTask;
private RecentTasks mRecentTasks;
+ private boolean mKeepIntentExtra;
- void getTasks(int maxNum, List<RunningTaskInfo> list, boolean filterOnlyVisibleRecents,
- RootWindowContainer root, int callingUid, boolean allowed, boolean crossUser,
- ArraySet<Integer> profileIds) {
+ void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
+ RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
// Return early if there are no tasks to fetch
if (maxNum <= 0) {
return;
@@ -63,12 +68,14 @@
mTmpSortedSet.clear();
mCallingUid = callingUid;
mUserId = UserHandle.getUserId(callingUid);
- mCrossUser = crossUser;
+ mCrossUser = (flags & FLAG_CROSS_USERS) == FLAG_CROSS_USERS;
mProfileIds = profileIds;
- mAllowed = allowed;
- mFilterOnlyVisibleRecents = filterOnlyVisibleRecents;
+ mAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
+ mFilterOnlyVisibleRecents =
+ (flags & FLAG_FILTER_ONLY_VISIBLE_RECENTS) == FLAG_FILTER_ONLY_VISIBLE_RECENTS;
mTopDisplayFocusRootTask = root.getTopDisplayFocusedRootTask();
mRecentTasks = root.mService.getRecentTasks();
+ mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
PooledLambda.__(Task.class));
@@ -126,7 +133,8 @@
/** Constructs a {@link RunningTaskInfo} from a given {@param task}. */
private RunningTaskInfo createRunningTaskInfo(Task task) {
- final RunningTaskInfo rti = task.getTaskInfo();
+ final RunningTaskInfo rti = new RunningTaskInfo();
+ task.fillTaskInfo(rti, !mKeepIntentExtra);
// Fill in some deprecated values
rti.id = rti.taskId;
return rti;
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index 39ac16a..48a7bde 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 @@
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.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 @@
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 @@
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 858d9f3..d4707d6f 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.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.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 @@
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 @@
}
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 @@
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 @@
@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 @@
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 @@
// 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 @@
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 @@
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;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 3799067..4d85e7b 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -172,18 +172,33 @@
*/
final boolean mCreatedByOrganizer;
+ /**
+ * True if this TaskDisplayArea can have a home task
+ * {@link WindowConfiguration#ACTIVITY_TYPE_HOME}
+ */
+ private final boolean mCanHostHomeTask;
+
TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
- int displayAreaFeature) {
- this(displayContent, service, name, displayAreaFeature, false /* createdByOrganizer */);
+ int displayAreaFeature) {
+ this(displayContent, service, name, displayAreaFeature, false /* createdByOrganizer */,
+ true /* canHostHomeTask */);
}
TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
- int displayAreaFeature, boolean createdByOrganizer) {
+ int displayAreaFeature, boolean createdByOrganizer) {
+ this(displayContent, service, name, displayAreaFeature, createdByOrganizer,
+ true /* canHostHomeTask */);
+ }
+
+ TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
+ int displayAreaFeature, boolean createdByOrganizer,
+ boolean canHostHomeTask) {
super(service, Type.ANY, name, displayAreaFeature);
mDisplayContent = displayContent;
mRootWindowContainer = service.mRoot;
mAtmService = service.mAtmService;
mCreatedByOrganizer = createdByOrganizer;
+ mCanHostHomeTask = canHostHomeTask;
}
/**
@@ -1667,7 +1682,9 @@
@Nullable
Task getOrCreateRootHomeTask(boolean onTop) {
Task homeTask = getRootHomeTask();
- if (homeTask == null && mDisplayContent.supportsSystemDecorations()) {
+ // Take into account if this TaskDisplayArea can have a home task before trying to
+ // create the root task
+ if (homeTask == null && canHostHomeTask()) {
homeTask = createRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop);
}
return homeTask;
@@ -1882,6 +1899,13 @@
}
/**
+ * Exposes the home task capability of the TaskDisplayArea
+ */
+ boolean canHostHomeTask() {
+ return mDisplayContent.supportsSystemDecorations() && mCanHostHomeTask;
+ }
+
+ /**
* Callback for when the order of the root tasks in the display changes.
*/
interface OnRootTaskOrderChangedListener {
diff --git a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
index 1779d2a..7e992ac 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 @@
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 @@
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/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 79a6bd7..cc4abab 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -37,6 +37,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
@@ -53,8 +54,10 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -69,7 +72,6 @@
import android.view.IWindowSession;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
@@ -124,9 +126,7 @@
private static final Point sTmpSurfaceSize = new Point();
private final Window mWindow;
- private final Surface mSurface;
- private SurfaceControl mSurfaceControl;
- private SurfaceControl mChildSurfaceControl;
+ private final SurfaceControl mSurfaceControl;
private final IWindowSession mSession;
private final WindowManagerService mService;
private final int mDisplayId;
@@ -200,7 +200,7 @@
// Setting as trusted overlay to let touches pass through. This is safe because this
// window is controlled by the system.
layoutParams.privateFlags = (windowPrivateFlags & PRIVATE_FLAG_INHERITS)
- | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ | PRIVATE_FLAG_TRUSTED_OVERLAY | PRIVATE_FLAG_USE_BLAST;
layoutParams.token = activity.token;
layoutParams.width = LayoutParams.MATCH_PARENT;
layoutParams.height = LayoutParams.MATCH_PARENT;
@@ -262,7 +262,6 @@
InsetsState insetsState) {
mService = service;
mDisplayId = displayId;
- mSurface = service.mSurfaceFactory.get();
mHandler = new Handler(mService.mH.getLooper());
mSession = WindowManagerGlobal.getWindowSession();
mWindow = window;
@@ -314,8 +313,6 @@
}
private void drawSnapshot() {
- mSurface.copyFrom(mSurfaceControl);
-
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Drawing snapshot surface sizeMismatch=%b",
mSizeMismatch);
if (mSizeMismatch) {
@@ -337,15 +334,14 @@
}
private void drawSizeMatchSnapshot() {
- mSurface.attachAndQueueBufferWithColorSpace(mSnapshot.getHardwareBuffer(),
- mSnapshot.getColorSpace());
- mSurface.release();
+ GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
+ mSnapshot.getHardwareBuffer());
+ mTransaction.setBuffer(mSurfaceControl, graphicBuffer)
+ .setColorSpace(mSurfaceControl, mSnapshot.getColorSpace())
+ .apply();
}
private void drawSizeMismatchSnapshot() {
- if (!mSurface.isValid()) {
- throw new IllegalStateException("mSurface does not hold a valid surface.");
- }
final HardwareBuffer buffer = mSnapshot.getHardwareBuffer();
final SurfaceSession session = new SurfaceSession();
// We consider nearly matched dimensions as there can be rounding errors and the user won't
@@ -356,26 +352,24 @@
// Keep a reference to it such that it doesn't get destroyed when finalized.
final String name = mTitle + " - task-snapshot-surface";
- mChildSurfaceControl = mService.mSurfaceControlFactory.apply(session)
+ SurfaceControl childSurfaceControl = mService.mSurfaceControlFactory.apply(session)
.setName(name)
- .setBufferSize(buffer.getWidth(), buffer.getHeight())
+ .setBLASTLayer()
.setFormat(buffer.getFormat())
.setParent(mSurfaceControl)
.setCallsite("TaskSnapshotSurface.drawSizeMismatchSnapshot")
.build();
- Surface surface = mService.mSurfaceFactory.get();
- surface.copyFrom(mChildSurfaceControl);
final Rect frame;
// We can just show the surface here as it will still be hidden as the parent is
// still hidden.
- mTransaction.show(mChildSurfaceControl);
+ mTransaction.show(childSurfaceControl);
if (aspectRatioMismatch) {
// Clip off ugly navigation bar.
final Rect crop = calculateSnapshotCrop();
frame = calculateSnapshotFrame(crop);
- mTransaction.setWindowCrop(mChildSurfaceControl, crop);
- mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top);
+ mTransaction.setWindowCrop(childSurfaceControl, crop);
+ mTransaction.setPosition(childSurfaceControl, frame.left, frame.top);
mTmpSnapshotSize.set(crop);
mTmpDstFrame.set(frame);
} else {
@@ -387,23 +381,29 @@
// Scale the mismatch dimensions to fill the task bounds
mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
- mTransaction.setMatrix(mChildSurfaceControl, mSnapshotMatrix, mTmpFloat9);
+ mTransaction.setMatrix(childSurfaceControl, mSnapshotMatrix, mTmpFloat9);
+ GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
+ mSnapshot.getHardwareBuffer());
+ mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
+ mTransaction.setBuffer(childSurfaceControl, graphicBuffer);
// This is the way to tell the input system to exclude this surface from occlusion
// detection since we don't have a window for it. We do this because this window is
// generated by the system as well as its content (the snapshot of the app).
- InputMonitor.setTrustedOverlayInputInfo(mChildSurfaceControl, mTransaction, mDisplayId,
+ InputMonitor.setTrustedOverlayInputInfo(childSurfaceControl, mTransaction, mDisplayId,
name);
- mTransaction.apply();
- surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace());
- surface.release();
if (aspectRatioMismatch) {
- final Canvas c = mSurface.lockCanvas(null);
+ GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(),
+ PixelFormat.RGBA_8888,
+ GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
+ | GraphicBuffer.USAGE_SW_WRITE_RARELY);
+ final Canvas c = background.lockCanvas();
drawBackgroundAndBars(c, frame);
- mSurface.unlockCanvasAndPost(c);
- mSurface.release();
+ background.unlockCanvasAndPost(c);
+ mTransaction.setBuffer(mSurfaceControl, background);
}
+ mTransaction.apply();
}
/**
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index e4ac1f6..42e2d2f 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_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;
@@ -106,11 +107,9 @@
private final BLASTSyncEngine mSyncEngine;
private IRemoteTransition mRemoteTransition = null;
- /**
- * This is a leash to put animating surfaces into flatly without clipping/ordering issues. It
- * is a child of all the targets' shared ancestor.
- */
- private SurfaceControl mRootLeash = null;
+ /** Only use for clean-up after binder death! */
+ private SurfaceControl.Transaction mStartTransaction = null;
+ private SurfaceControl.Transaction mFinishTransaction = null;
/**
* Contains change infos for both participants and all ancestors. We have to track ancestors
@@ -229,16 +228,16 @@
setReady(true);
}
- /** The transition has finished animating and is ready to finalize WM state */
- void finishTransition() {
- if (mState < STATE_PLAYING) {
- throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId);
- }
+ /**
+ * Build a transaction that "resets" all the re-parenting and layer changes. This is
+ * intended to be applied at the end of the transition but before the finish callback. This
+ * needs to be passed/applied in shell because until finish is called, shell owns the surfaces.
+ * Additionally, this gives shell the ability to better deal with merged transitions.
+ */
+ private void buildFinishTransaction(SurfaceControl.Transaction t, SurfaceControl rootLeash) {
final Point tmpPos = new Point();
// usually only size 1
final ArraySet<DisplayContent> displays = new ArraySet<>();
- // Immediately apply all surface reparents, don't wait for pending/sync/etc.
- SurfaceControl.Transaction t = mController.mAtm.mWindowManager.mTransactionFactory.get();
for (int i = mTargets.size() - 1; i >= 0; --i) {
final WindowContainer target = mTargets.valueAt(i);
if (target.getParent() != null) {
@@ -247,9 +246,6 @@
// Ensure surfaceControls are re-parented back into the hierarchy.
t.reparent(targetLeash, origParent);
t.setLayer(targetLeash, target.getLastLayer());
- // TODO(shell-transitions): Once all remotables have been moved, see if there is
- // a more appropriate place to do the following. This may
- // involve passing an SF transaction from shell on finish.
target.getRelativePosition(tmpPos);
t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
t.setCornerRadius(targetLeash, 0);
@@ -257,25 +253,26 @@
displays.add(target.getDisplayContent());
}
}
- // Need to update layers on ALL displays (for now) since they were all paused while
- // the animation played.
+ // Need to update layers on involved displays since they were all paused while
+ // the animation played. This puts the layers back into the correct order.
for (int i = displays.size() - 1; i >= 0; --i) {
if (displays.valueAt(i) == null) continue;
displays.valueAt(i).assignChildLayers(t);
}
- // Also pro-actively hide going-invisible activity surfaces in same transaction to
- // prevent flickers due to reparenting and animation z-order mismatch.
- for (int i = mParticipants.size() - 1; i >= 0; --i) {
- final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
- if (ar == null || ar.mVisibleRequested || !ar.isVisible()) continue;
- t.hide(ar.getSurfaceControl());
+ if (rootLeash.isValid()) {
+ t.reparent(rootLeash, null);
}
- if (mRootLeash.isValid()) {
- t.remove(mRootLeash);
+ }
+
+ /**
+ * The transition has finished animating and is ready to finalize WM state. This should not
+ * be called directly; use {@link TransitionController#finishTransition} instead.
+ */
+ void finishTransition() {
+ mStartTransaction = mFinishTransaction = null;
+ if (mState < STATE_PLAYING) {
+ throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId);
}
- mRootLeash = null;
- t.apply();
- t.close();
// Commit all going-invisible containers
for (int i = 0; i < mParticipants.size(); ++i) {
@@ -343,29 +340,60 @@
// Resolve the animating targets from the participants
mTargets = calculateTargets(mParticipants, mChanges);
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges);
- mRootLeash = info.getRootLeash();
handleNonAppWindowsInTransition(displayId, mType, mFlags);
+ // Manually show any activities that are visibleRequested. This is needed to properly
+ // support simultaneous animation queueing/merging. Specifically, if transition A makes
+ // an activity invisible, it's finishTransaction (which is applied *after* the animation)
+ // will hide the activity surface. If transition B then makes the activity visible again,
+ // the normal surfaceplacement logic won't add a show to this start transaction because
+ // the activity visibility hasn't been committed yet. To deal with this, we have to manually
+ // show here in the same way that we manually hide in finishTransaction.
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
+ if (ar == null || !ar.mVisibleRequested) continue;
+ transaction.show(ar.getSurfaceControl());
+ }
+
+ mStartTransaction = transaction;
+ mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
+ buildFinishTransaction(mFinishTransaction, info.getRootLeash());
if (mController.getTransitionPlayer() != null) {
try {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Calling onTransitionReady: %s", info);
- mController.getTransitionPlayer().onTransitionReady(this, info, transaction);
+ mController.getTransitionPlayer().onTransitionReady(
+ this, info, transaction, mFinishTransaction);
} catch (RemoteException e) {
// If there's an exception when trying to send the mergedTransaction to the
// client, we should finish and apply it here so the transactions aren't lost.
- transaction.apply();
- finishTransition();
+ cleanUpOnFailure();
}
} else {
// No player registered, so just finish/apply immediately
- transaction.apply();
- finishTransition();
+ cleanUpOnFailure();
}
mSyncId = -1;
}
+ /**
+ * If the remote failed for any reason, use this to do any appropriate clean-up. Do not call
+ * this directly, it's designed to by called by {@link TransitionController} only.
+ */
+ void cleanUpOnFailure() {
+ // No need to clean-up if this isn't playing yet.
+ if (mState < STATE_PLAYING) return;
+
+ if (mStartTransaction != null) {
+ mStartTransaction.apply();
+ }
+ if (mFinishTransaction != null) {
+ mFinishTransaction.apply();
+ }
+ finishTransition();
+ }
+
private void handleNonAppWindowsInTransition(int displayId,
@WindowManager.TransitionType int transit, int flags) {
final DisplayContent dc =
@@ -849,8 +877,18 @@
// 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/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 6338f39..cc63c49 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -42,17 +42,24 @@
private static final String TAG = "TransitionController";
private ITransitionPlayer mTransitionPlayer;
- private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> mTransitionPlayer = null;
final ActivityTaskManagerService mAtm;
- /** Currently playing transitions. When finished, records are removed from this list. */
+ /**
+ * Currently playing transitions (in the order they were started). When finished, records are
+ * removed from this list.
+ */
private final ArrayList<Transition> mPlayingTransitions = new ArrayList<>();
- /**
- * The transition currently being constructed (collecting participants).
- * TODO(shell-transitions): When simultaneous transitions are supported, merge this with
- * mPlayingTransitions.
- */
+ private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> {
+ // clean-up/finish any playing transitions.
+ for (int i = 0; i < mPlayingTransitions.size(); ++i) {
+ mPlayingTransitions.get(i).cleanUpOnFailure();
+ }
+ mPlayingTransitions.clear();
+ mTransitionPlayer = null;
+ };
+
+ /** The transition currently being constructed (collecting participants). */
private Transition mCollectingTransition = null;
TransitionController(ActivityTaskManagerService atm) {
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index 7f28ffc..66ab094 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 @@
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 @@
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 @@
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 @@
}
void drawIfNeeded() {
- if (mDrawNeeded) {
- final int dw = mLastDW;
- final int dh = mLastDH;
+ if (!mDrawNeeded) {
+ return;
+ }
- 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) {
+ 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 b9f67a5..7f21eeb 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_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.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 @@
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 @@
// 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 @@
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 @@
@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 015a0fb..a5ebf9a 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 @@
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 @@
/** @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 @@
case KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS:
updateSystemGestureExclusionLogDebounceMillis();
break;
- case KEY_RAW_SENSOR_LOGGING_ENABLED:
- updateRawSensorDataLoggingEnabled();
- break;
default:
break;
}
@@ -167,12 +158,6 @@
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 @@
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 12c9b97..9e8b6a3 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 @@
private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000;
- @VisibleForTesting
- WindowManagerConstants mConstants;
+ final WindowManagerConstants mConstants;
final WindowTracing mWindowTracing;
@@ -3659,7 +3658,7 @@
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 @@
// 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 @@
mBlurController.unregisterCrossWindowBlurEnabledListener(listener);
}
- @Override
- public void setForceCrossWindowBlurDisabled(boolean disable) {
- mBlurController.setForceCrossWindowBlurDisabled(disable);
- }
-
// -------------------------------------------------------------
// Internals
// -------------------------------------------------------------
@@ -5895,8 +5889,8 @@
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 @@
}
});
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 1b578d1..4dc6007 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.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 @@
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 @@
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 9382b8e..12a6a54 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -491,7 +491,7 @@
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 be6847ab..3e099fb 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 @@
private int mCurrentRotation = -1;
private final Context mContext;
- private final WindowManagerConstants mConstants;
private final Object mLock = new Object();
@@ -94,11 +93,9 @@
*
* @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 @@
* 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 @@
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 83f74cd..f71d08a 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 @@
// 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 @@
}
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 3ee2b8d..cca62b9 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 @@
jmethodID getVirtualKeyQuietTimeMillis;
jmethodID getExcludedDeviceNames;
jmethodID getInputPortAssociations;
+ jmethodID getInputUniqueIdAssociations;
jmethodID getKeyRepeatTimeout;
jmethodID getKeyRepeatDelay;
jmethodID getHoverTapTimeout;
@@ -579,6 +580,21 @@
}
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 @@
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 @@
(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 @@
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/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index b7fa796..698e3f7 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -370,6 +370,7 @@
jintArray supportedEffects = nullptr;
jintArray supportedBraking = nullptr;
jintArray supportedPrimitives = nullptr;
+ jintArray primitiveDurations = nullptr;
jfloatArray maxAmplitudes = nullptr;
if (info.supportedEffects.isOk()) {
@@ -390,6 +391,15 @@
env->SetIntArrayRegion(supportedPrimitives, 0, primitives.size(),
reinterpret_cast<jint*>(primitives.data()));
}
+ if (info.primitiveDurations.isOk()) {
+ std::vector<int32_t> durations;
+ for (auto duration : info.primitiveDurations.value()) {
+ durations.push_back(duration.count());
+ }
+ primitiveDurations = env->NewIntArray(durations.size());
+ env->SetIntArrayRegion(primitiveDurations, 0, durations.size(),
+ reinterpret_cast<jint*>(durations.data()));
+ }
if (info.maxAmplitudes.isOk()) {
std::vector<float> amplitudes = info.maxAmplitudes.value();
maxAmplitudes = env->NewFloatArray(amplitudes.size());
@@ -403,7 +413,7 @@
return env->NewObject(sVibratorInfoClass, sVibratorInfoCtor, wrapper->getVibratorId(),
capabilities, supportedEffects, supportedBraking, supportedPrimitives,
- qFactor, frequencyMapping);
+ primitiveDurations, qFactor, frequencyMapping);
}
static const JNINativeMethod method_table[] = {
@@ -450,9 +460,10 @@
sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V");
jclass vibratorInfoClass = FindClassOrDie(env, "android/os/VibratorInfo");
- sVibratorInfoClass = static_cast<jclass>(env->NewGlobalRef(vibratorInfoClass));
- sVibratorInfoCtor = GetMethodIDOrDie(env, sVibratorInfoClass, "<init>",
- "(IJ[I[I[IFLandroid/os/VibratorInfo$FrequencyMapping;)V");
+ sVibratorInfoClass = (jclass)env->NewGlobalRef(vibratorInfoClass);
+ sVibratorInfoCtor =
+ GetMethodIDOrDie(env, sVibratorInfoClass, "<init>",
+ "(IJ[I[I[I[IFLandroid/os/VibratorInfo$FrequencyMapping;)V");
return jniRegisterNativeMethods(env,
"com/android/server/vibrator/VibratorController$NativeWrapper",
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 e4b9612..01834dd 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 eb3f1b7..a848f82 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 @@
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 @@
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 @@
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 e5323cd..8274e38 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -629,6 +629,7 @@
private static final String CREDENTIAL_MANAGEMENT_APP_INVALID_ALIAS_MSG =
"The alias provided must be contained in the aliases specified in the credential "
+ "management app's authentication policy";
+ private static final String NOT_SYSTEM_CALLER_MSG = "Only the system can %s";
final Context mContext;
final Injector mInjector;
@@ -3631,7 +3632,8 @@
@Override
public boolean isSeparateProfileChallengeAllowed(int userHandle) {
- enforceSystemCaller("query separate challenge support");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "query separate challenge support"));
ComponentName profileOwner = getProfileOwnerAsUser(userHandle);
// Profile challenge is supported on N or newer release.
@@ -4505,8 +4507,7 @@
PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(parentUser);
final List<PasswordValidationError> passwordValidationErrors =
- PasswordMetrics.validatePasswordMetrics(
- minMetrics, complexity, false, metrics);
+ PasswordMetrics.validatePasswordMetrics(minMetrics, complexity, metrics);
isSufficient = passwordValidationErrors.isEmpty();
}
DevicePolicyEventLogger
@@ -4583,7 +4584,7 @@
maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity);
}
return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics),
- maxRequiredComplexity, false, metrics).isEmpty();
+ maxRequiredComplexity, metrics).isEmpty();
}
}
@@ -4619,8 +4620,7 @@
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();
}
@@ -4967,8 +4967,7 @@
// 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(
@@ -5457,7 +5456,7 @@
&& (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),
@@ -5660,8 +5659,7 @@
try (KeyChainConnection keyChainConnection =
KeyChain.bindAsUser(mContext, userHandle)) {
IKeyChainService keyChain = keyChainConnection.getService();
- keyChain.setGrant(granteeUid, alias, hasGrant);
- return true;
+ return keyChain.setGrant(granteeUid, alias, hasGrant);
} catch (RemoteException e) {
Slogf.e(LOG_TAG, "Setting grant for package.", e);
return false;
@@ -5987,7 +5985,8 @@
public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
final IBinder response) {
final CallerIdentity caller = getCallerIdentity();
- enforceSystemCaller("choose private key alias");
+ Preconditions.checkCallAuthorization(isSystemUid(caller),
+ String.format(NOT_SYSTEM_CALLER_MSG, "choose private key alias"));
// If there is a profile owner, redirect to that; otherwise query the device owner.
ComponentName aliasChooser = getProfileOwnerAsUser(caller.getUserId());
@@ -6535,7 +6534,8 @@
@Override
public String getAlwaysOnVpnPackageForUser(int userHandle) {
- enforceSystemCaller("getAlwaysOnVpnPackageForUser");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "call getAlwaysOnVpnPackageForUser"));
synchronized (getLockObject()) {
ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle);
return admin != null ? admin.mAlwaysOnVpnPackage : null;
@@ -6561,7 +6561,8 @@
@Override
public boolean isAlwaysOnVpnLockdownEnabledForUser(int userHandle) {
- enforceSystemCaller("isAlwaysOnVpnLockdownEnabledForUser");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "call isAlwaysOnVpnLockdownEnabledForUser"));
synchronized (getLockObject()) {
ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle);
return admin != null ? admin.mAlwaysOnVpnLockdown : null;
@@ -9160,10 +9161,8 @@
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
if ((mIsWatch || hasUserSetupCompleted(userHandle))) {
- if (!isCallerWithSystemUid()) {
- throw new IllegalStateException("Cannot set the profile owner on a user which is "
- + "already set-up");
- }
+ Preconditions.checkState(isSystemUid(caller),
+ "Cannot set the profile owner on a user which is already set-up");
if (!mIsWatch) {
// Only the default supervision profile owner can be set as profile owner after SUW
@@ -9318,10 +9317,6 @@
}
}
- private boolean isCallerWithSystemUid() {
- return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID);
- }
-
private boolean isSystemUid(CallerIdentity caller) {
return UserHandle.isSameApp(caller.getUid(), Process.SYSTEM_UID);
}
@@ -9752,7 +9747,8 @@
@Override
public ComponentName getRestrictionsProvider(int userHandle) {
- enforceSystemCaller("query the permission provider");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "query the permission provider"));
synchronized (getLockObject()) {
DevicePolicyData userData = getUserData(userHandle);
return userData != null ? userData.mRestrictionsProvider : null;
@@ -10022,7 +10018,9 @@
}
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkStringNotEmpty(packageName, "packageName is null");
- enforceSystemCaller("query if an accessibility service is disabled by admin");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG,
+ "query if an accessibility service is disabled by admin"));
synchronized (getLockObject()) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
@@ -10168,7 +10166,9 @@
}
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkStringNotEmpty(packageName, "packageName is null");
- enforceSystemCaller("query if an input method is disabled by admin");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG,
+ "query if an input method is disabled by admin"));
synchronized (getLockObject()) {
ActiveAdmin admin = getParentOfAdminIfRequired(
@@ -10228,7 +10228,9 @@
}
Preconditions.checkStringNotEmpty(packageName, "packageName is null or empty");
- enforceSystemCaller("query if a notification listener service is permitted");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG,
+ "query if a notification listener service is permitted"));
synchronized (getLockObject()) {
ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
@@ -10241,12 +10243,6 @@
}
}
- private void enforceSystemCaller(String action) {
- if (!isCallerWithSystemUid()) {
- throw new SecurityException("Only the system can " + action);
- }
- }
-
private void maybeSendAdminEnabledBroadcastLocked(int userHandle) {
DevicePolicyData policyData = getUserData(userHandle);
if (policyData.mAdminBroadcastPending) {
@@ -11790,7 +11786,8 @@
@Override
public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
- enforceSystemCaller("call notifyLockTaskModeChanged");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "call notifyLockTaskModeChanged"));
synchronized (getLockObject()) {
final DevicePolicyData policy = getUserData(userHandle);
@@ -13693,7 +13690,8 @@
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
- enforceSystemCaller("query support message for user");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "query support message for user"));
synchronized (getLockObject()) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
@@ -13710,7 +13708,8 @@
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
- enforceSystemCaller("query support message for user");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "query support message for user"));
synchronized (getLockObject()) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
@@ -13940,7 +13939,8 @@
if (!mHasFeature) {
return false;
}
- enforceSystemCaller("query restricted pkgs for a specific user");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG, "query restricted pkgs for a specific user"));
synchronized (getLockObject()) {
final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId);
@@ -14205,7 +14205,7 @@
}
synchronized (getLockObject()) {
- if (!isCallerWithSystemUid()) {
+ if (!isSystemUid(getCallerIdentity())) {
final CallerIdentity caller = getCallerIdentity(admin, packageName);
if (admin != null) {
Preconditions.checkCallAuthorization(
@@ -16603,7 +16603,9 @@
@Override
public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
- enforceSystemCaller("call canProfileOwnerResetPasswordWhenLocked");
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
+ String.format(NOT_SYSTEM_CALLER_MSG,
+ "call canProfileOwnerResetPasswordWhenLocked"));
synchronized (getLockObject()) {
final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(userId);
if (poAdmin == null
@@ -16769,7 +16771,9 @@
provisioningParams.isKeepAccountMigrated(), callerPackage);
if (provisioningParams.isOrganizationOwnedProvisioning()) {
- setProfileOwnerOnOrgOwnedDeviceState(admin, userInfo.id, caller.getUserId());
+ synchronized (getLockObject()) {
+ markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, userInfo.id);
+ }
}
return userInfo.getUserHandle();
@@ -17001,22 +17005,6 @@
}
}
- 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 4ce336d..9b42799 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -2660,9 +2660,6 @@
}
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:
@@ -2683,8 +2680,12 @@
}
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:
@@ -2709,7 +2710,8 @@
<< ", 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); });
@@ -2721,7 +2723,7 @@
}
void IncrementalService::DataLoaderStub::setCurrentStatus(int newStatus) {
- int targetStatus, oldStatus;
+ int oldStatus, oldTargetStatus, newTargetStatus;
DataLoaderStatusListener listener;
{
std::unique_lock lock(mMutex);
@@ -2730,22 +2732,31 @@
}
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 6a3d953..cdf2e89 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -126,7 +126,7 @@
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 @@
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 @@
}
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 @@
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();
@@ -1156,12 +1167,81 @@
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 d55bbd1..683fbd1 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.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.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.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.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.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 @@
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 @@
@Mock
private DeviceIdleInternal mDeviceIdleInternal;
@Mock
+ private PermissionManagerServiceInternal mPermissionManagerInternal;
+ @Mock
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Mock
private AppStandbyInternal mAppStandbyInternal;
@@ -351,7 +359,6 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
}
@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)));
-
- 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(true, true, MODE_ALLOWED);
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));
+ }
+
+ @Test
+ public void hasScheduleExactAlarmBinderCallNotDeclared() throws RemoteException {
+ mockExactAlarmPermissionGrant(false, false, MODE_DEFAULT);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockExactAlarmPermissionGrant(false, false, MODE_ALLOWED);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+
+ mockExactAlarmPermissionGrant(false, true, MODE_ALLOWED);
+ assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
}
@Test
@@ -1884,9 +1880,8 @@
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 @@
() -> 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 @@
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 @@
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 @@
() -> 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 @@
} 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 @@
() -> 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 @@
() -> 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 @@
() -> 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 @@
() -> 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 @@
() -> 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 @@
} 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 @@
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 @@
() -> 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 @@
@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 @@
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 ffbcc45..68cb8f9 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 @@
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 @@
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 0000000..96103e3
--- /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 c5ed20a..4d1f241 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.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.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 @@
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 2fcc021..2e00468 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 @@
}
@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 7cd6028..f156779 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 @@
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 @@
// 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 @@
// 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 @@
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 @@
// 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 0000000..88a21b4
--- /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 d784a22..8279624 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.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 @@
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 @@
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 @@
// 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 @@
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 f1e5782..b1582be 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 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.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 @@
@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 @@
@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 @@
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 @@
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 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 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 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 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 @@
};
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 ef7b274..011b8f8 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 @@
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 80da696..a29a76b 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 @@
@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 @@
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 @@
@Test
public void queryDisplayStatus() {
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
mHdmiControlService.queryDisplayStatus(new IHdmiControlCallback.Stub() {
@Override
public void onComplete(int result) {
@@ -1618,6 +1624,12 @@
@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 @@
@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 @@
}
@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 950b8a2..c7a508a 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 @@
@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 @@
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 5b01920..2307a85 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 @@
@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 d74bff2..4893173 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 @@
mPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
}
private OneTouchPlayAction createOneTouchPlayAction(HdmiCecLocalDevicePlayback device,
@@ -161,6 +162,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index fc26611..b76c279 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -175,7 +175,7 @@
final Context userContext = mContext.createPackageContextAsUser("system", 0,
UserHandle.of(userInfo.id));
assertThat(userContext.getSystemService(
- UserManager.class).sharesMediaWithParent()).isTrue();
+ UserManager.class).isMediaSharedWithParent()).isTrue();
List<UserInfo> list = mUserManager.getUsers();
List<UserInfo> cloneUsers = list.stream().filter(
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 b64810b..5234bb7 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 @@
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 @@
+ 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 @@
}
@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 0000000..4d1e405
--- /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 916a278..a246917 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.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.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.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.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 @@
}
static class MyInjector extends AppStandbyController.Injector {
+ @Mock
+ private PackageManagerInternal mPackageManagerInternal;
long mElapsedRealtime;
boolean mIsAppIdleEnabled = true;
boolean mIsCharging;
@@ -222,6 +231,7 @@
MyInjector(Context context, Looper looper) {
super(context, looper);
+ MockitoAnnotations.initMocks(this);
}
@Override
@@ -269,6 +279,11 @@
}
@Override
+ PackageManagerInternal getPackageManagerInternal() {
+ return mPackageManagerInternal;
+ }
+
+ @Override
void updatePowerWhitelistCache() {
}
@@ -491,6 +506,37 @@
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/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index 7d86961..b2e5ea0 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -21,13 +21,21 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.usage.UsageStatsManagerInternal;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.UserHandle;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -35,6 +43,9 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -89,6 +100,8 @@
private AppTimeLimitController mController;
+ @Mock private AlarmManager mMockAlarmManager;
+
private HandlerThread mThread;
private long mElapsedTime;
@@ -112,7 +125,12 @@
class MyAppTimeLimitController extends AppTimeLimitController {
MyAppTimeLimitController(AppTimeLimitController.TimeLimitCallbackListener listener,
Looper looper) {
- super(listener, looper);
+ super(InstrumentationRegistry.getContext(), listener, looper);
+ }
+
+ @Override
+ protected AlarmManager getAlarmManager() {
+ return mMockAlarmManager;
}
@Override
@@ -146,6 +164,8 @@
mThread = new HandlerThread("Test");
mThread.start();
mController = new MyAppTimeLimitController(mListener, mThread.getLooper());
+
+ MockitoAnnotations.initMocks(this);
}
@After
@@ -486,9 +506,14 @@
setTime(6_000L);
assertTrue(mLimitReachedLatch.await(6_000L, TimeUnit.MILLISECONDS));
stopUsage(PKG_SOC1);
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> onAlarmListenerArgumentCaptor =
+ ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+ verify(mMockAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
+ anyString(), onAlarmListenerArgumentCaptor.capture(), any());
// Usage has stopped, Session should end in a second. Verify session end occurs in a second
// (+/- 100ms, which is hopefully not too slim a margin)
assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+ onAlarmListenerArgumentCaptor.getValue().onAlarm();
assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
// Verify that the observer was not removed
assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
@@ -597,9 +622,14 @@
// Should call back by 11 seconds (6 earlier + 5 now)
assertTrue(mLimitReachedLatch.await(5_000L, TimeUnit.MILLISECONDS));
stopUsage(PKG_SOC1);
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> onAlarmListenerArgumentCaptor =
+ ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+ verify(mMockAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
+ anyString(), onAlarmListenerArgumentCaptor.capture(), any());
// Usage has stopped, Session should end in a second. Verify session end occurs in a second
// (+/- 100ms, which is hopefully not too slim a margin)
assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+ onAlarmListenerArgumentCaptor.getValue().onAlarm();
assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
// Verify that the observer was removed
assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
@@ -849,9 +879,14 @@
setTime(12_000L);
assertTrue(mLimitReachedLatch.await(1_000L, TimeUnit.MILLISECONDS));
stopUsage(PKG_SOC1);
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> onAlarmListenerArgumentCaptor =
+ ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+ verify(mMockAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
+ anyString(), onAlarmListenerArgumentCaptor.capture(), any());
// Usage has stopped, Session should end in 2 seconds. Verify session end occurs
// (+/- 100ms, which is hopefully not too slim a margin)
assertFalse(mSessionEndLatch.await(1_900L, TimeUnit.MILLISECONDS));
+ onAlarmListenerArgumentCaptor.getValue().onAlarm();
assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
// Verify that the observer was not removed
assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
@@ -882,9 +917,14 @@
setTime(18_000L);
assertTrue(mLimitReachedLatch.await(2000L, TimeUnit.MILLISECONDS));
stopUsage(PKG_SOC1);
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> onAlarmListenerArgumentCaptor =
+ ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+ verify(mMockAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
+ anyString(), onAlarmListenerArgumentCaptor.capture(), any());
// Usage has stopped, Session should end in 2 seconds. Verify session end occurs
// (+/- 100ms, which is hopefully not too slim a margin)
assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+ onAlarmListenerArgumentCaptor.getValue().onAlarm();
assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
// Verify that the observer was not removed
assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
@@ -903,9 +943,14 @@
setTime(11_000L);
assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
stopUsage(PKG_SOC1);
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> onAlarmListenerArgumentCaptor =
+ ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+ verify(mMockAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
+ anyString(), onAlarmListenerArgumentCaptor.capture(), any());
// Usage has stopped, Session should end in 1 seconds. Verify session end occurs
// (+/- 100ms, which is hopefully not too slim a margin)
assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+ onAlarmListenerArgumentCaptor.getValue().onAlarm();
assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
// Rearm the countdown latches
@@ -921,7 +966,10 @@
setTime(31_000L);
assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
stopUsage(PKG_SOC1);
+ verify(mMockAlarmManager, times(2)).setExact(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
+ anyString(), onAlarmListenerArgumentCaptor.capture(), any());
assertFalse(mSessionEndLatch.await(900L, TimeUnit.MILLISECONDS));
+ onAlarmListenerArgumentCaptor.getValue().onAlarm();
assertTrue(mSessionEndLatch.await(200L, TimeUnit.MILLISECONDS));
assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 1d715c8..0585758 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -156,7 +156,7 @@
mMinFrequency, mResonantFrequency, mFrequencyResolution,
suggestedFrequencyRange, mMaxAmplitudes);
return new VibratorInfo(vibratorId, mCapabilities, mSupportedEffects, mSupportedBraking,
- mSupportedPrimitives, mQFactor, frequencyMapping);
+ mSupportedPrimitives, null, mQFactor, frequencyMapping);
}
private void applyLatency() {
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 8c92a47..0e615a6 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 @@
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 @@
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 @@
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/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 37bc23e..9826031 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1142,4 +1142,38 @@
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/DisplayAreaPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
index 2a55083..147a44f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
@@ -28,6 +28,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -48,6 +49,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
/**
* Tests for the {@link DisplayAreaPolicy}.
@@ -183,6 +185,43 @@
taskDisplayArea5, taskDisplayArea1);
}
+ @Test
+ public void testTaskDisplayAreasCanHostHomeTask() {
+ final WindowManagerService wms = mWm;
+ final DisplayContent displayContent = mock(DisplayContent.class);
+ doReturn(true).when(displayContent).isTrusted();
+ doReturn(true).when(displayContent).supportsSystemDecorations();
+ final RootDisplayArea root = new SurfacelessDisplayAreaRoot(wms);
+ final TaskDisplayArea taskDisplayAreaWithHome = new TaskDisplayArea(displayContent, wms,
+ "Tasks1", FEATURE_DEFAULT_TASK_CONTAINER);
+ final TaskDisplayArea taskDisplayAreaWithNoHome = new TaskDisplayArea(displayContent, wms,
+ "Tasks2", FEATURE_VENDOR_FIRST + 1, false, false);
+ final DisplayArea.Tokens ime = new DisplayArea.Tokens(wms, ABOVE_TASKS, "Ime");
+ final DisplayAreaPolicy policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root)
+ .setImeContainer(ime)
+ .setTaskDisplayAreas(Lists.newArrayList(taskDisplayAreaWithHome,
+ taskDisplayAreaWithNoHome))
+ )
+ .build(wms);
+ assertTaskDisplayAreaPresentAndCanHaveHome(policy, FEATURE_DEFAULT_TASK_CONTAINER, true);
+ assertTaskDisplayAreaPresentAndCanHaveHome(policy, FEATURE_VENDOR_FIRST + 1, false);
+ final Task stackHome = taskDisplayAreaWithHome.getOrCreateRootHomeTask(true);
+ final Task stackNoHome = taskDisplayAreaWithNoHome.getOrCreateRootHomeTask(true);
+ assertNotNull(stackHome);
+ assertNull(stackNoHome);
+ }
+
+ private void assertTaskDisplayAreaPresentAndCanHaveHome(DisplayAreaPolicy policy,
+ int featureId,
+ boolean canHaveHome) {
+ Optional<DisplayArea> optionalDisplayArea = policy.mRoot.mChildren
+ .stream().filter(displayArea -> displayArea.mFeatureId == featureId)
+ .findAny();
+ assertTrue(optionalDisplayArea.isPresent());
+ assertEquals(canHaveHome, optionalDisplayArea.get().asTaskDisplayArea().canHostHomeTask());
+ }
+
private void assertTaskDisplayAreasOrder(DisplayAreaPolicy policy,
TaskDisplayArea... expectTdaOrder) {
List<TaskDisplayArea> expectOrder = new ArrayList<>();
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 4dbb2de..d3a825b 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 @@
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 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);
- // Speed up the test by a few seconds.
- mWm.mAtmService.deferWindowLayout();
- doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
-
- final Configuration displayConfig = displayContent.getConfiguration();
- final ActivityRecord pinnedActivity = createActivityRecord(displayContent,
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
- 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();
-
- assertEquals(homeConfigOrientation, displayConfig.orientation);
-
+ setBooted(mAtm);
clearInvocations(mWm);
+ // Speed up the test by a few seconds.
+ mAtm.deferWindowLayout();
+
+ final ActivityRecord homeActivity = createActivityRecord(
+ displayContent.getDefaultTaskDisplayArea().getRootHomeTask());
+ final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task pinnedTask = pinnedActivity.getRootTask();
+ doReturn((displayContent.getRotation() + 1) % 4).when(displayContent)
+ .rotationForActivityInDifferentOrientation(eq(homeActivity));
+ // Enter PiP from fullscreen.
+ pinnedTask.setWindowingMode(WINDOWING_MODE_PINNED);
+
+ 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);
+
+ 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 2321a73..e1aca55 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 @@
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 bdc4b4e..2c3f52e 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 @@
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 @@
}
/** 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 @@
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 1b9308d..f2418c6 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 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 @@
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 @@
}).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 @@
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/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 73404eb..95b5afc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1418,12 +1418,10 @@
public boolean mLastAllowed;
@Override
- void getTasks(int maxNum, List<RunningTaskInfo> list, boolean filterOnlyVisibleRecents,
- RootWindowContainer root, int callingUid, boolean allowed, boolean crossUser,
- ArraySet<Integer> profileIds) {
- mLastAllowed = allowed;
- super.getTasks(maxNum, list, filterOnlyVisibleRecents, root, callingUid, allowed,
- crossUser, profileIds);
+ void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
+ RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+ mLastAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
+ super.getTasks(maxNum, list, flags, root, callingUid, profileIds);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index e2be39b..c44c22f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -16,9 +16,14 @@
package com.android.server.wm;
+import static com.android.server.wm.RunningTasks.FLAG_ALLOWED;
+import static com.android.server.wm.RunningTasks.FLAG_CROSS_USERS;
+import static com.android.server.wm.RunningTasks.FLAG_KEEP_INTENT_EXTRA;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.app.ActivityManager.RunningTaskInfo;
@@ -83,8 +88,8 @@
// collected from all tasks across all the stacks
final int numFetchTasks = 5;
ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
- mRunningTasks.getTasks(5, tasks, false /* filterOnlyVisibleRecents */, mRootWindowContainer,
- -1 /* callingUid */, true /* allowed */, true /*crossUser */, PROFILE_IDS);
+ mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS, mRootWindowContainer,
+ -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numFetchTasks);
for (int i = 0; i < numFetchTasks; i++) {
assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -93,9 +98,8 @@
// Ensure that requesting more than the total number of tasks only returns the subset
// and does not crash
tasks.clear();
- mRunningTasks.getTasks(100, tasks, false /* filterOnlyVisibleRecents */,
- mRootWindowContainer, -1 /* callingUid */, true /* allowed */, true /* crossUser */,
- PROFILE_IDS);
+ mRunningTasks.getTasks(100, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numTasks);
for (int i = 0; i < numTasks; i++) {
assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -103,7 +107,7 @@
}
@Test
- public void testTaskInfo_expectNoExtras() {
+ public void testTaskInfo_expectNoExtrasByDefault() {
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
final int numTasks = 10;
for (int i = 0; i < numTasks; i++) {
@@ -118,9 +122,8 @@
final int numFetchTasks = 5;
final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
- mRunningTasks.getTasks(numFetchTasks, tasks, false /* filterOnlyVisibleRecents */,
- mRootWindowContainer, -1 /* callingUid */, true /* allowed */, true /*crossUser */,
- PROFILE_IDS);
+ mRunningTasks.getTasks(numFetchTasks, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numFetchTasks);
for (int i = 0; i < tasks.size(); i++) {
final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -128,6 +131,32 @@
}
}
+ @Test
+ public void testTaskInfo_expectExtrasWithKeepExtraFlag() {
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+ final int numTasks = 10;
+ for (int i = 0; i < numTasks; i++) {
+ final Task stack = new TaskBuilder(mSupervisor)
+ .setDisplay(display)
+ .setOnTop(true)
+ .build();
+ final Bundle data = new Bundle();
+ data.putInt("key", 100);
+ createTask(stack, ".Task" + i, i, i, data);
+ }
+
+ final int numFetchTasks = 5;
+ final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
+ mRunningTasks.getTasks(numFetchTasks, tasks,
+ FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
+ -1 /* callingUid */, PROFILE_IDS);
+ assertThat(tasks).hasSize(numFetchTasks);
+ for (int i = 0; i < tasks.size(); i++) {
+ final Bundle extras = tasks.get(i).baseIntent.getExtras();
+ assertNotNull(extras);
+ assertEquals(100, extras.getInt("key"));
+ }
+ }
/**
* Create a task with a single activity in it, with the given last active time.
@@ -145,6 +174,7 @@
.setComponent(new ComponentName(mContext.getPackageName(), ".TaskActivity"))
.setIntentExtras(extras)
.build();
+ task.intent = activity.intent;
return task;
}
}
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 212ffd5..0b1b877 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 @@
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 @@
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/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 9ac40ca..9b5d352 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1428,7 +1428,8 @@
@Override
public void onTransitionReady(IBinder transitToken, TransitionInfo transitionInfo,
- SurfaceControl.Transaction transaction) throws RemoteException {
+ SurfaceControl.Transaction transaction, SurfaceControl.Transaction finishT)
+ throws RemoteException {
mLastTransit = Transition.fromBinder(transitToken);
mLastReady = transitionInfo;
}
diff --git a/services/translation/java/com/android/server/translation/RemoteTranslationService.java b/services/translation/java/com/android/server/translation/RemoteTranslationService.java
index 82cb728..c77396d 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.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 @@
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 @@
mIdleUnbindTimeoutMs = TIMEOUT_IDLE_UNBIND_MS;
mRequestTimeoutMs = TIMEOUT_REQUEST_MS;
mComponentName = serviceName;
-
+ mRemoteCallback = callback;
// Bind right away.
connect();
}
@@ -67,7 +70,7 @@
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 6bba65d..628f8cd 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.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 @@
}
@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 @@
@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 d2a6951..4198d3b 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.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 @@
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 @@
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 @@
}
}
+ 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 @@
@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 @@
return;
}
try {
+ // TODO: Pipe uiTranslationSpec through to the UiTranslationController.
taskTopActivityTokens.getApplicationThread().updateUiTranslationState(
taskTopActivityTokens.getActivityToken(), state, sourceSpec, targetSpec,
viewIds);
@@ -221,4 +240,29 @@
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/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index 1dc1e77..f169926 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -17,8 +17,10 @@
package com.android.server.usage;
import android.annotation.UserIdInt;
+import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.usage.UsageStatsManagerInternal;
+import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -58,6 +60,10 @@
private final MyHandler mHandler;
+ private final Context mContext;
+
+ private AlarmManager mAlarmManager;
+
private TimeLimitCallbackListener mListener;
private static final long MAX_OBSERVER_PER_UID = 1000;
@@ -434,7 +440,7 @@
}
}
- class SessionUsageGroup extends UsageGroup {
+ class SessionUsageGroup extends UsageGroup implements AlarmManager.OnAlarmListener {
private long mNewSessionThresholdMs;
private PendingIntent mSessionEndCallback;
@@ -466,7 +472,7 @@
// New session has started, clear usage time.
mUsageTimeMs = 0;
}
- AppTimeLimitController.this.cancelInformSessionEndListener(this);
+ getAlarmManager().cancel(this);
}
super.noteUsageStart(startTimeMs, currentTimeMs);
}
@@ -479,10 +485,9 @@
if (mUsageTimeMs >= mTimeLimitMs) {
// Usage has ended. Schedule the session end callback to be triggered once
// the new session threshold has been reached
- AppTimeLimitController.this.postInformSessionEndListenerLocked(this,
- mNewSessionThresholdMs);
+ getAlarmManager().setExact(AlarmManager.ELAPSED_REALTIME,
+ getElapsedRealtime() + mNewSessionThresholdMs, TAG, this, mHandler);
}
-
}
}
@@ -499,6 +504,13 @@
}
@Override
+ public void onAlarm() {
+ synchronized (mLock) {
+ onSessionEnd();
+ }
+ }
+
+ @Override
@GuardedBy("mLock")
void dump(PrintWriter pw) {
super.dump(pw);
@@ -546,7 +558,6 @@
private class MyHandler extends Handler {
static final int MSG_CHECK_TIMEOUT = 1;
static final int MSG_INFORM_LIMIT_REACHED_LISTENER = 2;
- static final int MSG_INFORM_SESSION_END = 3;
MyHandler(Looper looper) {
super(looper);
@@ -565,11 +576,6 @@
((UsageGroup) msg.obj).onLimitReached();
}
break;
- case MSG_INFORM_SESSION_END:
- synchronized (mLock) {
- ((SessionUsageGroup) msg.obj).onSessionEnd();
- }
- break;
default:
super.handleMessage(msg);
break;
@@ -577,13 +583,24 @@
}
}
- public AppTimeLimitController(TimeLimitCallbackListener listener, Looper looper) {
+ public AppTimeLimitController(Context context, TimeLimitCallbackListener listener,
+ Looper looper) {
+ mContext = context;
mHandler = new MyHandler(looper);
mListener = listener;
}
/** Overrideable by a test */
@VisibleForTesting
+ protected AlarmManager getAlarmManager() {
+ if (mAlarmManager == null) {
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ }
+ return mAlarmManager;
+ }
+
+ /** Overrideable by a test */
+ @VisibleForTesting
protected long getElapsedRealtime() {
return SystemClock.elapsedRealtime();
}
@@ -985,18 +1002,6 @@
}
@GuardedBy("mLock")
- private void postInformSessionEndListenerLocked(SessionUsageGroup group, long timeout) {
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group),
- timeout);
- }
-
- @GuardedBy("mLock")
- private void cancelInformSessionEndListener(SessionUsageGroup group) {
- mHandler.removeMessages(MyHandler.MSG_INFORM_SESSION_END, group);
- }
-
- @GuardedBy("mLock")
private void postCheckTimeoutLocked(UsageGroup group, long timeout) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_CHECK_TIMEOUT, group),
timeout);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 309673d..f7e6375 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -252,7 +252,7 @@
mAppStandby = mInjector.getAppStandbyController(getContext());
- mAppTimeLimit = new AppTimeLimitController(
+ mAppTimeLimit = new AppTimeLimitController(getContext(),
new AppTimeLimitController.TimeLimitCallbackListener() {
@Override
public void onLimitReached(int observerId, int userId, long timeLimit,
diff --git a/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java b/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java
index 2f6a2b0..f34567f 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 @@
}
@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 d6ed98f..3fbd40f 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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java
index 6bf992e..2b4fb7d 100644
--- a/telephony/java/android/telephony/ims/DelegateStateCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -79,10 +79,30 @@
* messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration
* change event to reduce conditions where the remote application is using a stale IMS
* configuration.
+ * @deprecated This is being removed from API surface, Use
+ * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead.
*/
+ @Deprecated
void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config);
/**
+ * Call to notify the remote application of a configuration change associated with this
+ * {@link SipDelegate}.
+ * <p>
+ * The remote application will not be able to proceed sending SIP messages until after this
+ * configuration is sent the first time, so this configuration should be sent as soon as the
+ * {@link SipDelegate} has access to these configuration parameters.
+ * <p>
+ * Incoming SIP messages should not be routed to the remote application until AFTER this
+ * configuration change is sent to ensure that the remote application can respond correctly.
+ * Similarly, if there is an event that triggers the IMS configuration to change, incoming SIP
+ * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration
+ * change event to reduce conditions where the remote application is using a stale IMS
+ * configuration.
+ */
+ void onConfigurationChanged(@NonNull SipDelegateConfiguration config);
+
+ /**
* Call to notify the remote application that the {@link SipDelegate} has modified the IMS
* registration state of the RCS feature tags that were requested as part of the initial
* {@link DelegateRequest}.
diff --git a/telephony/java/android/telephony/ims/SipDelegateConfiguration.aidl b/telephony/java/android/telephony/ims/SipDelegateConfiguration.aidl
new file mode 100644
index 0000000..2fc9c48
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateConfiguration.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.telephony.ims;
+
+parcelable SipDelegateConfiguration;
diff --git a/telephony/java/android/telephony/ims/SipDelegateConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateConfiguration.java
new file mode 100644
index 0000000..1bf5cad
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SipDelegateConfiguration.java
@@ -0,0 +1,932 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.InetAddresses;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.Objects;
+
+/**
+ * The IMS registration and other attributes that the {@link SipDelegateConnection} used by the
+ * IMS application will need to be aware of to correctly generate outgoing {@link SipMessage}s.
+ * <p>
+ * The IMS service must generate new instances of this configuration as the IMS configuration
+ * managed by the IMS service changes. Along with each {@link SipDelegateConfiguration} instance
+ * containing the configuration is the "version", which should be incremented every time a new
+ * {@link SipDelegateConfiguration} instance is created. The {@link SipDelegateConnection} will
+ * include the version of the {@link SipDelegateConfiguration} instance that it used in order for
+ * the {@link SipDelegate} to easily identify if the IMS application used a now stale configuration
+ * to generate the {@link SipMessage} and return
+ * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION} in
+ * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} so that the IMS application can
+ * regenerate that {@link SipMessage} using the correct {@link SipDelegateConfiguration}
+ * instance.
+ * <p>
+ * Every time the IMS configuration state changes in the IMS service, a full configuration should
+ * be generated. The new {@link SipDelegateConfiguration} instance should not be an incremental
+ * update.
+ * @see Builder
+ * @hide
+ */
+@SystemApi
+public final class SipDelegateConfiguration implements Parcelable {
+
+ /**
+ * The SIP transport uses UDP.
+ */
+ public static final int SIP_TRANSPORT_UDP = 0;
+
+ /**
+ * The SIP transport uses TCP.
+ */
+ public static final int SIP_TRANSPORT_TCP = 1;
+
+ /**@hide*/
+ @IntDef(prefix = "SIP_TRANSPORT_", value = {
+ SIP_TRANSPORT_UDP,
+ SIP_TRANSPORT_TCP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TransportType {}
+
+ /**
+ * The value returned by {@link #getMaxUdpPayloadSizeBytes()} when it is not defined.
+ */
+ public static final int UDP_PAYLOAD_SIZE_UNDEFINED = -1;
+
+ /**
+ * SIP over IPSec configuration
+ */
+ public static final class IpSecConfiguration {
+ private final int mLocalTxPort;
+ private final int mLocalRxPort;
+ private final int mLastLocalTxPort;
+ private final int mRemoteTxPort;
+ private final int mRemoteRxPort;
+ private final int mLastRemoteTxPort;
+ private final String mSecurityHeader;
+
+ /**
+ * Describes the SIP over IPSec configuration the SipDelegate will need to use.
+ *
+ * @param localTxPort Local SIP port number used to send traffic.
+ * @param localRxPort Local SIP port number used to receive traffic.
+ * @param lastLocalTxPort Local SIP port number used for the previous IPsec security
+ * association.
+ * @param remoteTxPort Remote port number used by the SIP server to send SIP traffic.
+ * @param remoteRxPort Remote port number used by the SIP server to receive incoming SIP
+ * traffic.
+ * @param lastRemoteTxPort Remote port number used by the SIP server to send SIP traffic on
+ * the previous IPSec security association.
+ * @param securityHeader The value of the SIP security verify header.
+ */
+ public IpSecConfiguration(int localTxPort, int localRxPort, int lastLocalTxPort,
+ int remoteTxPort, int remoteRxPort, int lastRemoteTxPort,
+ @NonNull String securityHeader) {
+ mLocalTxPort = localTxPort;
+ mLocalRxPort = localRxPort;
+ mLastLocalTxPort = lastLocalTxPort;
+ mRemoteTxPort = remoteTxPort;
+ mRemoteRxPort = remoteRxPort;
+ mLastRemoteTxPort = lastRemoteTxPort;
+ mSecurityHeader = securityHeader;
+ }
+
+ /**
+ * @return The local SIP port number used to send traffic.
+ */
+ public int getLocalTxPort() {
+ return mLocalTxPort;
+ }
+
+ /**
+ * @return The Local SIP port number used to receive traffic.
+ */
+ public int getLocalRxPort() {
+ return mLocalRxPort;
+ }
+
+ /**
+ * @return The last local SIP port number used for the previous IPsec security association.
+ */
+ public int getLastLocalTxPort() {
+ return mLastLocalTxPort;
+ }
+
+ /**
+ * @return The remote port number used by the SIP server to send SIP traffic.
+ */
+ public int getRemoteTxPort() {
+ return mRemoteTxPort;
+ }
+
+ /**
+ * @return the remote port number used by the SIP server to receive incoming SIP traffic.
+ */
+ public int getRemoteRxPort() {
+ return mRemoteRxPort;
+ }
+
+ /**
+ * @return the remote port number used by the SIP server to send SIP traffic on the previous
+ * IPSec security association.
+ */
+ public int getLastRemoteTxPort() {
+ return mLastRemoteTxPort;
+ }
+
+ /**
+ * @return The value of the SIP security verify header.
+ */
+ public @NonNull String getSipSecurityVerifyHeader() {
+ return mSecurityHeader;
+ }
+
+ /**
+ * Helper for parcelling this object.
+ * @hide
+ */
+ public void addToParcel(Parcel dest) {
+ dest.writeInt(mLocalTxPort);
+ dest.writeInt(mLocalRxPort);
+ dest.writeInt(mLastLocalTxPort);
+ dest.writeInt(mRemoteTxPort);
+ dest.writeInt(mRemoteRxPort);
+ dest.writeInt(mLastRemoteTxPort);
+ dest.writeString(mSecurityHeader);
+ }
+
+ /**
+ * Helper for unparcelling this object.
+ * @hide
+ */
+ public static IpSecConfiguration fromParcel(Parcel source) {
+ return new IpSecConfiguration(source.readInt(), source.readInt(), source.readInt(),
+ source.readInt(), source.readInt(), source.readInt(), source.readString());
+ }
+
+ @Override
+ public String toString() {
+ return "IpSecConfiguration{" + "localTx=" + mLocalTxPort + ", localRx=" + mLocalRxPort
+ + ", lastLocalTx=" + mLastLocalTxPort + ", remoteTx=" + mRemoteTxPort
+ + ", remoteRx=" + mRemoteRxPort + ", lastRemoteTx=" + mLastRemoteTxPort
+ + ", securityHeader=" + mSecurityHeader + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ IpSecConfiguration that = (IpSecConfiguration) o;
+ return mLocalTxPort == that.mLocalTxPort
+ && mLocalRxPort == that.mLocalRxPort
+ && mLastLocalTxPort == that.mLastLocalTxPort
+ && mRemoteTxPort == that.mRemoteTxPort
+ && mRemoteRxPort == that.mRemoteRxPort
+ && mLastRemoteTxPort == that.mLastRemoteTxPort
+ && Objects.equals(mSecurityHeader, that.mSecurityHeader);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mLocalTxPort, mLocalRxPort, mLastLocalTxPort, mRemoteTxPort,
+ mRemoteRxPort, mLastRemoteTxPort, mSecurityHeader);
+ }
+ }
+
+ /**
+ * Creates a new instance of {@link SipDelegateConfiguration} composed from optional
+ * configuration items.
+ */
+ public static final class Builder {
+ private final SipDelegateConfiguration mConfig;
+
+ /**
+ *
+ * @param version The version associated with the {@link SipDelegateConfiguration} instance
+ * being built. See {@link #getVersion()} for more information.
+ * @param transportType The transport type to use for SIP signalling.
+ * @param localAddr The local socket address used for SIP traffic.
+ * @param serverAddr The SIP server or P-CSCF default IP address for sip traffic.
+ * @see InetAddresses#parseNumericAddress(String) for how to create an
+ * {@link InetAddress} without requiring a DNS lookup.
+ */
+ public Builder(@IntRange(from = 0) long version, @TransportType int transportType,
+ @NonNull InetSocketAddress localAddr, @NonNull InetSocketAddress serverAddr) {
+ mConfig = new SipDelegateConfiguration(version, transportType, localAddr,
+ serverAddr);
+ }
+
+ /**
+ * Create a new {@link SipDelegateConfiguration} instance with the same exact configuration
+ * as the passed in instance, except for the version parameter, which will be incremented
+ * by 1.
+ * <p>
+ * This method is useful for cases where only a small subset of configurations have changed
+ * and the new configuration is based off of the old configuration.
+ * @param c The older {@link SipDelegateConfiguration} instance to base this instance's
+ * configuration off of.
+ */
+ public Builder(@NonNull SipDelegateConfiguration c) {
+ mConfig = c.copyAndIncrementVersion();
+ }
+
+ /**
+ * Sets whether or not SIP compact form is enabled for the associated SIP delegate.
+ * <p>
+ * If unset, this configuration defaults to {@code false}.
+ * @param isEnabled {@code true} if SIP compact form is enabled for the associated SIP
+ * Delegate, {@code false} if it is not.
+ * @return this Builder instance with the compact form configuration set.
+ */
+ public @NonNull Builder setSipCompactFormEnabled(boolean isEnabled) {
+ mConfig.mIsSipCompactFormEnabled = isEnabled;
+ return this;
+ }
+
+ /**
+ * Sets whether or not underlying SIP keepalives are enabled for the associated SIP
+ * delegate.
+ * <p>
+ * If unset, this configuration defaults to {@code false}.
+ * @param isEnabled {@code true} if SIP keepalives are enabled for the associated SIP
+ * Delegate, {@code false} if it is not.
+ * @return this Builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipKeepaliveEnabled(boolean isEnabled) {
+ mConfig.mIsSipKeepaliveEnabled = isEnabled;
+ return this;
+ }
+
+ /**
+ * Sets the max SIP payload size in bytes to be sent on UDP. If the SIP message payload is
+ * greater than the max UDP payload size, then TCP must be used.
+ * <p>
+ * If unset, this configuration defaults to {@link #UDP_PAYLOAD_SIZE_UNDEFINED}, or no
+ * size specified.
+ * @param size The maximum SIP payload size in bytes for UDP.
+ * @return this Builder instance with the new configuration set.
+ */
+ public @NonNull Builder setMaxUdpPayloadSizeBytes(@IntRange(from = 1) int size) {
+ mConfig.mMaxUdpPayloadSize = size;
+ return this;
+ }
+
+ /**
+ * Sets the IMS public user identifier.
+ * <p>
+ * If unset, this configuration defaults to {@code null}, or no identifier specified.
+ * @param id The IMS public user identifier.
+ * @return this Builder instance with the new configuration set.
+ */
+ public @NonNull Builder setPublicUserIdentifier(@Nullable String id) {
+ mConfig.mPublicUserIdentifier = id;
+ return this;
+ }
+
+ /**
+ * Sets the IMS private user identifier.
+ * <p>
+ * If unset, this configuration defaults to {@code null}, or no identifier specified.
+ * @param id The IMS private user identifier.
+ * @return this Builder instance with the new configuration set.
+ */
+ public @NonNull Builder setPrivateUserIdentifier(@Nullable String id) {
+ mConfig.mPrivateUserIdentifier = id;
+ return this;
+ }
+
+ /**
+ * Sets the IMS home domain.
+ * <p>
+ * If unset, this configuration defaults to {@code null}, or no domain specified.
+ * @param domain The IMS home domain.
+ * @return this Builder instance with the new configuration set.
+ */
+ public @NonNull Builder setHomeDomain(@Nullable String domain) {
+ mConfig.mHomeDomain = domain;
+ return this;
+ }
+
+ /**
+ * Sets the IMEI of the associated device.
+ * <p>
+ * Application can include the Instance-ID feature tag {@code "+sip.instance"} in the
+ * Contact header with a value of the device IMEI in the form
+ * {@code "urn:gsma:imei:<device IMEI>"}.
+ * <p>
+ * If unset, this configuration defaults to {@code null}, or no IMEI string specified.
+ * @param imei The IMEI of the device.
+ * @return this Builder instance with the new configuration set.
+ */
+ public @NonNull Builder setImei(@Nullable String imei) {
+ mConfig.mImei = imei;
+ return this;
+ }
+
+ /**
+ * Set the optional {@link IpSecConfiguration} instance used if the associated SipDelegate
+ * is communicating over IPSec.
+ * <p>
+ * If unset, this configuration defaults to {@code null}
+ * @param c The IpSecConfiguration instance to set.
+ * @return this Builder instance with IPSec configuration set.
+ */
+ public @NonNull Builder setIpSecConfiguration(@Nullable IpSecConfiguration c) {
+ mConfig.mIpSecConfiguration = c;
+ return this;
+ }
+
+ /**
+ * Describes the Device's Public IP Address and port that is set when Network Address
+ * Translation is enabled and the device is behind a NAT.
+ * <p>
+ * If unset, this configuration defaults to {@code null}
+ * @param addr The {@link InetAddress} representing the device's public IP address and port
+ * when behind a NAT.
+ * @return this Builder instance with the new configuration set.
+ * @see InetAddresses#parseNumericAddress(String) For an example of how to create an
+ * instance of {@link InetAddress} without causing a DNS lookup.
+ */
+ public @NonNull Builder setNatSocketAddress(@Nullable InetSocketAddress addr) {
+ mConfig.mNatAddress = addr;
+ return this;
+ }
+
+ /**
+ * Sets the optional URI of the device's Globally routable user-agent URI (GRUU) if this
+ * feature is enabled for the SIP delegate.
+ * <p>
+ * If unset, this configuration defaults to {@code null}
+ * @param uri The GRUU to set.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setPublicGruuUri(@Nullable Uri uri) {
+ mConfig.mGruu = uri;
+ return this;
+ }
+
+ /**
+ * Sets the SIP authentication header value.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP authentication header's value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipAuthenticationHeader(@Nullable String header) {
+ mConfig.mSipAuthHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP authentication nonce.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param nonce The SIP authentication nonce.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipAuthenticationNonce(@Nullable String nonce) {
+ mConfig.mSipAuthNonce = nonce;
+ return this;
+ }
+
+ /**
+ * Sets the SIP service route header value.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP service route header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipServiceRouteHeader(@Nullable String header) {
+ mConfig.mServiceRouteHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP path header value.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP path header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipPathHeader(@Nullable String header) {
+ mConfig.mPathHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP User-Agent header value.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP User-Agent header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipUserAgentHeader(@Nullable String header) {
+ mConfig.mUserAgentHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP Contact header's User parameter value.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param param The SIP Contact header's User parameter value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipContactUserParameter(@Nullable String param) {
+ mConfig.mContactUserParam = param;
+ return this;
+ }
+
+ /**
+ * Sets the SIP P-Access-Network-Info (P-ANI) header value. Populated for networks that
+ * require this information to be provided as part of outgoing SIP messages.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP P-ANI header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipPaniHeader(@Nullable String header) {
+ mConfig.mPaniHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP P-Last-Access-Network-Info (P-LANI) header value. Populated for
+ * networks that require this information to be provided as part of outgoing SIP messages.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP P-LANI header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipPlaniHeader(@Nullable String header) {
+ mConfig.mPlaniHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP Cellular-Network-Info (CNI) header value (See 3GPP 24.229, section 7.2.15),
+ * populated for networks that require this information to be provided as part of outgoing
+ * SIP messages.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP P-LANI header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipCniHeader(@Nullable String header) {
+ mConfig.mCniHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the SIP P-associated-uri header value.
+ * <p>
+ * If unset, this configuration defaults to {@code null}.
+ * @param header The SIP P-associated-uri header value.
+ * @return this builder instance with the new configuration set.
+ */
+ public @NonNull Builder setSipAssociatedUriHeader(@Nullable String header) {
+ mConfig.mAssociatedUriHeader = header;
+ return this;
+ }
+
+ /**
+ * @return A {@link SipDelegateConfiguration} instance with the optional configurations set.
+ */
+ public @NonNull SipDelegateConfiguration build() {
+ return mConfig;
+ }
+ }
+
+ private final long mVersion;
+ private final int mTransportType;
+ private final InetSocketAddress mLocalAddress;
+ private final InetSocketAddress mSipServerAddress;
+ private boolean mIsSipCompactFormEnabled = false;
+ private boolean mIsSipKeepaliveEnabled = false;
+ private int mMaxUdpPayloadSize = -1;
+ private String mPublicUserIdentifier = null;
+ private String mPrivateUserIdentifier = null;
+ private String mHomeDomain = null;
+ private String mImei = null;
+ private Uri mGruu = null;
+ private String mSipAuthHeader = null;
+ private String mSipAuthNonce = null;
+ private String mServiceRouteHeader = null;
+ private String mPathHeader = null;
+ private String mUserAgentHeader = null;
+ private String mContactUserParam = null;
+ private String mPaniHeader = null;
+ private String mPlaniHeader = null;
+ private String mCniHeader = null;
+ private String mAssociatedUriHeader = null;
+ private IpSecConfiguration mIpSecConfiguration = null;
+ private InetSocketAddress mNatAddress = null;
+
+
+ private SipDelegateConfiguration(long version, int transportType,
+ InetSocketAddress localAddress, InetSocketAddress sipServerAddress) {
+ mVersion = version;
+ mTransportType = transportType;
+ mLocalAddress = localAddress;
+ mSipServerAddress = sipServerAddress;
+ }
+
+ private SipDelegateConfiguration(Parcel source) {
+ mVersion = source.readLong();
+ mTransportType = source.readInt();
+ mLocalAddress = readAddressFromParcel(source);
+ mSipServerAddress = readAddressFromParcel(source);
+ mIsSipCompactFormEnabled = source.readBoolean();
+ mIsSipKeepaliveEnabled = source.readBoolean();
+ mMaxUdpPayloadSize = source.readInt();
+ mPublicUserIdentifier = source.readString();
+ mPrivateUserIdentifier = source.readString();
+ mHomeDomain = source.readString();
+ mImei = source.readString();
+ mGruu = source.readParcelable(null);
+ mSipAuthHeader = source.readString();
+ mSipAuthNonce = source.readString();
+ mServiceRouteHeader = source.readString();
+ mPathHeader = source.readString();
+ mUserAgentHeader = source.readString();
+ mContactUserParam = source.readString();
+ mPaniHeader = source.readString();
+ mPlaniHeader = source.readString();
+ mCniHeader = source.readString();
+ mAssociatedUriHeader = source.readString();
+ boolean isIpsecConfigAvailable = source.readBoolean();
+ if (isIpsecConfigAvailable) {
+ mIpSecConfiguration = IpSecConfiguration.fromParcel(source);
+ }
+ boolean isNatConfigAvailable = source.readBoolean();
+ if (isNatConfigAvailable) {
+ mNatAddress = readAddressFromParcel(source);
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mVersion);
+ dest.writeInt(mTransportType);
+ writeAddressToParcel(mLocalAddress, dest);
+ writeAddressToParcel(mSipServerAddress, dest);
+ dest.writeBoolean(mIsSipCompactFormEnabled);
+ dest.writeBoolean(mIsSipKeepaliveEnabled);
+ dest.writeInt(mMaxUdpPayloadSize);
+ dest.writeString(mPublicUserIdentifier);
+ dest.writeString(mPrivateUserIdentifier);
+ dest.writeString(mHomeDomain);
+ dest.writeString(mImei);
+ dest.writeParcelable(mGruu, flags);
+ dest.writeString(mSipAuthHeader);
+ dest.writeString(mSipAuthNonce);
+ dest.writeString(mServiceRouteHeader);
+ dest.writeString(mPathHeader);
+ dest.writeString(mUserAgentHeader);
+ dest.writeString(mContactUserParam);
+ dest.writeString(mPaniHeader);
+ dest.writeString(mPlaniHeader);
+ dest.writeString(mCniHeader);
+ dest.writeString(mAssociatedUriHeader);
+ dest.writeBoolean(mIpSecConfiguration != null);
+ if (mIpSecConfiguration != null) {
+ mIpSecConfiguration.addToParcel(dest);
+ }
+ dest.writeBoolean(mNatAddress != null);
+ if (mNatAddress != null) {
+ writeAddressToParcel(mNatAddress, dest);
+ }
+ }
+
+ /**
+ * @return A copy of this instance with an incremented version.
+ * @hide
+ */
+ public SipDelegateConfiguration copyAndIncrementVersion() {
+ SipDelegateConfiguration c = new SipDelegateConfiguration(getVersion() + 1, mTransportType,
+ mLocalAddress, mSipServerAddress);
+ c.mIsSipCompactFormEnabled = mIsSipCompactFormEnabled;
+ c.mIsSipKeepaliveEnabled = mIsSipKeepaliveEnabled;
+ c.mMaxUdpPayloadSize = mMaxUdpPayloadSize;
+ c.mIpSecConfiguration = mIpSecConfiguration;
+ c.mNatAddress = mNatAddress;
+ c.mPublicUserIdentifier = mPublicUserIdentifier;
+ c.mPrivateUserIdentifier = mPrivateUserIdentifier;
+ c.mHomeDomain = mHomeDomain;
+ c.mImei = mImei;
+ c.mGruu = mGruu;
+ c.mSipAuthHeader = mSipAuthHeader;
+ c.mSipAuthNonce = mSipAuthNonce;
+ c.mServiceRouteHeader = mServiceRouteHeader;
+ c.mPathHeader = mPathHeader;
+ c.mUserAgentHeader = mUserAgentHeader;
+ c.mContactUserParam = mContactUserParam;
+ c.mPaniHeader = mPaniHeader;
+ c.mPlaniHeader = mPlaniHeader;
+ c.mCniHeader = mCniHeader;
+ c.mAssociatedUriHeader = mAssociatedUriHeader;
+ return c;
+ }
+
+ /**
+ * An integer representing the version number of this SipDelegateImsConfiguration.
+ * {@link SipMessage}s that are created using this configuration will also have a this
+ * version number associated with them, which will allow the IMS service to validate that the
+ * {@link SipMessage} was using the latest configuration during creation and not a stale
+ * configuration due to race conditions between the configuration being updated and the RCS
+ * application not receiving the updated configuration before generating a new message.
+ * <p>
+ * The version number should be a positive number that starts at 0 and increments sequentially
+ * as new {@link SipDelegateConfiguration} instances are created to update the IMS
+ * configuration state.
+ *
+ * @return the version number associated with this {@link SipDelegateConfiguration}.
+ */
+ public @IntRange(from = 0) long getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * @return The Transport type of the SIP delegate.
+ */
+ public @TransportType int getTransportType() {
+ return mTransportType;
+ }
+
+ /**
+ * @return The local IP address and port used for SIP traffic.
+ */
+ public @NonNull InetSocketAddress getLocalAddress() {
+ return mLocalAddress;
+ }
+
+ /**
+ * @return The default IP Address and port of the SIP server or P-CSCF used for SIP traffic.
+ */
+ public @NonNull InetSocketAddress getSipServerAddress() {
+ return mSipServerAddress;
+ }
+
+ /**
+ * @return {@code true} if SIP compact form is enabled for the associated SIP Delegate,
+ * {@code false} if it is not.
+ */
+ public boolean isSipCompactFormEnabled() {
+ return mIsSipCompactFormEnabled;
+ }
+
+ /**
+ * @return {@code true} if SIP keepalives are enabled for the associated SIP Delegate,
+ * {@code false} if it is not.
+ */
+ public boolean isSipKeepaliveEnabled() {
+ return mIsSipKeepaliveEnabled;
+ }
+
+ /**
+ * @return The maximum SIP payload size in bytes for UDP or {code -1} if no value was set.
+ */
+ public int getMaxUdpPayloadSizeBytes() {
+ return mMaxUdpPayloadSize;
+ }
+
+ /**
+ * @return The IMS public user identifier or {@code null} if it was not set.
+ */
+ public @Nullable String getPublicUserIdentifier() {
+ return mPublicUserIdentifier;
+ }
+
+ /**
+ * @return The IMS private user identifier or {@code null} if it was not set.
+ */
+ public @Nullable String getPrivateUserIdentifier() {
+ return mPrivateUserIdentifier;
+ }
+
+ /**
+ * @return The IMS home domain or {@code null} if it was not set.
+ */
+ public @Nullable String getHomeDomain() {
+ return mHomeDomain;
+ }
+
+ /**
+ * get the IMEI of the associated device.
+ * <p>
+ * Application can include the Instance-ID feature tag {@code "+sip.instance"} in the Contact
+ * header with a value of the device IMEI in the form {@code "urn:gsma:imei:<device IMEI>"}.
+ * @return The IMEI of the device or {@code null} if it was not set.
+ */
+ public @Nullable String getImei() {
+ return mImei;
+ }
+
+ /**
+ * @return The IPSec configuration that must be used because SIP is communicating over IPSec.
+ * This returns {@code null} SIP is not communicating over IPSec.
+ */
+ public @Nullable IpSecConfiguration getIpSecConfiguration() {
+ return mIpSecConfiguration;
+ }
+
+ /**
+ * @return The public IP address and port of the device due to it being behind a NAT.
+ * This returns {@code null} if the device is not behind a NAT.
+ */
+ public @Nullable InetSocketAddress getNatSocketAddress() {
+ return mNatAddress;
+ }
+
+ /**
+ * @return The device's Globally routable user-agent URI (GRUU) or {@code null} if this feature
+ * is not enabled for the SIP delegate.
+ */
+ public @Nullable Uri getPublicGruuUri() {
+ return mGruu;
+ }
+
+ /**
+ * @return The value of the SIP authentication header or {@code null} if there is none set.
+ */
+ public @Nullable String getSipAuthenticationHeader() {
+ return mSipAuthHeader;
+ }
+
+ /**
+ * @return The value of the SIP authentication nonce or {@code null} if there is none set.
+ */
+ public @Nullable String getSipAuthenticationNonce() {
+ return mSipAuthNonce;
+ }
+
+ /**
+ * @return The value of the SIP service route header or {@code null} if there is none set.
+ */
+ public @Nullable String getSipServiceRouteHeader() {
+ return mServiceRouteHeader;
+ }
+
+ /**
+ * @return The value of the SIP path header or {@code null} if there is none set.
+ */
+ public @Nullable String getSipPathHeader() {
+ return mPathHeader;
+ }
+
+ /**
+ * @return The value of the SIP User-Agent header or {@code null} if there is none set.
+ */
+ public @Nullable String getSipUserAgentHeader() {
+ return mUserAgentHeader;
+ }
+ /**
+ * @return The value of the SIP Contact header's User parameter or {@code null} if there is
+ * none set.
+ */
+ public @Nullable String getSipContactUserParameter() {
+ return mContactUserParam;
+ }
+
+ /**
+ * @return The value of the SIP P-Access-Network-Info (P-ANI) header or {@code null} if there is
+ * none set.
+ */
+ public @Nullable String getSipPaniHeader() {
+ return mPaniHeader;
+ }
+ /**
+ * @return The value of the SIP P-Last-Access-Network-Info (P-LANI) header or {@code null} if
+ * there is none set.
+ */
+ public @Nullable String getSipPlaniHeader() {
+ return mPlaniHeader;
+ }
+
+ /**
+ * @return The value of the SIP Cellular-Network-Info (CNI) header or {@code null} if there is
+ * none set.
+ */
+ public @Nullable String getSipCniHeader() {
+ return mCniHeader;
+ }
+
+ /**
+ * @return The value of the SIP P-associated-uri header or {@code null} if there is none set.
+ */
+ public @Nullable String getSipAssociatedUriHeader() {
+ return mAssociatedUriHeader;
+ }
+
+ private void writeAddressToParcel(InetSocketAddress addr, Parcel dest) {
+ dest.writeByteArray(addr.getAddress().getAddress());
+ dest.writeInt(addr.getPort());
+ }
+
+ private InetSocketAddress readAddressFromParcel(Parcel source) {
+ final byte[] addressBytes = source.createByteArray();
+ final int port = source.readInt();
+ try {
+ return new InetSocketAddress(InetAddress.getByAddress(addressBytes), port);
+ } catch (UnknownHostException e) {
+ // Should not happen, as length of array was verified before parcelling.
+ Log.e("SipDelegateConfiguration", "exception reading address, returning null");
+ return null;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<SipDelegateConfiguration> CREATOR =
+ new Creator<SipDelegateConfiguration>() {
+ @Override
+ public SipDelegateConfiguration createFromParcel(Parcel source) {
+ return new SipDelegateConfiguration(source);
+ }
+
+ @Override
+ public SipDelegateConfiguration[] newArray(int size) {
+ return new SipDelegateConfiguration[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SipDelegateConfiguration that = (SipDelegateConfiguration) o;
+ return mVersion == that.mVersion
+ && mTransportType == that.mTransportType
+ && mIsSipCompactFormEnabled == that.mIsSipCompactFormEnabled
+ && mIsSipKeepaliveEnabled == that.mIsSipKeepaliveEnabled
+ && mMaxUdpPayloadSize == that.mMaxUdpPayloadSize
+ && Objects.equals(mLocalAddress, that.mLocalAddress)
+ && Objects.equals(mSipServerAddress, that.mSipServerAddress)
+ && Objects.equals(mPublicUserIdentifier, that.mPublicUserIdentifier)
+ && Objects.equals(mPrivateUserIdentifier, that.mPrivateUserIdentifier)
+ && Objects.equals(mHomeDomain, that.mHomeDomain)
+ && Objects.equals(mImei, that.mImei)
+ && Objects.equals(mGruu, that.mGruu)
+ && Objects.equals(mSipAuthHeader, that.mSipAuthHeader)
+ && Objects.equals(mSipAuthNonce, that.mSipAuthNonce)
+ && Objects.equals(mServiceRouteHeader, that.mServiceRouteHeader)
+ && Objects.equals(mPathHeader, that.mPathHeader)
+ && Objects.equals(mUserAgentHeader, that.mUserAgentHeader)
+ && Objects.equals(mContactUserParam, that.mContactUserParam)
+ && Objects.equals(mPaniHeader, that.mPaniHeader)
+ && Objects.equals(mPlaniHeader, that.mPlaniHeader)
+ && Objects.equals(mCniHeader, that.mCniHeader)
+ && Objects.equals(mAssociatedUriHeader, that.mAssociatedUriHeader)
+ && Objects.equals(mIpSecConfiguration, that.mIpSecConfiguration)
+ && Objects.equals(mNatAddress, that.mNatAddress);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mVersion, mTransportType, mLocalAddress, mSipServerAddress,
+ mIsSipCompactFormEnabled, mIsSipKeepaliveEnabled, mMaxUdpPayloadSize,
+ mPublicUserIdentifier, mPrivateUserIdentifier, mHomeDomain, mImei, mGruu,
+ mSipAuthHeader, mSipAuthNonce, mServiceRouteHeader, mPathHeader, mUserAgentHeader,
+ mContactUserParam, mPaniHeader, mPlaniHeader, mCniHeader, mAssociatedUriHeader,
+ mIpSecConfiguration, mNatAddress);
+ }
+
+ @Override
+ public String toString() {
+ return "SipDelegateConfiguration{ mVersion=" + mVersion + ", mTransportType="
+ + mTransportType + '}';
+ }
+}
diff --git a/telephony/java/android/telephony/ims/SipDelegateConnection.java b/telephony/java/android/telephony/ims/SipDelegateConnection.java
index d7a19bc..4dbb08d 100644
--- a/telephony/java/android/telephony/ims/SipDelegateConnection.java
+++ b/telephony/java/android/telephony/ims/SipDelegateConnection.java
@@ -48,7 +48,7 @@
* sending the message.
* @param sipMessage The SipMessage to be sent.
* @param configVersion The SipDelegateImsConfiguration version used to construct the
- * SipMessage. See {@link SipDelegateImsConfiguration#getVersion} for more
+ * SipMessage. See {@link SipDelegateConfiguration#getVersion} for more
*/
void sendMessage(@NonNull SipMessage sipMessage, long configVersion);
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
index 0d63f7b..ffbfde6 100644
--- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -21,35 +21,21 @@
import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.net.InetAddresses;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
-import android.telephony.ims.stub.SipDelegate;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.net.InetSocketAddress;
/**
- * The IMS registration and other attributes that the {@link SipDelegateConnection} used by the
- * IMS application will need to be aware of to correctly generate outgoing {@link SipMessage}s.
- * <p>
- * The IMS service must generate new instances of this configuration as the IMS configuration
- * managed by the IMS service changes. Along with each {@link SipDelegateImsConfiguration} instance
- * containing the configuration is the "version", which should be incremented every time a new
- * {@link SipDelegateImsConfiguration} instance is created. The {@link SipDelegateConnection} will
- * include the version of the {@link SipDelegateImsConfiguration} instance that it used in order for
- * the {@link SipDelegate} to easily identify if the IMS application used a now stale configuration
- * to generate the {@link SipMessage} and return
- * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION} in
- * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} so that the IMS application can
- * regenerate that {@link SipMessage} using the correct {@link SipDelegateImsConfiguration}
- * instance.
- * <p>
- * Every time the IMS configuration state changes in the IMS service, a full configuration should
- * be generated. The new {@link SipDelegateImsConfiguration} instance should not be an incremental
- * update.
* @hide
+ * @deprecated Use {@link SipDelegateConfiguration} instead.
*/
+@Deprecated
@SystemApi
public final class SipDelegateImsConfiguration implements Parcelable {
@@ -535,4 +521,68 @@
return new SipDelegateImsConfiguration[size];
}
};
+
+ /**
+ * Temporary helper to transition from old form of config to new form.
+ * @return new config
+ * @hide
+ */
+ public SipDelegateConfiguration toNewConfig() {
+ // IP version is now included in call to InetSocketAddr
+ String transportTypeString = getString(KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING);
+ int transportType = (transportTypeString != null
+ && transportTypeString.equals(SIP_TRANSPORT_UDP))
+ ? SipDelegateConfiguration.SIP_TRANSPORT_UDP
+ : SipDelegateConfiguration.SIP_TRANSPORT_TCP;
+ SipDelegateConfiguration.Builder builder = new SipDelegateConfiguration.Builder(mVersion,
+ transportType,
+ getSocketAddr(getString(KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING),
+ getInt(KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT, -1)),
+ getSocketAddr(getString(KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING),
+ getInt(KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT, -1)));
+ builder.setSipCompactFormEnabled(
+ getBoolean(KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL, false));
+ builder.setSipKeepaliveEnabled(
+ getBoolean(KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL, false));
+ builder.setMaxUdpPayloadSizeBytes(getInt(KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT, -1));
+ builder.setPublicUserIdentifier(getString(KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING));
+ builder.setPrivateUserIdentifier(getString(KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING));
+ builder.setHomeDomain(getString(KEY_SIP_CONFIG_HOME_DOMAIN_STRING));
+ builder.setImei(getString(KEY_SIP_CONFIG_IMEI_STRING));
+ builder.setSipAuthenticationHeader(getString(KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING));
+ builder.setSipAuthenticationNonce(getString(KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING));
+ builder.setSipServiceRouteHeader(getString(KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING));
+ builder.setSipPathHeader(getString(KEY_SIP_CONFIG_PATH_HEADER_STRING));
+ builder.setSipUserAgentHeader(getString(KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING));
+ builder.setSipContactUserParameter(getString(KEY_SIP_CONFIG_URI_USER_PART_STRING));
+ builder.setSipPaniHeader(getString(KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING));
+ builder.setSipPlaniHeader(
+ getString(KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING));
+ builder.setSipCniHeader(getString(KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING));
+ builder.setSipAssociatedUriHeader(getString(KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING));
+ if (getBoolean(KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, false)) {
+ builder.setPublicGruuUri(Uri.parse(getString(KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING)));
+ }
+ if (getBoolean(KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL, false)) {
+ builder.setIpSecConfiguration(new SipDelegateConfiguration.IpSecConfiguration(
+ getInt(KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT, -1),
+ getInt(KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT, -1),
+ getInt(KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT, -1),
+ getInt(KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT, -1),
+ getInt(KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT, -1),
+ getInt(KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT, -1),
+ getString(KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING))
+ );
+ }
+ if (getBoolean(KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL, false)) {
+ builder.setNatSocketAddress(getSocketAddr(
+ getString(KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING),
+ getInt(KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT, -1)));
+ }
+ return builder.build();
+ }
+
+ private InetSocketAddress getSocketAddr(String ipAddr, int port) {
+ return new InetSocketAddress(InetAddresses.parseNumericAddress(ipAddr), port);
+ }
}
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 77fe75a..ca01d0f 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -126,12 +126,12 @@
public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9;
/**
- * The outgoing SIP message has not been sent because the {@link SipDelegateImsConfiguration}
+ * The outgoing SIP message has not been sent because the {@link SipDelegateConfiguration}
* version associated with the outgoing {@link SipMessage} is now stale and has failed
* validation checks.
* <p>
* The @link SipMessage} should be recreated using the newest
- * {@link SipDelegateImsConfiguration} and sent again.
+ * {@link SipDelegateConfiguration} and sent again.
*/
public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10;
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl
index ddfcb99..855000b 100644
--- a/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateConnectionStateCallback.aidl
@@ -18,6 +18,7 @@
import android.telephony.ims.DelegateRegistrationState;
import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
import android.telephony.ims.SipDelegateImsConfiguration;
import android.telephony.ims.aidl.ISipDelegate;
@@ -30,5 +31,6 @@
void onFeatureTagStatusChanged(in DelegateRegistrationState registrationState,
in List<FeatureTagState> deniedFeatureTags);
void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig);
+ void onConfigurationChanged(in SipDelegateConfiguration registeredSipConfig);
void onDestroyed(int reason);
}
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl
index 609ee26..4c3c93d 100644
--- a/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegateStateCallback.aidl
@@ -18,6 +18,7 @@
import android.telephony.ims.DelegateRegistrationState;
import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
import android.telephony.ims.SipDelegateImsConfiguration;
import android.telephony.ims.aidl.ISipDelegate;
@@ -29,5 +30,6 @@
void onCreated(ISipDelegate c, in List<FeatureTagState> deniedFeatureTags);
void onFeatureTagRegistrationChanged(in DelegateRegistrationState registrationState);
void onImsConfigurationChanged(in SipDelegateImsConfiguration registeredSipConfig);
+ void onConfigurationChanged(in SipDelegateConfiguration registeredSipConfig);
void onDestroyed(int reason);
}
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index 6a98d80..c18ab33 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -24,6 +24,7 @@
import android.telephony.ims.DelegateRegistrationState;
import android.telephony.ims.DelegateStateCallback;
import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
import android.telephony.ims.SipDelegateImsConfiguration;
import android.telephony.ims.SipDelegateManager;
import android.telephony.ims.SipMessage;
@@ -166,6 +167,15 @@
}
@Override
+ public void onConfigurationChanged(@NonNull SipDelegateConfiguration config) {
+ try {
+ mStateBinder.onConfigurationChanged(config);
+ } catch (RemoteException e) {
+ // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+ }
+ }
+
+ @Override
public void onDestroyed(int reasonCode) {
mDelegate = null;
try {
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
index 0abb495..47ddcb9 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -21,6 +21,7 @@
import android.os.RemoteException;
import android.telephony.ims.DelegateRegistrationState;
import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
import android.telephony.ims.SipDelegateConnection;
import android.telephony.ims.SipDelegateImsConfiguration;
import android.telephony.ims.SipDelegateManager;
@@ -90,6 +91,17 @@
}
@Override
+ public void onConfigurationChanged(SipDelegateConfiguration registeredSipConfig) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mStateCallback.onConfigurationChanged(registeredSipConfig));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void onDestroyed(int reason) {
invalidateSipDelegateBinder();
final long token = Binder.clearCallingIdentity();
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index b384e50..ddd6fbe 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -120,8 +120,8 @@
@Nullable ICapabilityExchangeEventListener listener) throws RemoteException {
CapabilityExchangeEventListener listenerWrapper =
new CapabilityExchangeAidlWrapper(listener);
- executeMethodAsync(() -> mReference.setCapabilityExchangeEventListener(
- mExecutor, listenerWrapper), "setCapabilityExchangeEventListener");
+ executeMethodAsync(() -> mReference.setCapabilityExchangeEventListener(listenerWrapper),
+ "setCapabilityExchangeEventListener");
}
@Override
@@ -385,30 +385,6 @@
* operation and the RcsFeature sets the status of the capability to true using
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
*
- * @param executor The executor for the framework to use when request RCS resquests to this
- * service.
- * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
- * event to the framework.
- * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
- * exchange if it is supported by the device.
- * @hide
- */
- public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
- @NonNull Executor executor, @NonNull CapabilityExchangeEventListener listener) {
- // Base Implementation, override to implement functionality
- return new RcsCapabilityExchangeImplBase(executor);
- }
-
- /**
- * Retrieve the implementation of UCE for this {@link RcsFeature}, which can use either
- * presence or OPTIONS for capability exchange.
- *
- * Will only be requested by the framework if capability exchange is configured
- * as capable during a
- * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
- * operation and the RcsFeature sets the status of the capability to true using
- * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
- *
* @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
* event to the framework.
* @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
@@ -461,17 +437,15 @@
/**
* Set the capability exchange listener.
- * @param executor The executor for the framework to use when request RCS requests to this
- * service.
* @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
* event to the framework.
*/
- private void setCapabilityExchangeEventListener(@NonNull Executor executor,
+ private void setCapabilityExchangeEventListener(
@Nullable CapabilityExchangeEventListener listener) {
synchronized (mLock) {
mCapExchangeEventListener = listener;
if (mCapExchangeEventListener != null) {
- initRcsCapabilityExchangeImplBase(executor, mCapExchangeEventListener);
+ initRcsCapabilityExchangeImplBase(mCapExchangeEventListener);
} else {
// Remove the RcsCapabilityExchangeImplBase instance when the capability exchange
// instance has been removed in the framework.
@@ -486,19 +460,17 @@
/**
* Initialize the RcsCapabilityExchangeImplBase instance if the capability exchange instance
* has already been created in the framework.
- * @param executor The executor for the framework to use when request RCS requests to this
- * service.
* @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
* event to the framework.
*/
- private void initRcsCapabilityExchangeImplBase(@NonNull Executor executor,
+ private void initRcsCapabilityExchangeImplBase(
@NonNull CapabilityExchangeEventListener listener) {
synchronized (mLock) {
// Remove the original instance
if (mCapabilityExchangeImpl != null) {
removeCapabilityExchangeImpl(mCapabilityExchangeImpl);
}
- mCapabilityExchangeImpl = createCapabilityExchangeImpl(executor, listener);
+ mCapabilityExchangeImpl = createCapabilityExchangeImpl(listener);
}
}
diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
index 02218ea..c078637 100644
--- a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
+++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
@@ -21,6 +21,7 @@
import android.telephony.ims.DelegateRegistrationState;
import android.telephony.ims.DelegateRequest;
import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
import android.telephony.ims.SipDelegateConnection;
import android.telephony.ims.SipDelegateImsConfiguration;
import android.telephony.ims.SipDelegateManager;
@@ -44,7 +45,7 @@
* <p>
* In order to start sending SIP messages, the SIP configuration parameters will need to be
* received, so the messaging application should make no assumptions about these parameters and wait
- * until {@link #onImsConfigurationChanged(SipDelegateImsConfiguration)} has been called. This is
+ * until {@link #onConfigurationChanged(SipDelegateConfiguration)} has been called. This is
* guaranteed to happen after the first {@link #onFeatureTagStatusChanged} if there is at least one
* feature tag that has been successfully associated with the {@link SipDelegateConnection}. If all
* feature tags were denied, no IMS configuration will be sent.
@@ -135,8 +136,32 @@
* not compleed yet.
*
* @param registeredSipConfig The configuration of the IMS stack registered on the IMS network.
+ * @deprecated Will not be in final API, use
+ * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead}.
*/
- void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration registeredSipConfig);
+ @Deprecated
+ default void onImsConfigurationChanged(
+ @NonNull SipDelegateImsConfiguration registeredSipConfig) {
+ onConfigurationChanged(registeredSipConfig.toNewConfig());
+ }
+
+ /**
+ * IMS configuration of the underlying IMS stack used by this IMS application for construction
+ * of the SIP messages that will be sent over the carrier's network.
+ * <p>
+ * There should never be assumptions made about the configuration of the underling IMS stack and
+ * the IMS application should wait for this indication before sending out any outgoing SIP
+ * messages.
+ * <p>
+ * Configuration may change due to IMS registration changes as well as
+ * other optional events on the carrier network. If IMS stack is already registered at the time
+ * of callback registration, then this method shall be invoked with the current configuration.
+ * Otherwise, there may be a delay in this method being called if initial IMS registration has
+ * not compleed yet.
+ *
+ * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network.
+ */
+ default void onConfigurationChanged(@NonNull SipDelegateConfiguration registeredSipConfig) {}
/**
* The previously created {@link SipDelegateConnection} instance delivered via
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 57616d35..474eb05 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -356,21 +356,6 @@
void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException;
}
- private Executor mBinderExecutor;
-
- /**
- * Create a new RcsCapabilityExchangeImplBase instance.
- *
- * @param executor The executor that remote calls from the framework will be called on.
- * @hide
- */
- public RcsCapabilityExchangeImplBase(@NonNull Executor executor) {
- if (executor == null) {
- throw new IllegalArgumentException("executor must not be null");
- }
- mBinderExecutor = executor;
- }
-
/**
* Create a new RcsCapabilityExchangeImplBase instance.
*/
diff --git a/telephony/java/android/telephony/ims/stub/SipDelegate.java b/telephony/java/android/telephony/ims/stub/SipDelegate.java
index d5198a0..997d00b 100644
--- a/telephony/java/android/telephony/ims/stub/SipDelegate.java
+++ b/telephony/java/android/telephony/ims/stub/SipDelegate.java
@@ -21,8 +21,8 @@
import android.telephony.ims.DelegateMessageCallback;
import android.telephony.ims.DelegateRegistrationState;
import android.telephony.ims.ImsService;
+import android.telephony.ims.SipDelegateConfiguration;
import android.telephony.ims.SipDelegateConnection;
-import android.telephony.ims.SipDelegateImsConfiguration;
import android.telephony.ims.SipDelegateManager;
import android.telephony.ims.SipMessage;
@@ -38,7 +38,7 @@
* modified to include the features managed by these SipDelegates.
* <p>
* This SipDelegate will need to notify the remote application of the registration of these features
- * as well as the associated {@link SipDelegateImsConfiguration} before the application can start
+ * as well as the associated {@link SipDelegateConfiguration} before the application can start
* sending/receiving SIP messages via the transport. See
* {@link android.telephony.ims.DelegateStateCallback} for more information.
* @hide
@@ -55,9 +55,9 @@
* {@link DelegateMessageCallback#onMessageSendFailure(String, int)}.
* @param message The SIP message to be sent over the operator’s network.
* @param configVersion The SipDelegateImsConfiguration version used to construct the
- * SipMessage. See {@link SipDelegateImsConfiguration} for more information. If the
+ * SipMessage. See {@link SipDelegateConfiguration} for more information. If the
* version specified here does not match the most recently constructed
- * {@link SipDelegateImsConfiguration}, this message should fail validation checks and
+ * {@link SipDelegateConfiguration}, this message should fail validation checks and
* {@link DelegateMessageCallback#onMessageSendFailure} should be called with code
* {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION}.
*/
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 65aa9ae..7a1dda3 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -375,6 +375,13 @@
/** @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 7e5911d..3347314 100644
--- a/tests/Camera2Tests/CameraToo/Android.mk
+++ b/tests/Camera2Tests/CameraToo/Android.mk
@@ -17,6 +17,9 @@
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 fe4dc42..dfa64f1 100644
--- a/tests/Camera2Tests/CameraToo/tests/Android.mk
+++ b/tests/Camera2Tests/CameraToo/tests/Android.mk
@@ -17,6 +17,9 @@
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 4e3675f..6003628 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk
@@ -32,6 +32,9 @@
$(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 a900077..c23d593 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
@@ -22,6 +22,9 @@
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 6a0a93e..b82ae65 100644
--- a/tests/CanvasCompare/Android.mk
+++ b/tests/CanvasCompare/Android.mk
@@ -20,6 +20,9 @@
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/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index cdec51d..7bc004f 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 @@
@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 559d953..5703e6c 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 @@
@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/LockTaskTests/Android.mk b/tests/LockTaskTests/Android.mk
index a693eaa..5406ee1 100644
--- a/tests/LockTaskTests/Android.mk
+++ b/tests/LockTaskTests/Android.mk
@@ -5,6 +5,9 @@
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 204a74e..cc0fa1c 100644
--- a/tests/SoundTriggerTests/Android.mk
+++ b/tests/SoundTriggerTests/Android.mk
@@ -31,6 +31,9 @@
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 9b155c9..b6f3471 100644
--- a/tests/backup/Android.mk
+++ b/tests/backup/Android.mk
@@ -47,4 +47,7 @@
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/integration/AndroidManifest.xml b/tests/net/integration/AndroidManifest.xml
index db18500..2e13689 100644
--- a/tests/net/integration/AndroidManifest.xml
+++ b/tests/net/integration/AndroidManifest.xml
@@ -38,6 +38,8 @@
<!-- Reading DeviceConfig flags -->
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
+ <!-- Querying the resources package -->
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<application android:debuggable="true">
<uses-library android:name="android.test.runner"/>
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 19f8843..591e0cc 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -379,7 +379,7 @@
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 fee65f0..ccaa5cf 100644
--- a/tests/net/java/android/net/VpnTransportInfoTest.java
+++ b/tests/net/java/android/net/VpnTransportInfoTest.java
@@ -63,6 +63,6 @@
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 1662082..dcbfb93 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1387,7 +1387,7 @@
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 @@
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 @@
() -> 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 6ad4900..b725b82 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 @@
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 4a1f96d..3da8b46 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 @@
}
@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 6bc2064..27b6068 100644
--- a/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk
@@ -20,10 +20,13 @@
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 4462374..98b7440 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
@@ -20,6 +20,9 @@
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 83e2289..30375728 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk
@@ -20,6 +20,9 @@
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 8ea114f..eeb9e3c 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: {