diff options
229 files changed, 7146 insertions, 1952 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 11e0a6bfb151..e2325b05b5c8 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -135,9 +135,6 @@ package android { field public static final String READ_HOME_APP_SEARCH_DATA = "android.permission.READ_HOME_APP_SEARCH_DATA"; field @Deprecated public static final String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE"; field public static final String READ_LOGS = "android.permission.READ_LOGS"; - field public static final String READ_MEDIA_AUDIO = "android.permission.READ_MEDIA_AUDIO"; - field public static final String READ_MEDIA_IMAGE = "android.permission.READ_MEDIA_IMAGE"; - field public static final String READ_MEDIA_VIDEO = "android.permission.READ_MEDIA_VIDEO"; field public static final String READ_NEARBY_STREAMING_POLICY = "android.permission.READ_NEARBY_STREAMING_POLICY"; field public static final String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS"; field public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE"; @@ -223,8 +220,6 @@ package android { field public static final String NEARBY_DEVICES = "android.permission-group.NEARBY_DEVICES"; field public static final String NOTIFICATIONS = "android.permission-group.NOTIFICATIONS"; field public static final String PHONE = "android.permission-group.PHONE"; - field public static final String READ_MEDIA_AURAL = "android.permission-group.READ_MEDIA_AURAL"; - field public static final String READ_MEDIA_VISUAL = "android.permission-group.READ_MEDIA_VISUAL"; field public static final String SENSORS = "android.permission-group.SENSORS"; field public static final String SMS = "android.permission-group.SMS"; field public static final String STORAGE = "android.permission-group.STORAGE"; @@ -7311,10 +7306,10 @@ package android.app.admin { method @Nullable public java.util.List<java.lang.String> getDelegatePackages(@NonNull android.content.ComponentName, @NonNull String); method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String); method public CharSequence getDeviceOwnerLockScreenInfo(); - method @NonNull public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); - method @NonNull public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); - method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); - method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); + method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); + method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); + method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); + method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName); method @NonNull public String getEnrollmentSpecificId(); method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName); @@ -22728,6 +22723,7 @@ package android.media { method public int describeContents(); method @Nullable public String getClientPackageName(); method public int getConnectionState(); + method @NonNull public java.util.Set<java.lang.String> getDeduplicationIds(); method @Nullable public CharSequence getDescription(); method @Nullable public android.os.Bundle getExtras(); method @NonNull public java.util.List<java.lang.String> getFeatures(); @@ -22761,6 +22757,7 @@ package android.media { method @NonNull public android.media.MediaRoute2Info.Builder clearFeatures(); method @NonNull public android.media.MediaRoute2Info.Builder setClientPackageName(@Nullable String); method @NonNull public android.media.MediaRoute2Info.Builder setConnectionState(int); + method @NonNull public android.media.MediaRoute2Info.Builder setDeduplicationIds(@NonNull java.util.Set<java.lang.String>); method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence); method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle); method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri); @@ -23287,8 +23284,12 @@ package android.media { public final class RouteDiscoveryPreference implements android.os.Parcelable { method public int describeContents(); + method @NonNull public java.util.List<java.lang.String> getAllowedPackages(); + method @NonNull public java.util.List<java.lang.String> getDeduplicationPackageOrder(); method @NonNull public java.util.List<java.lang.String> getPreferredFeatures(); + method @NonNull public java.util.List<java.lang.String> getRequiredFeatures(); method public boolean shouldPerformActiveScan(); + method public boolean shouldRemoveDuplicates(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteDiscoveryPreference> CREATOR; } @@ -23297,7 +23298,10 @@ package android.media { ctor public RouteDiscoveryPreference.Builder(@NonNull java.util.List<java.lang.String>, boolean); ctor public RouteDiscoveryPreference.Builder(@NonNull android.media.RouteDiscoveryPreference); method @NonNull public android.media.RouteDiscoveryPreference build(); + method @NonNull public android.media.RouteDiscoveryPreference.Builder setAllowedPackages(@NonNull java.util.List<java.lang.String>); + method @NonNull public android.media.RouteDiscoveryPreference.Builder setDeduplicationPackageOrder(@NonNull java.util.List<java.lang.String>); method @NonNull public android.media.RouteDiscoveryPreference.Builder setPreferredFeatures(@NonNull java.util.List<java.lang.String>); + method @NonNull public android.media.RouteDiscoveryPreference.Builder setRequiredFeatures(@NonNull java.util.List<java.lang.String>); method @NonNull public android.media.RouteDiscoveryPreference.Builder setShouldPerformActiveScan(boolean); } @@ -31734,9 +31738,14 @@ package android.os { method public void release(); method public void release(int); method public void setReferenceCounted(boolean); + method public void setStateListener(@NonNull java.util.concurrent.Executor, @Nullable android.os.PowerManager.WakeLockStateListener); method public void setWorkSource(android.os.WorkSource); } + public static interface PowerManager.WakeLockStateListener { + method public void onStateChanged(boolean); + } + public class Process { ctor public Process(); method public static final long getElapsedCpuTime(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index d6e48157063c..b3dd7a76a8cf 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -166,6 +166,7 @@ package android { field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING"; field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS"; field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION"; + field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY"; field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE"; field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION"; field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS"; @@ -202,6 +203,7 @@ package android { field public static final String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS"; field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE"; field public static final String MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE = "android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE"; + field public static final String MODIFY_TOUCH_MODE_STATE = "android.permission.MODIFY_TOUCH_MODE_STATE"; field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE"; field public static final String NETWORK_AIRPLANE_MODE = "android.permission.NETWORK_AIRPLANE_MODE"; field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING"; @@ -259,6 +261,7 @@ package android { field public static final String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL"; field public static final String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL"; field public static final String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS"; + field public static final String RECEIVE_BLUETOOTH_MAP = "android.permission.RECEIVE_BLUETOOTH_MAP"; field public static final String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"; field public static final String RECEIVE_DEVICE_CUSTOMIZATION_READY = "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"; field public static final String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST"; @@ -303,6 +306,7 @@ package android { field public static final String SET_POINTER_SPEED = "android.permission.SET_POINTER_SPEED"; field public static final String SET_SCREEN_COMPATIBILITY = "android.permission.SET_SCREEN_COMPATIBILITY"; field public static final String SET_SYSTEM_AUDIO_CAPTION = "android.permission.SET_SYSTEM_AUDIO_CAPTION"; + field public static final String SET_UNRESTRICTED_KEEP_CLEAR_AREAS = "android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS"; field public static final String SET_VOLUME_KEY_LONG_PRESS_LISTENER = "android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER"; field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT"; field public static final String SET_WALLPAPER_DIM_AMOUNT = "android.permission.SET_WALLPAPER_DIM_AMOUNT"; @@ -349,6 +353,7 @@ package android { field public static final String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"; field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE"; field public static final String WRITE_OBB = "android.permission.WRITE_OBB"; + field public static final String WRITE_SMS = "android.permission.WRITE_SMS"; } public static final class Manifest.permission_group { @@ -1058,8 +1063,8 @@ package android.app.admin { method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser(); method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException; method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException; - method @NonNull public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>); - method @NonNull public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>, @NonNull java.lang.Object...); + method @Nullable public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>); + method @Nullable public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>, @NonNull java.lang.Object...); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState(); method public boolean isDeviceManaged(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned(); @@ -1195,6 +1200,7 @@ package android.app.admin { public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable { method public boolean canDeviceOwnerGrantSensorsPermissions(); method public int describeContents(); + method @NonNull public android.os.PersistableBundle getAdminExtras(); method @NonNull public android.content.ComponentName getDeviceAdminComponentName(); method public long getLocalTime(); method @Nullable public java.util.Locale getLocale(); @@ -1208,6 +1214,7 @@ package android.app.admin { public static final class FullyManagedDeviceProvisioningParams.Builder { ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String); method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build(); + method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setAdminExtras(@NonNull android.os.PersistableBundle); method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setCanDeviceOwnerGrantSensorsPermissions(boolean); method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean); method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long); @@ -1218,6 +1225,7 @@ package android.app.admin { public final class ManagedProfileProvisioningParams implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.accounts.Account getAccountToMigrate(); + method @NonNull public android.os.PersistableBundle getAdminExtras(); method @NonNull public String getOwnerName(); method @NonNull public android.content.ComponentName getProfileAdminComponentName(); method @Nullable public String getProfileName(); @@ -1232,6 +1240,7 @@ package android.app.admin { ctor public ManagedProfileProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String); method @NonNull public android.app.admin.ManagedProfileProvisioningParams build(); method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAccountToMigrate(@Nullable android.accounts.Account); + method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAdminExtras(@NonNull android.os.PersistableBundle); method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setKeepingAccountOnMigration(boolean); method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean); method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setOrganizationOwnedProvisioning(boolean); @@ -2683,6 +2692,7 @@ package android.content { method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public android.content.Intent registerReceiverForAllUsers(@Nullable android.content.BroadcastReceiver, @NonNull android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int); method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle); + method public void sendBroadcastMultiplePermissions(@NonNull android.content.Intent, @NonNull String[], @Nullable android.app.BroadcastOptions); method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle); field public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context"; @@ -2786,6 +2796,7 @@ package android.content { field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP"; field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED"; field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED"; + field public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED"; field @RequiresPermission(android.Manifest.permission.START_VIEW_APP_FEATURES) public static final String ACTION_VIEW_APP_FEATURES = "android.intent.action.VIEW_APP_FEATURES"; field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST"; field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS"; @@ -2808,7 +2819,9 @@ package android.content { field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME"; field public static final String EXTRA_SHOWING_ATTRIBUTION = "android.intent.extra.SHOWING_ATTRIBUTION"; field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP"; + field public static final String EXTRA_USER_HANDLE = "android.intent.extra.user_handle"; field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE"; + field public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 16777216; // 0x1000000 field public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 67108864; // 0x4000000 field public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION"; } @@ -5884,6 +5897,7 @@ package android.media { field public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0; // 0x0 field public static final String EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE"; field public static final String EXTRA_VOLUME_STREAM_VALUE = "android.media.EXTRA_VOLUME_STREAM_VALUE"; + field public static final int FLAG_BLUETOOTH_ABS_VOLUME = 64; // 0x40 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int STREAM_ASSISTANT = 11; // 0xb field public static final int STREAM_BLUETOOTH_SCO = 6; // 0x6 field public static final int SUCCESS = 0; // 0x0 @@ -10230,6 +10244,7 @@ package android.provider { method @Deprecated public static boolean checkAndNoteWriteSettingsOperation(@NonNull android.content.Context, int, @NonNull String, boolean); method public static boolean checkAndNoteWriteSettingsOperation(@NonNull android.content.Context, int, @NonNull String, @Nullable String, boolean); field public static final String ACTION_ACCESSIBILITY_DETAILS_SETTINGS = "android.settings.ACCESSIBILITY_DETAILS_SETTINGS"; + field public static final String ACTION_BEDTIME_SETTINGS = "android.settings.BEDTIME_SETTINGS"; field public static final String ACTION_BUGREPORT_HANDLER_SETTINGS = "android.settings.BUGREPORT_HANDLER_SETTINGS"; field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS"; field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS"; @@ -11045,7 +11060,7 @@ package android.service.games { public class GameService extends android.app.Service { ctor public GameService(); - method public final void createGameSession(@IntRange(from=0) int); + method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final void createGameSession(@IntRange(from=0) int); method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent); method public void onConnected(); method public void onDisconnected(); @@ -11059,7 +11074,7 @@ package android.service.games { method public void onCreate(); method public void onDestroy(); method public void onGameTaskFocusChanged(boolean); - method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public final boolean restartGame(); + method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final boolean restartGame(); method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams); method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback); } diff --git a/core/java/Android.bp b/core/java/Android.bp index 8234f03100ca..5649c5f1f7db 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -128,6 +128,7 @@ filegroup { "android/os/IThermalStatusListener.aidl", "android/os/IThermalService.aidl", "android/os/IPowerManager.aidl", + "android/os/IWakeLockCallback.aidl", ], } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 876e4014ba45..3289304a0df4 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1005,6 +1005,11 @@ public final class ActivityThread extends ClientTransactionHandler RemoteCallback finishCallback; } + static final class DumpResourcesData { + public ParcelFileDescriptor fd; + public RemoteCallback finishCallback; + } + static final class UpdateCompatibilityData { String pkg; CompatibilityInfo info; @@ -1316,6 +1321,20 @@ public final class ActivityThread extends ClientTransactionHandler sendMessage(H.SCHEDULE_CRASH, args, typeId); } + @Override + public void dumpResources(ParcelFileDescriptor fd, RemoteCallback callback) { + DumpResourcesData data = new DumpResourcesData(); + try { + data.fd = fd.dup(); + data.finishCallback = callback; + sendMessage(H.DUMP_RESOURCES, data, 0, 0, false /*async*/); + } catch (IOException e) { + Slog.w(TAG, "dumpResources failed", e); + } finally { + IoUtils.closeQuietly(fd); + } + } + public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken, String prefix, String[] args) { DumpComponentInfo data = new DumpComponentInfo(); @@ -2039,6 +2058,7 @@ public final class ActivityThread extends ClientTransactionHandler public static final int UPDATE_UI_TRANSLATION_STATE = 163; public static final int SET_CONTENT_CAPTURE_OPTIONS_CALLBACK = 164; public static final int DUMP_GFXINFO = 165; + public static final int DUMP_RESOURCES = 166; public static final int INSTRUMENT_WITHOUT_RESTART = 170; public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171; @@ -2092,6 +2112,7 @@ public final class ActivityThread extends ClientTransactionHandler case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART"; case FINISH_INSTRUMENTATION_WITHOUT_RESTART: return "FINISH_INSTRUMENTATION_WITHOUT_RESTART"; + case DUMP_RESOURCES: return "DUMP_RESOURCES"; } } return Integer.toString(code); @@ -2207,6 +2228,9 @@ public final class ActivityThread extends ClientTransactionHandler case DUMP_HEAP: handleDumpHeap((DumpHeapData) msg.obj); break; + case DUMP_RESOURCES: + handleDumpResources((DumpResourcesData) msg.obj); + break; case DUMP_ACTIVITY: handleDumpActivity((DumpComponentInfo)msg.obj); break; @@ -4585,6 +4609,23 @@ public final class ActivityThread extends ClientTransactionHandler } } + private void handleDumpResources(DumpResourcesData info) { + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + PrintWriter pw = new FastPrintWriter(new FileOutputStream( + info.fd.getFileDescriptor())); + + Resources.dumpHistory(pw, ""); + pw.flush(); + if (info.finishCallback != null) { + info.finishCallback.sendResult(null); + } + } finally { + IoUtils.closeQuietly(info.fd); + StrictMode.setThreadPolicy(oldPolicy); + } + } + private void handleDumpActivity(DumpComponentInfo info) { final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); try { diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 0d1bc05df67b..fdf37f6633ee 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -2363,11 +2363,11 @@ public class AppOpsManager { Manifest.permission.USE_BIOMETRIC, Manifest.permission.ACTIVITY_RECOGNITION, Manifest.permission.SMS_FINANCIAL_TRANSACTIONS, - Manifest.permission.READ_MEDIA_AUDIO, + null, null, // no permission for OP_WRITE_MEDIA_AUDIO - Manifest.permission.READ_MEDIA_VIDEO, + null, null, // no permission for OP_WRITE_MEDIA_VIDEO - Manifest.permission.READ_MEDIA_IMAGE, + null, null, // no permission for OP_WRITE_MEDIA_IMAGES null, // no permission for OP_LEGACY_STORAGE null, // no permission for OP_ACCESS_ACCESSIBILITY diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index f3315a8dc089..8181a74e8f95 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1997,7 +1997,8 @@ class ContextImpl extends Context { private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, String instanceName, Handler handler, Executor executor, UserHandle user) { - // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser. + // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser and + // ActivityManagerService.LocalService.startAndBindSupplementalProcessService IServiceConnection sd; if (conn == null) { throw new IllegalArgumentException("connection is null"); @@ -2023,10 +2024,10 @@ class ContextImpl extends Context { flags |= BIND_WAIVE_PRIORITY; } service.prepareToLeaveProcess(this); - int res = ActivityManager.getService().bindIsolatedService( - mMainThread.getApplicationThread(), getActivityToken(), service, - service.resolveTypeIfNeeded(getContentResolver()), - sd, flags, instanceName, getOpPackageName(), user.getIdentifier()); + int res = ActivityManager.getService().bindServiceInstance( + mMainThread.getApplicationThread(), getActivityToken(), service, + service.resolveTypeIfNeeded(getContentResolver()), + sd, flags, instanceName, getOpPackageName(), user.getIdentifier()); if (res < 0) { throw new SecurityException( "Not allowed to bind to service " + service); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 82c419affac2..e4ef12c250ab 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -169,7 +169,7 @@ interface IActivityManager { int bindService(in IApplicationThread caller, in IBinder token, in Intent service, in String resolvedType, in IServiceConnection connection, int flags, in String callingPackage, int userId); - int bindIsolatedService(in IApplicationThread caller, in IBinder token, in Intent service, + int bindServiceInstance(in IApplicationThread caller, in IBinder token, in Intent service, in String resolvedType, in IServiceConnection connection, int flags, in String instanceName, in String callingPackage, int userId); void updateServiceGroup(in IServiceConnection connection, int group, int importance); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 1714229486e4..77657d58cc4c 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -113,6 +113,7 @@ oneway interface IApplicationThread { in ParcelFileDescriptor fd, in RemoteCallback finishCallback); void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix, in String[] args); + void dumpResources(in ParcelFileDescriptor fd, in RemoteCallback finishCallback); void clearDnsCache(); void updateHttpProxy(); void setCoreSettings(in Bundle coreSettings); diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 14afd0fcebf8..5d1f4df4359e 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -746,7 +746,7 @@ public class KeyguardManager { if (!hasPermission(Manifest.permission.SET_INITIAL_LOCK)) { throw new SecurityException("Requires SET_INITIAL_LOCK permission."); } - return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); + return true; } private boolean hasPermission(String permission) { @@ -814,6 +814,8 @@ public class KeyguardManager { /** * Set the lockscreen password after validating against its expected complexity level. * + * Below {@link android.os.Build.VERSION_CODES#S_V2}, this API will only work + * when {@link PackageManager.FEATURE_AUTOMOTIVE} is present. * @param lockType - type of lock as specified in {@link LockTypes} * @param password - password to validate; this has the same encoding * as the output of String#getBytes diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 18f9379c4111..3d2c03dc4e24 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -214,6 +214,12 @@ public class TaskInfo { public boolean topActivityInSizeCompat; /** + * Whether the direct top activity is eligible for letterbox education. + * @hide + */ + public boolean topActivityEligibleForLetterboxEducation; + + /** * Whether this task is resizable. Unlike {@link #resizeMode} (which is what the top activity * supports), this is what the system actually uses for resizability based on other policy and * developer options. @@ -398,7 +404,8 @@ public class TaskInfo { /** @hide */ public boolean hasCompatUI() { - return hasCameraCompatControl() || topActivityInSizeCompat; + return hasCameraCompatControl() || topActivityInSizeCompat + || topActivityEligibleForLetterboxEducation; } /** @@ -460,6 +467,8 @@ public class TaskInfo { return displayId == that.displayId && taskId == that.taskId && topActivityInSizeCompat == that.topActivityInSizeCompat + && topActivityEligibleForLetterboxEducation + == that.topActivityEligibleForLetterboxEducation && cameraCompatControlState == that.cameraCompatControlState // Bounds are important if top activity has compat controls. && (!hasCompatUI() || configuration.windowConfiguration.getBounds() @@ -507,6 +516,7 @@ public class TaskInfo { isVisible = source.readBoolean(); isSleeping = source.readBoolean(); topActivityInSizeCompat = source.readBoolean(); + topActivityEligibleForLetterboxEducation = source.readBoolean(); mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR); displayAreaFeatureId = source.readInt(); cameraCompatControlState = source.readInt(); @@ -551,6 +561,7 @@ public class TaskInfo { dest.writeBoolean(isVisible); dest.writeBoolean(isSleeping); dest.writeBoolean(topActivityInSizeCompat); + dest.writeBoolean(topActivityEligibleForLetterboxEducation); dest.writeTypedObject(mTopActivityLocusId, flags); dest.writeInt(displayAreaFeatureId); dest.writeInt(cameraCompatControlState); @@ -585,6 +596,8 @@ public class TaskInfo { + " isVisible=" + isVisible + " isSleeping=" + isSleeping + " topActivityInSizeCompat=" + topActivityInSizeCompat + + " topActivityEligibleForLetterboxEducation= " + + topActivityEligibleForLetterboxEducation + " locusId=" + mTopActivityLocusId + " displayAreaFeatureId=" + displayAreaFeatureId + " cameraCompatControlState=" diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 9ac4030c73a7..a4227a4d3074 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -633,7 +633,7 @@ public class DevicePolicyManager { * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} to signal that an update * to the role holder is required. * - * <p>This result code must be accompanied by {@link #EXTRA_ROLE_HOLDER_STATE}. + * <p>This result code can be accompanied by {@link #EXTRA_ROLE_HOLDER_STATE}. * * @hide */ @@ -641,15 +641,19 @@ public class DevicePolicyManager { public static final int RESULT_UPDATE_ROLE_HOLDER = 2; /** - * A {@link Bundle} extra which describes the state of the role holder at the time when it - * returns {@link #RESULT_UPDATE_ROLE_HOLDER}. + * A {@link PersistableBundle} extra which the role holder can use to describe its own state + * when it returns {@link #RESULT_UPDATE_ROLE_HOLDER}. * - * <p>After the update completes, the role holder's {@link - * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} or {@link + * <p>If {@link #RESULT_UPDATE_ROLE_HOLDER} was accompanied by this extra, after the update + * completes, the role holder's {@link #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} or {@link * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent will be relaunched, * which will contain this extra. It is the role holder's responsibility to restore its * state from this extra. * + * <p>The content of this {@link PersistableBundle} is entirely up to the role holder. It + * should contain anything the role holder needs to restore its original state when it gets + * restarted. + * * @hide */ @SystemApi @@ -14815,6 +14819,11 @@ public class DevicePolicyManager { * The device may not connect to networks that do not meet the minimum security level. * If the current network does not meet the minimum security level set, it will be disconnected. * + * The following shows the Wi-Fi security levels from the lowest to the highest security level: + * {@link #WIFI_SECURITY_OPEN} + * {@link #WIFI_SECURITY_PERSONAL} + * {@link #WIFI_SECURITY_ENTERPRISE_EAP} + * {@link #WIFI_SECURITY_ENTERPRISE_192} * * @param level minimum security level * @throws SecurityException if the caller is not a device owner or a profile owner on @@ -14991,8 +15000,8 @@ public class DevicePolicyManager { * <p>Also returns the drawable from {@code defaultDrawableLoader} if * {@link DevicePolicyResources.Drawables#UNDEFINED} was passed. * - * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a - * {@link NullPointerException} is thrown. + * <p>Calls to this API will not return {@code null} unless no updated drawable was found + * and the call to {@code defaultDrawableLoader} returned {@code null}. * * <p>This API uses the screen density returned from {@link Resources#getConfiguration()}, to * set a different value use @@ -15009,7 +15018,7 @@ public class DevicePolicyManager { * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for * the provided params. */ - @NonNull + @Nullable public Drawable getDrawable( @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId, @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, @@ -15025,8 +15034,8 @@ public class DevicePolicyManager { * {@link #getDrawable(String, String, Callable)} * if an override was set for that specific source. * - * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a - * {@link NullPointerException} is thrown. + * <p>Calls to this API will not return {@code null} unless no updated drawable was found + * and the call to {@code defaultDrawableLoader} returned {@code null}. * * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get * notified when a resource has been updated. @@ -15037,7 +15046,7 @@ public class DevicePolicyManager { * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for * the provided params. */ - @NonNull + @Nullable public Drawable getDrawable( @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId, @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, @@ -15080,8 +15089,8 @@ public class DevicePolicyManager { * Similar to {@link #getDrawable(String, String, Callable)}, but also accepts * {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}. * - * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a - * {@link NullPointerException} is thrown. + * <p>Calls to this API will not return {@code null} unless no updated drawable was found + * and the call to {@code defaultDrawableLoader} returned {@code null}. * * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get * notified when a resource has been updated. @@ -15094,7 +15103,7 @@ public class DevicePolicyManager { * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for * the provided params. */ - @NonNull + @Nullable public Drawable getDrawableForDensity( @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId, @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, @@ -15112,8 +15121,8 @@ public class DevicePolicyManager { * Similar to {@link #getDrawable(String, String, String, Callable)}, but also accepts * {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}. * - * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a - * {@link NullPointerException} is thrown. + * <p>Calls to this API will not return {@code null} unless no updated drawable was found + * and the call to {@code defaultDrawableLoader} returned {@code null}. * * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get * notified when a resource has been updated. @@ -15127,7 +15136,7 @@ public class DevicePolicyManager { * @param defaultDrawableLoader To get the default drawable if no updated drawable was set for * the provided params. */ - @NonNull + @Nullable public Drawable getDrawableForDensity( @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId, @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, @@ -15166,7 +15175,7 @@ public class DevicePolicyManager { /** * For each {@link DevicePolicyStringResource} item in {@code strings}, it updates the string * resource for {@link DevicePolicyStringResource#getStringId()} to the string with ID - * {@code callingPackageResourceId} (see {@link DevicePolicyResources.String}), meaning any + * {@code callingPackageResourceId} (see {@link DevicePolicyResources.Strings}), meaning any * system UI surface calling {@link #getString} with {@code stringId} will get * the new resource after this API is called. * @@ -15202,7 +15211,7 @@ public class DevicePolicyManager { /** * Removes the updated strings for the list of {@code stringIds} (see - * {@link DevicePolicyResources.String}) that was previously set by calling {@link #setStrings}, + * {@link DevicePolicyResources.Strings}) that was previously set by calling {@link #setStrings}, * meaning any subsequent calls to {@link #getString} for the provided IDs will * return the default string from {@code defaultStringLoader}. * @@ -15227,14 +15236,14 @@ public class DevicePolicyManager { /** * Returns the appropriate updated string for the {@code stringId} (see - * {@link DevicePolicyResources.String}) if one was set using + * {@link DevicePolicyResources.Strings}) if one was set using * {@link #setStrings}, otherwise returns the string from {@code defaultStringLoader}. * * <p>Also returns the string from {@code defaultStringLoader} if - * {@link DevicePolicyResources.String#INVALID_ID} was passed. + * {@link DevicePolicyResources.Strings#UNDEFINED} was passed. * - * <p>{@code defaultStringLoader} must return a non {@code null} {@link String}, otherwise a - * {@link NullPointerException} is thrown. + * <p>Calls to this API will not return {@code null} unless no updated drawable was found + * and the call to {@code defaultStringLoader} returned {@code null}. * * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get * notified when a resource has been updated. @@ -15249,7 +15258,7 @@ public class DevicePolicyManager { * @hide */ @SystemApi - @NonNull + @Nullable public String getString( @NonNull @DevicePolicyResources.UpdatableStringId String stringId, @NonNull Callable<String> defaultStringLoader) { @@ -15284,8 +15293,8 @@ public class DevicePolicyManager { * {@link java.util.Formatter} and {@link java.lang.String#format}, (see * {@link Resources#getString(int, Object...)}). * - * <p>{@code defaultStringLoader} must return a non {@code null} {@link String}, otherwise a - * {@link NullPointerException} is thrown. + * <p>Calls to this API will not return {@code null} unless no updated drawable was found + * and the call to {@code defaultStringLoader} returned {@code null}. * * @param stringId The IDs to get the updated resource for. * @param defaultStringLoader To get the default string if no updated string was set for @@ -15295,7 +15304,7 @@ public class DevicePolicyManager { * @hide */ @SystemApi - @NonNull + @Nullable @SuppressLint("SamShouldBeLast") public String getString( @NonNull @DevicePolicyResources.UpdatableStringId String stringId, diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java index 2ad201093d58..cf349b04425a 100644 --- a/core/java/android/app/admin/DevicePolicyResources.java +++ b/core/java/android/app/admin/DevicePolicyResources.java @@ -93,6 +93,167 @@ import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.BLOC import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_PERSONAL_MESSAGE; import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_WORK_MESSAGE; import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.WORK_PROFILE_PAUSED_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_PERSONAL; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_WORK; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_WORK_ACCOUNT_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCOUNTS_SEARCH_KEYWORDS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVATE_DEVICE_ADMIN_APP; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVATE_DEVICE_ADMIN_APP_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVATE_THIS_DEVICE_ADMIN_APP; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ACTIVE_DEVICE_ADMIN_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTIONS_APPS_COUNT; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTIONS_APPS_COUNT_MINIMUM; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_ACCESS_CAMERA; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_ACCESS_LOCATION; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_ACCESS_MICROPHONE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_APPS_COUNT_ESTIMATED; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_APPS_INSTALLED; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_NONE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_SET_CURRENT_INPUT_METHOD; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_SET_DEFAULT_APPS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_SET_HTTP_PROXY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_ACTION_SET_INPUT_METHOD_NAME; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_LOCK_DEVICE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_APPS_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_BUG_REPORT_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_SECURITY_LOGS_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_NETWORK_LOGS_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_USAGE_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_WORK_DATA_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_WIPE_DEVICE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ALWAYS_ON_VPN_DEVICE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ALWAYS_ON_VPN_PERSONAL_PROFILE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ALWAYS_ON_VPN_WORK_PROFILE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.APP_CAN_ACCESS_PERSONAL_DATA; +import static android.app.admin.DevicePolicyResources.Strings.Settings.APP_CAN_ACCESS_PERSONAL_PERMISSIONS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CA_CERTS_DEVICE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CA_CERTS_PERSONAL_PROFILE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CA_CERTS_WORK_PROFILE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PASSWORD_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PATTERN_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PIN_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_APPS_SEARCH_KEYWORDS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_WORK_AND_PERSONAL_APPS_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECT_APPS_DIALOG_SUMMARY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECT_APPS_DIALOG_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CONTACT_YOUR_IT_ADMIN; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CONTROLLED_BY_ADMIN_SUMMARY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CROSS_PROFILE_CALENDAR_SUMMARY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CROSS_PROFILE_CALENDAR_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_ADMIN_POLICIES_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_ADMIN_SETTINGS_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_MANAGED_WITHOUT_NAME; +import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_MANAGED_WITH_NAME; +import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.DISABLED_BY_IT_ADMIN_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ENTERPRISE_PRIVACY_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ERROR_MOVE_DEVICE_ADMIN; +import static android.app.admin.DevicePolicyResources.Strings.Settings.FACE_SETTINGS_FOR_WORK_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.FACE_UNLOCK_DISABLED; +import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_FOR_WORK; +import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED; +import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED_EXPLANATION; +import static android.app.admin.DevicePolicyResources.Strings.Settings.FORGOT_PASSWORD_TEXT; +import static android.app.admin.DevicePolicyResources.Strings.Settings.FORGOT_PASSWORD_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.HOW_TO_DISCONNECT_APPS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT; +import static android.app.admin.DevicePolicyResources.Strings.Settings.INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT; +import static android.app.admin.DevicePolicyResources.Strings.Settings.IT_ADMIN_POLICY_DISABLING_INFO_URL; +import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_BY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_DEVICE_INFO; +import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_DEVICE_INFO_SUMMARY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME; +import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_PROFILE_SETTINGS_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGE_DEVICE_ADMIN_APPS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.NEW_DEVICE_ADMIN_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED; +import static android.app.admin.DevicePolicyResources.Strings.Settings.NO_DEVICE_ADMINS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.NUMBER_OF_DEVICE_ADMINS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.NUMBER_OF_DEVICE_ADMINS_NONE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.ONLY_CONNECT_TRUSTED_APPS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.OTHER_OPTIONS_DISABLED_BY_ADMIN; +import static android.app.admin.DevicePolicyResources.Strings.Settings.PASSWORD_RECENTLY_USED; +import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_PROFILE_APP_SUBTEXT; +import static android.app.admin.DevicePolicyResources.Strings.Settings.PIN_RECENTLY_USED; +import static android.app.admin.DevicePolicyResources.Strings.Settings.REENTER_WORK_PROFILE_PASSWORD_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.REENTER_WORK_PROFILE_PIN_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION; +import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_AND_UNINSTALL_DEVICE_ADMIN; +import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_DEVICE_ADMIN; +import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_WORK_PROFILE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.SELECT_DEVICE_ADMIN_APPS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_PROFILE_OWNER_DIALOG_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_PROFILE_OWNER_POSTSETUP_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_PROFILE_OWNER_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_WORK_PROFILE_PASSWORD_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_WORK_PROFILE_PATTERN_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.SET_WORK_PROFILE_PIN_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARE_REMOTE_BUGREPORT_DIALOG_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT; +import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT; +import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARING_REMOTE_BUGREPORT_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.UNINSTALL_DEVICE_ADMIN; +import static android.app.admin.DevicePolicyResources.Strings.Settings.USER_ADMIN_POLICIES_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_ADMIN_POLICIES_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_ALARM_RINGTONE_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_APP_SUBTEXT; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PATTERN; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_PIN; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_REMOVE_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONFIRM_REMOVE_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONTACT_SEARCH_SUMMARY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_CONTACT_SEARCH_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_KEYBOARDS_AND_TOOLS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCATION_SWITCH_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCKED_NOTIFICATION_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCK_ATTEMPTS_FAILED; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_MANAGED_BY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOT_AVAILABLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_OFF_CONDITION_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PASSWORD_REQUIRED; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PRIVACY_POLICY_INFO; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_RINGTONE_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SECURITY_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING_OFF_SUMMARY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SETTING_ON_SUMMARY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFY_LOCKS_DETAIL; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFY_LOCKS_SUMMARY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_UNIFY_LOCKS_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USER_LABEL; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Settings.YOUR_ACCESS_TO_THIS_DEVICE_TITLE; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT; import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT; @@ -159,7 +320,8 @@ public final class DevicePolicyResources { Drawables.WORK_PROFILE_OFF_ICON, Drawables.WORK_PROFILE_USER_ICON }) - public @interface UpdatableDrawableId {} + public @interface UpdatableDrawableId { + } /** * Identifiers to specify the desired style for the updatable device management system @@ -174,7 +336,8 @@ public final class DevicePolicyResources { Drawables.Style.OUTLINE, Drawables.Style.DEFAULT }) - public @interface UpdatableDrawableStyle {} + public @interface UpdatableDrawableStyle { + } /** * Identifiers to specify the location if the updatable device management system resource. @@ -191,7 +354,8 @@ public final class DevicePolicyResources { Drawables.Source.QUICK_SETTINGS, Drawables.Source.STATUS_BAR }) - public @interface UpdatableDrawableSource {} + public @interface UpdatableDrawableSource { + } /** * Resource identifiers used to update device management-related string resources. @@ -231,7 +395,7 @@ public final class DevicePolicyResources { PERSONAL_APP_SUSPENSION_TITLE, PERSONAL_APP_SUSPENSION_MESSAGE, PERSONAL_APP_SUSPENSION_SOON_MESSAGE, PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE, PRINTING_DISABLED_NAMED_ADMIN, LOCATION_CHANGED_TITLE, LOCATION_CHANGED_MESSAGE, - NETWORK_LOGGING_TITLE, NETWORK_LOGGING_MESSAGE, + NETWORK_LOGGING_TITLE, NETWORK_LOGGING_MESSAGE, NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION, NOTIFICATION_CHANNEL_DEVICE_ADMIN, SWITCH_TO_WORK_LABEL, SWITCH_TO_PERSONAL_LABEL, FORWARD_INTENT_TO_WORK, FORWARD_INTENT_TO_PERSONAL, RESOLVER_WORK_PROFILE_NOT_SUPPORTED, RESOLVER_PERSONAL_TAB, @@ -257,7 +421,86 @@ public final class DevicePolicyResources { SWITCH_TO_WORK_MESSAGE, SWITCH_TO_PERSONAL_MESSAGE, BLOCKED_BY_ADMIN_TITLE, BLOCKED_FROM_PERSONAL_MESSAGE, BLOCKED_FROM_PERSONAL_MESSAGE, BLOCKED_FROM_WORK_MESSAGE, Strings.MediaProvider.WORK_PROFILE_PAUSED_TITLE, - WORK_PROFILE_PAUSED_MESSAGE + WORK_PROFILE_PAUSED_MESSAGE, + + // Settings Strings + FACE_SETTINGS_FOR_WORK_TITLE, WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE, + WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK, WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE, + WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE, + WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE, + WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE, + WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE, WORK_PROFILE_LOCK_ATTEMPTS_FAILED, + ACCESSIBILITY_CATEGORY_WORK, ACCESSIBILITY_CATEGORY_PERSONAL, + ACCESSIBILITY_WORK_ACCOUNT_TITLE, ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE, + WORK_PROFILE_LOCATION_SWITCH_TITLE, SET_WORK_PROFILE_PASSWORD_HEADER, + SET_WORK_PROFILE_PIN_HEADER, SET_WORK_PROFILE_PATTERN_HEADER, + CONFIRM_WORK_PROFILE_PASSWORD_HEADER, CONFIRM_WORK_PROFILE_PIN_HEADER, + CONFIRM_WORK_PROFILE_PATTERN_HEADER, REENTER_WORK_PROFILE_PASSWORD_HEADER, + REENTER_WORK_PROFILE_PIN_HEADER, WORK_PROFILE_CONFIRM_PATTERN, WORK_PROFILE_CONFIRM_PIN, + WORK_PROFILE_PASSWORD_REQUIRED, WORK_PROFILE_SECURITY_TITLE, + WORK_PROFILE_UNIFY_LOCKS_TITLE, WORK_PROFILE_UNIFY_LOCKS_SUMMARY, + WORK_PROFILE_UNIFY_LOCKS_DETAIL, WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT, + WORK_PROFILE_KEYBOARDS_AND_TOOLS, WORK_PROFILE_NOT_AVAILABLE, WORK_PROFILE_SETTING, + WORK_PROFILE_SETTING_ON_SUMMARY, WORK_PROFILE_SETTING_OFF_SUMMARY, REMOVE_WORK_PROFILE, + DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING, + WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING, WORK_PROFILE_CONFIRM_REMOVE_TITLE, + WORK_PROFILE_CONFIRM_REMOVE_MESSAGE, WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS, + WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER, WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE, + WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY, WORK_PROFILE_RINGTONE_TITLE, + WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE, WORK_PROFILE_ALARM_RINGTONE_TITLE, + WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY, + ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE, + ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE, + WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER, WORK_PROFILE_LOCKED_NOTIFICATION_TITLE, + WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE, + WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY, + WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED, CONNECTED_WORK_AND_PERSONAL_APPS_TITLE, + CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA, ONLY_CONNECT_TRUSTED_APPS, + HOW_TO_DISCONNECT_APPS, CONNECT_APPS_DIALOG_TITLE, CONNECT_APPS_DIALOG_SUMMARY, + APP_CAN_ACCESS_PERSONAL_DATA, APP_CAN_ACCESS_PERSONAL_PERMISSIONS, + INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT, + INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT, WORK_PROFILE_MANAGED_BY, MANAGED_BY, + WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING, DISABLED_BY_IT_ADMIN_TITLE, + CONTACT_YOUR_IT_ADMIN, WORK_PROFILE_ADMIN_POLICIES_WARNING, USER_ADMIN_POLICIES_WARNING, + DEVICE_ADMIN_POLICIES_WARNING, WORK_PROFILE_OFF_CONDITION_TITLE, + MANAGED_PROFILE_SETTINGS_TITLE, WORK_PROFILE_CONTACT_SEARCH_TITLE, + WORK_PROFILE_CONTACT_SEARCH_SUMMARY, CROSS_PROFILE_CALENDAR_TITLE, + CROSS_PROFILE_CALENDAR_SUMMARY, ALWAYS_ON_VPN_PERSONAL_PROFILE, ALWAYS_ON_VPN_DEVICE, + ALWAYS_ON_VPN_WORK_PROFILE, CA_CERTS_PERSONAL_PROFILE, CA_CERTS_WORK_PROFILE, + CA_CERTS_DEVICE, ADMIN_CAN_LOCK_DEVICE, ADMIN_CAN_WIPE_DEVICE, + ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE, + ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE, DEVICE_MANAGED_WITHOUT_NAME, + DEVICE_MANAGED_WITH_NAME, WORK_PROFILE_APP_SUBTEXT, PERSONAL_PROFILE_APP_SUBTEXT, + FINGERPRINT_FOR_WORK, FACE_UNLOCK_DISABLED, FINGERPRINT_UNLOCK_DISABLED, + FINGERPRINT_UNLOCK_DISABLED_EXPLANATION, PIN_RECENTLY_USED, PASSWORD_RECENTLY_USED, + MANAGE_DEVICE_ADMIN_APPS, NUMBER_OF_DEVICE_ADMINS_NONE, NUMBER_OF_DEVICE_ADMINS, + FORGOT_PASSWORD_TITLE, FORGOT_PASSWORD_TEXT, ERROR_MOVE_DEVICE_ADMIN, + DEVICE_ADMIN_SETTINGS_TITLE, REMOVE_DEVICE_ADMIN, UNINSTALL_DEVICE_ADMIN, + REMOVE_AND_UNINSTALL_DEVICE_ADMIN, SELECT_DEVICE_ADMIN_APPS, NO_DEVICE_ADMINS, + ACTIVATE_DEVICE_ADMIN_APP, ACTIVATE_THIS_DEVICE_ADMIN_APP, + ACTIVATE_DEVICE_ADMIN_APP_TITLE, NEW_DEVICE_ADMIN_WARNING, + NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED, ACTIVE_DEVICE_ADMIN_WARNING, + SET_PROFILE_OWNER_TITLE, SET_PROFILE_OWNER_DIALOG_TITLE, + SET_PROFILE_OWNER_POSTSETUP_WARNING, OTHER_OPTIONS_DISABLED_BY_ADMIN, + REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION, IT_ADMIN_POLICY_DISABLING_INFO_URL, + SHARE_REMOTE_BUGREPORT_DIALOG_TITLE, SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT, + SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT, SHARING_REMOTE_BUGREPORT_MESSAGE, + MANAGED_DEVICE_INFO, MANAGED_DEVICE_INFO_SUMMARY, MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME, + ENTERPRISE_PRIVACY_HEADER, INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE, + CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE, YOUR_ACCESS_TO_THIS_DEVICE_TITLE, + ADMIN_CAN_SEE_WORK_DATA_WARNING, ADMIN_CAN_SEE_APPS_WARNING, + ADMIN_CAN_SEE_USAGE_WARNING, ADMIN_CAN_SEE_NETWORK_LOGS_WARNING, + ADMIN_CAN_SEE_BUG_REPORT_WARNING, ADMIN_CAN_SEE_SECURITY_LOGS_WARNING, + ADMIN_ACTION_NONE, ADMIN_ACTION_APPS_INSTALLED, ADMIN_ACTION_APPS_COUNT_ESTIMATED, + ADMIN_ACTIONS_APPS_COUNT_MINIMUM, ADMIN_ACTION_ACCESS_LOCATION, + ADMIN_ACTION_ACCESS_MICROPHONE, ADMIN_ACTION_ACCESS_CAMERA, + ADMIN_ACTION_SET_DEFAULT_APPS, ADMIN_ACTIONS_APPS_COUNT, + ADMIN_ACTION_SET_CURRENT_INPUT_METHOD, ADMIN_ACTION_SET_INPUT_METHOD_NAME, + ADMIN_ACTION_SET_HTTP_PROXY, WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY, + WORK_PROFILE_PRIVACY_POLICY_INFO, CONNECTED_APPS_SEARCH_KEYWORDS, + WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS, ACCOUNTS_SEARCH_KEYWORDS, + CONTROLLED_BY_ADMIN_SUMMARY, WORK_PROFILE_USER_LABEL, WORK_CATEGORY_HEADER, + PERSONAL_CATEGORY_HEADER }) public @interface UpdatableStringId { } @@ -432,7 +675,8 @@ public final class DevicePolicyResources { @SystemApi public static final class Strings { - private Strings() {} + private Strings() { + } /** * An ID for any string that can't be updated. @@ -456,13 +700,1209 @@ public final class DevicePolicyResources { /** * Class containing the identifiers used to update device management-related system strings + * in the Settings package + * + * @hide + */ + public static final class Settings { + + private Settings() { + } + + private static final String PREFIX = "Settings."; + + /** + * Title shown for menu item that launches face settings or enrollment, for work profile + */ + public static final String FACE_SETTINGS_FOR_WORK_TITLE = + PREFIX + "FACE_SETTINGS_FOR_WORK_TITLE"; + + /** + * Warning when removing the last fingerprint on a work profile + */ + public static final String WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE = + PREFIX + "WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE"; + + /** + * Text letting the user know that their IT admin can't reset their screen lock if they + * forget it, and they can choose to set another lock that would be specifically for + * their work apps + */ + public static final String WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK = + PREFIX + "WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK"; + + /** + * Message shown in screen lock picker for setting up a work profile screen lock + */ + public static final String WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE = + PREFIX + "WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE"; + + /** + * Title for PreferenceScreen to launch picker for security method for the managed + * profile when there is none + */ + public static final String WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE = + PREFIX + "WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE"; + + /** + * Content of the dialog shown when the user only has one attempt left to provide the + * work lock pattern before the work profile is removed + */ + public static final String WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE = + PREFIX + "WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE"; + + /** + * Content of the dialog shown when the user only has one attempt left to provide the + * work lock pattern before the work profile is removed + */ + public static final String WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE = + PREFIX + "WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE"; + + /** + * Content of the dialog shown when the user only has one attempt left to provide the + * work lock pattern before the work profile is removed + */ + public static final String WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE = + PREFIX + "WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE"; + + /** + * Content of the dialog shown when the user has failed to provide the device lock too + * many times and the device is wiped + */ + public static final String WORK_PROFILE_LOCK_ATTEMPTS_FAILED = + PREFIX + "WORK_PROFILE_LOCK_ATTEMPTS_FAILED"; + + /** + * Content description for work profile accounts group + */ + public static final String ACCESSIBILITY_CATEGORY_WORK = + PREFIX + "ACCESSIBILITY_CATEGORY_WORK"; + + /** + * Content description for personal profile accounts group + */ + public static final String ACCESSIBILITY_CATEGORY_PERSONAL = + PREFIX + "ACCESSIBILITY_CATEGORY_PERSONAL"; + + /** + * Content description for work profile details page title + */ + public static final String ACCESSIBILITY_WORK_ACCOUNT_TITLE = + PREFIX + "ACCESSIBILITY_WORK_ACCOUNT_TITLE"; + + /** + * Content description for personal profile details page title + */ + public static final String ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE = + PREFIX + "ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE"; + + /** + * Title for work profile location switch + */ + public static final String WORK_PROFILE_LOCATION_SWITCH_TITLE = + PREFIX + "WORK_PROFILE_LOCATION_SWITCH_TITLE"; + + /** + * Header when setting work profile password + */ + public static final String SET_WORK_PROFILE_PASSWORD_HEADER = + PREFIX + "SET_WORK_PROFILE_PASSWORD_HEADER"; + + /** + * Header when setting work profile PIN + */ + public static final String SET_WORK_PROFILE_PIN_HEADER = + PREFIX + "SET_WORK_PROFILE_PIN_HEADER"; + + /** + * Header when setting work profile pattern + */ + public static final String SET_WORK_PROFILE_PATTERN_HEADER = + PREFIX + "SET_WORK_PROFILE_PATTERN_HEADER"; + + /** + * Header when confirming work profile password + */ + public static final String CONFIRM_WORK_PROFILE_PASSWORD_HEADER = + PREFIX + "CONFIRM_WORK_PROFILE_PASSWORD_HEADER"; + + /** + * Header when confirming work profile pin + */ + public static final String CONFIRM_WORK_PROFILE_PIN_HEADER = + PREFIX + "CONFIRM_WORK_PROFILE_PIN_HEADER"; + + /** + * Header when confirming work profile pattern + */ + public static final String CONFIRM_WORK_PROFILE_PATTERN_HEADER = + PREFIX + "CONFIRM_WORK_PROFILE_PATTERN_HEADER"; + + /** + * Header when re-entering work profile password + */ + public static final String REENTER_WORK_PROFILE_PASSWORD_HEADER = + PREFIX + "REENTER_WORK_PROFILE_PASSWORD_HEADER"; + + /** + * Header when re-entering work profile pin + */ + public static final String REENTER_WORK_PROFILE_PIN_HEADER = + PREFIX + "REENTER_WORK_PROFILE_PIN_HEADER"; + + /** + * Message to be used to explain the users that they need to enter their work pattern to + * continue a particular operation + */ + public static final String WORK_PROFILE_CONFIRM_PATTERN = + PREFIX + "WORK_PROFILE_CONFIRM_PATTERN"; + + /** + * Message to be used to explain the users that they need to enter their work pin to + * continue a particular operation + */ + public static final String WORK_PROFILE_CONFIRM_PIN = + PREFIX + "WORK_PROFILE_CONFIRM_PIN"; + + /** + * Message to be used to explain the users that they need to enter their work password + * to + * continue a particular operation + */ + public static final String WORK_PROFILE_CONFIRM_PASSWORD = + PREFIX + "WORK_PROFILE_CONFIRM_PASSWORD"; + + /** + * This string shows = PREFIX + "shows"; up on a screen where a user can enter a pattern + * that lets them access + * their work profile. This is an extra security measure that's required for them to + * continue + */ + public static final String WORK_PROFILE_PATTERN_REQUIRED = + PREFIX + "WORK_PROFILE_PATTERN_REQUIRED"; + + /** + * This string shows = PREFIX + "shows"; up on a screen where a user can enter a pin + * that lets them access + * their work profile. This is an extra security measure that's required for them to + * continue + */ + public static final String WORK_PROFILE_PIN_REQUIRED = + PREFIX + "WORK_PROFILE_PIN_REQUIRED"; + + /** + * This string shows = PREFIX + "shows"; up on a screen where a user can enter a + * password that lets them access + * their work profile. This is an extra security measure that's required for them to + * continue + */ + public static final String WORK_PROFILE_PASSWORD_REQUIRED = + PREFIX + "WORK_PROFILE_PASSWORD_REQUIRED"; + + /** + * Header for Work Profile security settings + */ + public static final String WORK_PROFILE_SECURITY_TITLE = + PREFIX + "WORK_PROFILE_SECURITY_TITLE"; + + /** + * Header for Work Profile unify locks settings + */ + public static final String WORK_PROFILE_UNIFY_LOCKS_TITLE = + PREFIX + "WORK_PROFILE_UNIFY_LOCKS_TITLE"; + + /** + * Setting option explanation to unify work and personal locks + */ + public static final String WORK_PROFILE_UNIFY_LOCKS_SUMMARY = + PREFIX + "WORK_PROFILE_UNIFY_LOCKS_SUMMARY"; + + /** + * Further explanation when the user wants to unify work and personal locks + */ + public static final String WORK_PROFILE_UNIFY_LOCKS_DETAIL = + PREFIX + "WORK_PROFILE_UNIFY_LOCKS_DETAIL"; + + /** + * Ask if the user wants to create a new lock for personal and work as the current work + * lock is not enough for the device + */ + public static final String WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT = + PREFIX + "WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT"; + + /** + * Title of 'Work profile keyboards & tools' preference category + */ + public static final String WORK_PROFILE_KEYBOARDS_AND_TOOLS = + PREFIX + "WORK_PROFILE_KEYBOARDS_AND_TOOLS"; + + /** + * Label for state when work profile is not available + */ + public static final String WORK_PROFILE_NOT_AVAILABLE = + PREFIX + "WORK_PROFILE_NOT_AVAILABLE"; + + /** + * Label for work profile setting (to allow turning work profile on and off) + */ + public static final String WORK_PROFILE_SETTING = PREFIX + "WORK_PROFILE_SETTING"; + + /** + * Description of the work profile setting when the work profile is on + */ + public static final String WORK_PROFILE_SETTING_ON_SUMMARY = + PREFIX + "WORK_PROFILE_SETTING_ON_SUMMARY"; + + /** + * Description of the work profile setting when the work profile is off + */ + public static final String WORK_PROFILE_SETTING_OFF_SUMMARY = + PREFIX + "WORK_PROFILE_SETTING_OFF_SUMMARY"; + + /** + * Button text to remove work profile + */ + public static final String REMOVE_WORK_PROFILE = PREFIX + "REMOVE_WORK_PROFILE"; + + /** + * Text of message to show to device owner user whose administrator has installed a SSL + * CA Cert + */ + public static final String DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING = + PREFIX + "DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING"; + + /** + * Text of message to show to work profile users whose administrator has installed a SSL + * CA Cert + */ + public static final String WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING = + PREFIX + "WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING"; + + /** + * Work profile removal confirmation title + */ + public static final String WORK_PROFILE_CONFIRM_REMOVE_TITLE = + PREFIX + "WORK_PROFILE_CONFIRM_REMOVE_TITLE"; + + /** + * Work profile removal confirmation message + */ + public static final String WORK_PROFILE_CONFIRM_REMOVE_MESSAGE = + PREFIX + "WORK_PROFILE_CONFIRM_REMOVE_MESSAGE"; + + /** + * Toast shown when an app in the work profile attempts to open notification settings + * and apps in the work profile cannot access notification settings + */ + public static final String WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS = + PREFIX + "WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS"; + + /** + * Work sound settings section header + */ + public static final String WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER = + PREFIX + "WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER"; + + /** + * Title for the switch that enables syncing of personal ringtones to work profile + */ + public static final String WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE = + PREFIX + "WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE"; + + /** + * Summary for the switch that enables syncing of personal ringtones to work profile + */ + public static final String WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY = + PREFIX + "WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY"; + + /** + * Title for the option defining the work profile phone ringtone + */ + public static final String WORK_PROFILE_RINGTONE_TITLE = + PREFIX + "WORK_PROFILE_RINGTONE_TITLE"; + + /** + * Title for the option defining the default work profile notification ringtone + */ + public static final String WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE = + PREFIX + "WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE"; + + /** + * Title for the option defining the default work alarm ringtone + */ + public static final String WORK_PROFILE_ALARM_RINGTONE_TITLE = + PREFIX + "WORK_PROFILE_ALARM_RINGTONE_TITLE"; + + /** + * Summary for sounds when sync with personal sounds is active + */ + public static final String WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY = + PREFIX + "WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY"; + + /** + * Title for dialog shown when enabling sync with personal sounds + */ + public static final String + ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE = + PREFIX + "ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE"; + + /** + * Message for dialog shown when using the same sounds for work events as for personal + * events + */ + public static final String + ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE = + PREFIX + "ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE"; + + /** + * Work profile notifications section header + */ + public static final String WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER = + PREFIX + "WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER"; + + /** + * Title for the option controlling notifications for work profile + */ + public static final String WORK_PROFILE_LOCKED_NOTIFICATION_TITLE = + PREFIX + "WORK_PROFILE_LOCKED_NOTIFICATION_TITLE"; + + /** + * Title for redacting sensitive content on lockscreen for work profiles + */ + public static final String WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE = + PREFIX + "WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE"; + + /** + * Summary for redacting sensitive content on lockscreen for work profiles + */ + public static final String WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY = + PREFIX + "WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY"; + + /** + * Indicates that the work profile admin doesn't allow this notification listener to + * access + * work profile notifications + */ + public static final String WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED = + PREFIX + "WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED"; + + /** + * This setting shows a user's connected work and personal apps. + */ + public static final String CONNECTED_WORK_AND_PERSONAL_APPS_TITLE = + PREFIX + "CONNECTED_WORK_AND_PERSONAL_APPS_TITLE"; + + /** + * This text lets a user know that if they connect work and personal apps, + * they will share permissions and can access each other's data + */ + public static final String CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA = + PREFIX + "CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA"; + + /** + * This text lets a user know that they should only connect work and personal apps if + * they + * trust the work app with their personal data + */ + public static final String ONLY_CONNECT_TRUSTED_APPS = + PREFIX + "ONLY_CONNECT_TRUSTED_APPS"; + + /** + * This text lets a user know how to disconnect work and personal apps + */ + public static final String HOW_TO_DISCONNECT_APPS = PREFIX + "HOW_TO_DISCONNECT_APPS"; + + /** + * Title of confirmation dialog when connecting work and personal apps + */ + public static final String CONNECT_APPS_DIALOG_TITLE = + PREFIX + "CONNECT_APPS_DIALOG_TITLE"; + + /** + * This dialog is shown when a user tries to connect a work app to a personal + * app + */ + public static final String CONNECT_APPS_DIALOG_SUMMARY = + PREFIX + "CONNECT_APPS_DIALOG_SUMMARY"; + + /** + * This text lets the user know that their work app will be able to access data in their + * personal app + */ + public static final String APP_CAN_ACCESS_PERSONAL_DATA = + PREFIX + "APP_CAN_ACCESS_PERSONAL_DATA"; + + /** + * This text lets the user know that their work app will be able to use permissions in + * their personal app + */ + public static final String APP_CAN_ACCESS_PERSONAL_PERMISSIONS = + PREFIX + "APP_CAN_ACCESS_PERSONAL_PERMISSIONS"; + + /** + * lets a user know that they need to install an app in their work profile in order to + * connect it to the corresponding personal app + */ + public static final String INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT = + PREFIX + "INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT"; + + /** + * lets a user know that they need to install an app in their personal profile in order + * to + * connect it to the corresponding work app + */ + public static final String INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT = + PREFIX + "INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT"; + + /** + * Header for showing the organisation managing the work profile + */ + public static final String WORK_PROFILE_MANAGED_BY = PREFIX + "WORK_PROFILE_MANAGED_BY"; + + /** + * Summary showing the enterprise who manages the device or profile. + */ + public static final String MANAGED_BY = PREFIX + "MANAGED_BY"; + + /** + * Warning message about disabling usage access on profile owner + */ + public static final String WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING = + PREFIX + "WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING"; + + /** + * Title for dialog displayed when user taps a setting on their phone that's blocked by + * their IT admin + */ + public static final String DISABLED_BY_IT_ADMIN_TITLE = + PREFIX + "DISABLED_BY_IT_ADMIN_TITLE"; + + /** + * Shown when the user tries to change phone settings that are blocked by their IT admin + */ + public static final String CONTACT_YOUR_IT_ADMIN = PREFIX + "CONTACT_YOUR_IT_ADMIN"; + + /** + * warn user about policies the admin can set in a work profile + */ + public static final String WORK_PROFILE_ADMIN_POLICIES_WARNING = + PREFIX + "WORK_PROFILE_ADMIN_POLICIES_WARNING"; + + /** + * warn user about policies the admin can set on a user + */ + public static final String USER_ADMIN_POLICIES_WARNING = + PREFIX + "USER_ADMIN_POLICIES_WARNING"; + + /** + * warn user about policies the admin can set on a device + */ + public static final String DEVICE_ADMIN_POLICIES_WARNING = + PREFIX + "DEVICE_ADMIN_POLICIES_WARNING"; + + /** + * Condition that work profile is off + */ + public static final String WORK_PROFILE_OFF_CONDITION_TITLE = + PREFIX + "WORK_PROFILE_OFF_CONDITION_TITLE"; + + /** + * Title of work profile setting page + */ + public static final String MANAGED_PROFILE_SETTINGS_TITLE = + PREFIX + "MANAGED_PROFILE_SETTINGS_TITLE"; + + /** + * Setting that lets a user's personal apps identify contacts using the user's work + * directory + */ + public static final String WORK_PROFILE_CONTACT_SEARCH_TITLE = + PREFIX + "WORK_PROFILE_CONTACT_SEARCH_TITLE"; + + /** + * This setting lets a user's personal apps identify contacts using the user's work + * directory + */ + public static final String WORK_PROFILE_CONTACT_SEARCH_SUMMARY = + PREFIX + "WORK_PROFILE_CONTACT_SEARCH_SUMMARY"; + + /** + * This setting lets the user show their work events on their personal calendar + */ + public static final String CROSS_PROFILE_CALENDAR_TITLE = + PREFIX + "CROSS_PROFILE_CALENDAR_TITLE"; + + /** + * Setting description. If the user turns on this setting, they can see their work + * events on their personal calendar + */ + public static final String CROSS_PROFILE_CALENDAR_SUMMARY = + PREFIX + "CROSS_PROFILE_CALENDAR_SUMMARY"; + + /** + * Label explaining that an always-on VPN was set by the admin in the personal profile + */ + public static final String ALWAYS_ON_VPN_PERSONAL_PROFILE = + PREFIX + "ALWAYS_ON_VPN_PERSONAL_PROFILE"; + + /** + * Label explaining that an always-on VPN was set by the admin for the entire device + */ + public static final String ALWAYS_ON_VPN_DEVICE = PREFIX + "ALWAYS_ON_VPN_DEVICE"; + + /** + * Label explaining that an always-on VPN was set by the admin in the work profile + */ + public static final String ALWAYS_ON_VPN_WORK_PROFILE = + PREFIX + "ALWAYS_ON_VPN_WORK_PROFILE"; + + /** + * Label explaining that the admin installed trusted CA certificates in personal profile + */ + public static final String CA_CERTS_PERSONAL_PROFILE = + PREFIX + "CA_CERTS_PERSONAL_PROFILE"; + + /** + * Label explaining that the admin installed trusted CA certificates in work profile + */ + public static final String CA_CERTS_WORK_PROFILE = PREFIX + "CA_CERTS_WORK_PROFILE"; + + /** + * Label explaining that the admin installed trusted CA certificates for the entire + * device + */ + public static final String CA_CERTS_DEVICE = PREFIX + "CA_CERTS_DEVICE"; + + /** + * Label explaining that the admin can lock the device and change the user's password + */ + public static final String ADMIN_CAN_LOCK_DEVICE = PREFIX + "ADMIN_CAN_LOCK_DEVICE"; + + /** + * Label explaining that the admin can wipe the device remotely + */ + public static final String ADMIN_CAN_WIPE_DEVICE = PREFIX + "ADMIN_CAN_WIPE_DEVICE"; + + /** + * Label explaining that the admin configured the device to wipe itself when the + * password is mistyped too many times + */ + public static final String ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE = + PREFIX + "ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE"; + + /** + * Label explaining that the admin configured the work profile to wipe itself when the + * password is mistyped too many times + */ + public static final String ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE = + PREFIX + "ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE"; + + /** + * Message indicating that the device is enterprise-managed by a Device Owner + */ + public static final String DEVICE_MANAGED_WITHOUT_NAME = + PREFIX + "DEVICE_MANAGED_WITHOUT_NAME"; + + /** + * Message indicating that the device is enterprise-managed by a Device Owner + */ + public static final String DEVICE_MANAGED_WITH_NAME = + PREFIX + "DEVICE_MANAGED_WITH_NAME"; + + /** + * Subtext of work profile app for current setting + */ + public static final String WORK_PROFILE_APP_SUBTEXT = + PREFIX + "WORK_PROFILE_APP_SUBTEXT"; + + /** + * Subtext of personal profile app for current setting + */ + public static final String PERSONAL_PROFILE_APP_SUBTEXT = + PREFIX + "PERSONAL_PROFILE_APP_SUBTEXT"; + + /** + * Title shown for work menu item that launches fingerprint settings or enrollment + */ + public static final String FINGERPRINT_FOR_WORK = PREFIX + "FINGERPRINT_FOR_WORK"; + + /** + * Message shown in face enrollment dialog, when face unlock is disabled by device admin + */ + public static final String FACE_UNLOCK_DISABLED = PREFIX + "FACE_UNLOCK_DISABLED"; + + /** + * message shown in fingerprint enrollment dialog, when fingerprint unlock is disabled + * by device admin + */ + public static final String FINGERPRINT_UNLOCK_DISABLED = + PREFIX + "FINGERPRINT_UNLOCK_DISABLED"; + + /** + * Text shown in fingerprint settings explaining what the fingerprint can be used for in + * the case unlocking is disabled + */ + public static final String FINGERPRINT_UNLOCK_DISABLED_EXPLANATION = + PREFIX + "FINGERPRINT_UNLOCK_DISABLED_EXPLANATION"; + + /** + * Error shown when in PIN mode and PIN has been used recently + */ + public static final String PIN_RECENTLY_USED = PREFIX + "PIN_RECENTLY_USED"; + + /** + * Error shown when in PASSWORD mode and password has been used recently + */ + public static final String PASSWORD_RECENTLY_USED = PREFIX + "PASSWORD_RECENTLY_USED"; + + /** + * Title of preference to manage device admin apps + */ + public static final String MANAGE_DEVICE_ADMIN_APPS = + PREFIX + "MANAGE_DEVICE_ADMIN_APPS"; + + /** + * Inform the user that currently no device admin apps are installed and active + */ + public static final String NUMBER_OF_DEVICE_ADMINS_NONE = + PREFIX + "NUMBER_OF_DEVICE_ADMINS_NONE"; + + /** + * Inform the user how many device admin apps are installed and active + */ + public static final String NUMBER_OF_DEVICE_ADMINS = PREFIX + "NUMBER_OF_DEVICE_ADMINS"; + + /** + * Title that asks the user to contact the IT admin to reset password + */ + public static final String FORGOT_PASSWORD_TITLE = PREFIX + "FORGOT_PASSWORD_TITLE"; + + /** + * Content that asks the user to contact the IT admin to reset password + */ + public static final String FORGOT_PASSWORD_TEXT = PREFIX + "FORGOT_PASSWORD_TEXT"; + + /** + * Error message shown when trying to move device administrators to external disks, such + * as SD card + */ + public static final String ERROR_MOVE_DEVICE_ADMIN = PREFIX + "ERROR_MOVE_DEVICE_ADMIN"; + + /** + * Device admin app settings title + */ + public static final String DEVICE_ADMIN_SETTINGS_TITLE = + PREFIX + "DEVICE_ADMIN_SETTINGS_TITLE"; + + /** + * Button to remove the active device admin app + */ + public static final String REMOVE_DEVICE_ADMIN = PREFIX + "REMOVE_DEVICE_ADMIN"; + + /** + * Button to uninstall the device admin app + */ + public static final String UNINSTALL_DEVICE_ADMIN = PREFIX + "UNINSTALL_DEVICE_ADMIN"; + + /** + * Button to deactivate and uninstall the device admin app + */ + public static final String REMOVE_AND_UNINSTALL_DEVICE_ADMIN = + PREFIX + "REMOVE_AND_UNINSTALL_DEVICE_ADMIN"; + + /** + * Title for selecting device admin apps + */ + public static final String SELECT_DEVICE_ADMIN_APPS = + PREFIX + "SELECT_DEVICE_ADMIN_APPS"; + + /** + * Message when there are no available device admin apps to display + */ + public static final String NO_DEVICE_ADMINS = PREFIX + "NO_DEVICE_ADMINS"; + + /** + * Title for screen to add a device admin app + */ + public static final String ACTIVATE_DEVICE_ADMIN_APP = + PREFIX + "ACTIVATE_DEVICE_ADMIN_APP"; + + /** + * Label for button to set the active device admin + */ + public static final String ACTIVATE_THIS_DEVICE_ADMIN_APP = + PREFIX + "ACTIVATE_THIS_DEVICE_ADMIN_APP"; + + /** + * Activate a specific device admin app title + */ + public static final String ACTIVATE_DEVICE_ADMIN_APP_TITLE = + PREFIX + "ACTIVATE_DEVICE_ADMIN_APP_TITLE"; + + /** + * Device admin warning message about policies a not active admin can use + */ + public static final String NEW_DEVICE_ADMIN_WARNING = + PREFIX + "NEW_DEVICE_ADMIN_WARNING"; + + /** + * Simplified device admin warning message + */ + public static final String NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED = + PREFIX + "NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED"; + + /** + * Device admin warning message about policies the active admin can use + */ + public static final String ACTIVE_DEVICE_ADMIN_WARNING = + PREFIX + "ACTIVE_DEVICE_ADMIN_WARNING"; + + /** + * Title for screen to set a profile owner + */ + public static final String SET_PROFILE_OWNER_TITLE = PREFIX + "SET_PROFILE_OWNER_TITLE"; + + /** + * Simplified title for dialog to set a profile owner + */ + public static final String SET_PROFILE_OWNER_DIALOG_TITLE = + PREFIX + "SET_PROFILE_OWNER_DIALOG_TITLE"; + + /** + * Warning when trying to add a profile owner admin after setup has completed + */ + public static final String SET_PROFILE_OWNER_POSTSETUP_WARNING = + PREFIX + "SET_PROFILE_OWNER_POSTSETUP_WARNING"; + + /** + * Message displayed to let the user know that some of the options are disabled by admin + */ + public static final String OTHER_OPTIONS_DISABLED_BY_ADMIN = + PREFIX + "OTHER_OPTIONS_DISABLED_BY_ADMIN"; + + /** + * This is shown if the authenticator for a given account fails to remove it due to + * admin restrictions + */ + public static final String REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION = + PREFIX + "REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION"; + + /** + * Url for learning more about IT admin policy disabling + */ + public static final String IT_ADMIN_POLICY_DISABLING_INFO_URL = + PREFIX + "IT_ADMIN_POLICY_DISABLING_INFO_URL"; + + /** + * Title of dialog shown to ask for user consent for sharing a bugreport that was + * requested + * remotely by the IT administrator + */ + public static final String SHARE_REMOTE_BUGREPORT_DIALOG_TITLE = + PREFIX + "SHARE_REMOTE_BUGREPORT_DIALOG_TITLE"; + + /** + * Message of a dialog shown to ask for user consent for sharing a bugreport that was + * requested remotely by the IT administrator + */ + public static final String SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT = + PREFIX + "SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT"; + + /** + * Message of a dialog shown to ask for user consent for sharing a bugreport that was + * requested remotely by the IT administrator and it's still being taken + */ + public static final String SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT = + PREFIX + "SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT"; + + /** + * Message of a dialog shown to inform that the remote bugreport that was requested + * remotely by the IT administrator is still being taken and will be shared when + * finished + */ + public static final String SHARING_REMOTE_BUGREPORT_MESSAGE = + PREFIX + "SHARING_REMOTE_BUGREPORT_MESSAGE"; + + /** + * Managed device information screen title + */ + public static final String MANAGED_DEVICE_INFO = PREFIX + "MANAGED_DEVICE_INFO"; + + /** + * Summary for managed device info section + */ + public static final String MANAGED_DEVICE_INFO_SUMMARY = + PREFIX + "MANAGED_DEVICE_INFO_SUMMARY"; + + /** + * Summary for managed device info section including organization name + */ + public static final String MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME = + PREFIX + "MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME"; + + /** + * Enterprise Privacy settings header, summarizing the powers that the admin has + */ + public static final String ENTERPRISE_PRIVACY_HEADER = + PREFIX + "ENTERPRISE_PRIVACY_HEADER"; + + /** + * Types of information your organization can see section title + */ + public static final String INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE = + PREFIX + "INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE"; + + /** + * Changes made by your organization's admin section title + */ + public static final String CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE = + PREFIX + "CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE"; + + /** + * Your access to this device section title + */ + public static final String YOUR_ACCESS_TO_THIS_DEVICE_TITLE = + PREFIX + "YOUR_ACCESS_TO_THIS_DEVICE_TITLE"; + + /** + * Things the admin can see: data associated with the work account + */ + public static final String ADMIN_CAN_SEE_WORK_DATA_WARNING = + PREFIX + "ADMIN_CAN_SEE_WORK_DATA_WARNING"; + + /** + * Things the admin can see: Apps installed on the device + */ + public static final String ADMIN_CAN_SEE_APPS_WARNING = + PREFIX + "ADMIN_CAN_SEE_APPS_WARNING"; + + /** + * Things the admin can see: Amount of time and data spent in each app + */ + public static final String ADMIN_CAN_SEE_USAGE_WARNING = + PREFIX + "ADMIN_CAN_SEE_USAGE_WARNING"; + + /** + * Things the admin can see: Most recent network traffic log + */ + public static final String ADMIN_CAN_SEE_NETWORK_LOGS_WARNING = + PREFIX + "ADMIN_CAN_SEE_NETWORK_LOGS_WARNING"; + /** + * Things the admin can see: Most recent bug report + */ + public static final String ADMIN_CAN_SEE_BUG_REPORT_WARNING = + PREFIX + "ADMIN_CAN_SEE_BUG_REPORT_WARNING"; + + /** + * Things the admin can see: Security logs + */ + public static final String ADMIN_CAN_SEE_SECURITY_LOGS_WARNING = + PREFIX + "ADMIN_CAN_SEE_SECURITY_LOGS_WARNING"; + + /** + * Indicate that the admin never took a given action so far (e.g. did not retrieve + * security logs or request bug reports). + */ + public static final String ADMIN_ACTION_NONE = PREFIX + "ADMIN_ACTION_NONE"; + + /** + * Indicate that the admin installed one or more apps on the device + */ + public static final String ADMIN_ACTION_APPS_INSTALLED = + PREFIX + "ADMIN_ACTION_APPS_INSTALLED"; + + /** + * Explaining that the number of apps is an estimation + */ + public static final String ADMIN_ACTION_APPS_COUNT_ESTIMATED = + PREFIX + "ADMIN_ACTION_APPS_COUNT_ESTIMATED"; + + /** + * Indicating the minimum number of apps that a label refers to + */ + public static final String ADMIN_ACTIONS_APPS_COUNT_MINIMUM = + PREFIX + "ADMIN_ACTIONS_APPS_COUNT_MINIMUM"; + + /** + * Indicate that the admin granted one or more apps access to the device's location + */ + public static final String ADMIN_ACTION_ACCESS_LOCATION = + PREFIX + "ADMIN_ACTION_ACCESS_LOCATION"; + + /** + * Indicate that the admin granted one or more apps access to the microphone + */ + public static final String ADMIN_ACTION_ACCESS_MICROPHONE = + PREFIX + "ADMIN_ACTION_ACCESS_MICROPHONE"; + + /** + * Indicate that the admin granted one or more apps access to the camera + */ + public static final String ADMIN_ACTION_ACCESS_CAMERA = + PREFIX + "ADMIN_ACTION_ACCESS_CAMERA"; + + /** + * Indicate that the admin set one or more apps as defaults for common actions + */ + public static final String ADMIN_ACTION_SET_DEFAULT_APPS = + PREFIX + "ADMIN_ACTION_SET_DEFAULT_APPS"; + + /** + * Indicate the number of apps that a label refers to + */ + public static final String ADMIN_ACTIONS_APPS_COUNT = + PREFIX + "ADMIN_ACTIONS_APPS_COUNT"; + + /** + * Indicate that the current input method was set by the admin + */ + public static final String ADMIN_ACTION_SET_CURRENT_INPUT_METHOD = + PREFIX + "ADMIN_ACTION_SET_CURRENT_INPUT_METHOD"; + + /** + * The input method set by the admin + */ + public static final String ADMIN_ACTION_SET_INPUT_METHOD_NAME = + PREFIX + "ADMIN_ACTION_SET_INPUT_METHOD_NAME"; + + /** + * Indicate that a global HTTP proxy was set by the admin + */ + public static final String ADMIN_ACTION_SET_HTTP_PROXY = + PREFIX + "ADMIN_ACTION_SET_HTTP_PROXY"; + + /** + * Summary for Enterprise Privacy settings, explaining what the user can expect to find + * under it + */ + public static final String WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY = + PREFIX + "WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY"; + + /** + * Setting on privacy settings screen that will show work policy info + */ + public static final String WORK_PROFILE_PRIVACY_POLICY_INFO = + PREFIX + "WORK_PROFILE_PRIVACY_POLICY_INFO"; + + /** + * Search keywords for connected work and personal apps + */ + public static final String CONNECTED_APPS_SEARCH_KEYWORDS = + PREFIX + "CONNECTED_APPS_SEARCH_KEYWORDS"; + + /** + * Work profile unification keywords + */ + public static final String WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS = + PREFIX + "WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS"; + + /** + * Accounts keywords + */ + public static final String ACCOUNTS_SEARCH_KEYWORDS = + PREFIX + "ACCOUNTS_SEARCH_KEYWORDS"; + + /** + * Summary for settings preference disabled by administrator + */ + public static final String CONTROLLED_BY_ADMIN_SUMMARY = + PREFIX + "CONTROLLED_BY_ADMIN_SUMMARY"; + + /** + * User label for a work profile + */ + public static final String WORK_PROFILE_USER_LABEL = PREFIX + "WORK_PROFILE_USER_LABEL"; + + /** + * Header for items under the work user + */ + public static final String WORK_CATEGORY_HEADER = PREFIX + "WORK_CATEGORY_HEADER"; + + /** + * Header for items under the personal user + */ + public static final String PERSONAL_CATEGORY_HEADER = PREFIX + "category_personal"; + + /** + * @hide + */ + static Set<String> buildStringsSet() { + Set<String> strings = new HashSet<>(); + strings.add(FACE_SETTINGS_FOR_WORK_TITLE); + strings.add(WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE); + strings.add(WORK_PROFILE_IT_ADMIN_CANT_RESET_SCREEN_LOCK); + strings.add(WORK_PROFILE_SCREEN_LOCK_SETUP_MESSAGE); + strings.add(WORK_PROFILE_SET_UNLOCK_LAUNCH_PICKER_TITLE); + strings.add(WORK_PROFILE_LAST_PATTERN_ATTEMPT_BEFORE_WIPE); + strings.add(WORK_PROFILE_LAST_PIN_ATTEMPT_BEFORE_WIPE); + strings.add(WORK_PROFILE_LAST_PASSWORD_ATTEMPT_BEFORE_WIPE); + strings.add(WORK_PROFILE_LOCK_ATTEMPTS_FAILED); + strings.add(ACCESSIBILITY_CATEGORY_WORK); + strings.add(ACCESSIBILITY_CATEGORY_PERSONAL); + strings.add(ACCESSIBILITY_WORK_ACCOUNT_TITLE); + strings.add(ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE); + strings.add(WORK_PROFILE_LOCATION_SWITCH_TITLE); + strings.add(SET_WORK_PROFILE_PASSWORD_HEADER); + strings.add(SET_WORK_PROFILE_PIN_HEADER); + strings.add(SET_WORK_PROFILE_PATTERN_HEADER); + strings.add(CONFIRM_WORK_PROFILE_PASSWORD_HEADER); + strings.add(CONFIRM_WORK_PROFILE_PIN_HEADER); + strings.add(CONFIRM_WORK_PROFILE_PATTERN_HEADER); + strings.add(REENTER_WORK_PROFILE_PASSWORD_HEADER); + strings.add(REENTER_WORK_PROFILE_PIN_HEADER); + strings.add(WORK_PROFILE_CONFIRM_PATTERN); + strings.add(WORK_PROFILE_CONFIRM_PIN); + strings.add(WORK_PROFILE_PASSWORD_REQUIRED); + strings.add(WORK_PROFILE_SECURITY_TITLE); + strings.add(WORK_PROFILE_UNIFY_LOCKS_TITLE); + strings.add(WORK_PROFILE_UNIFY_LOCKS_SUMMARY); + strings.add(WORK_PROFILE_UNIFY_LOCKS_DETAIL); + strings.add(WORK_PROFILE_UNIFY_LOCKS_NONCOMPLIANT); + strings.add(WORK_PROFILE_KEYBOARDS_AND_TOOLS); + strings.add(WORK_PROFILE_NOT_AVAILABLE); + strings.add(WORK_PROFILE_SETTING); + strings.add(WORK_PROFILE_SETTING_ON_SUMMARY); + strings.add(WORK_PROFILE_SETTING_OFF_SUMMARY); + strings.add(REMOVE_WORK_PROFILE); + strings.add(DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING); + strings.add(WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING); + strings.add(WORK_PROFILE_CONFIRM_REMOVE_TITLE); + strings.add(WORK_PROFILE_CONFIRM_REMOVE_MESSAGE); + strings.add(WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS); + strings.add(WORK_PROFILE_SOUND_SETTINGS_SECTION_HEADER); + strings.add(WORK_PROFILE_USE_PERSONAL_SOUNDS_TITLE); + strings.add(WORK_PROFILE_USE_PERSONAL_SOUNDS_SUMMARY); + strings.add(WORK_PROFILE_RINGTONE_TITLE); + strings.add(WORK_PROFILE_NOTIFICATION_RINGTONE_TITLE); + strings.add(WORK_PROFILE_ALARM_RINGTONE_TITLE); + strings.add(WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_ACTIVE_SUMMARY); + strings.add(ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_TITLE); + strings.add(ENABLE_WORK_PROFILE_SYNC_WITH_PERSONAL_SOUNDS_DIALOG_MESSAGE); + strings.add(WORK_PROFILE_NOTIFICATIONS_SECTION_HEADER); + strings.add(WORK_PROFILE_LOCKED_NOTIFICATION_TITLE); + strings.add(WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_TITLE); + strings.add(WORK_PROFILE_LOCK_SCREEN_REDACT_NOTIFICATION_SUMMARY); + strings.add(WORK_PROFILE_NOTIFICATION_LISTENER_BLOCKED); + strings.add(CONNECTED_WORK_AND_PERSONAL_APPS_TITLE); + strings.add(CONNECTED_APPS_SHARE_PERMISSIONS_AND_DATA); + strings.add(ONLY_CONNECT_TRUSTED_APPS); + strings.add(HOW_TO_DISCONNECT_APPS); + strings.add(CONNECT_APPS_DIALOG_TITLE); + strings.add(CONNECT_APPS_DIALOG_SUMMARY); + strings.add(APP_CAN_ACCESS_PERSONAL_DATA); + strings.add(APP_CAN_ACCESS_PERSONAL_PERMISSIONS); + strings.add(INSTALL_IN_WORK_PROFILE_TO_CONNECT_PROMPT); + strings.add(INSTALL_IN_PERSONAL_PROFILE_TO_CONNECT_PROMPT); + strings.add(WORK_PROFILE_MANAGED_BY); + strings.add(MANAGED_BY); + strings.add(WORK_PROFILE_DISABLE_USAGE_ACCESS_WARNING); + strings.add(DISABLED_BY_IT_ADMIN_TITLE); + strings.add(CONTACT_YOUR_IT_ADMIN); + strings.add(WORK_PROFILE_ADMIN_POLICIES_WARNING); + strings.add(USER_ADMIN_POLICIES_WARNING); + strings.add(DEVICE_ADMIN_POLICIES_WARNING); + strings.add(WORK_PROFILE_OFF_CONDITION_TITLE); + strings.add(MANAGED_PROFILE_SETTINGS_TITLE); + strings.add(WORK_PROFILE_CONTACT_SEARCH_TITLE); + strings.add(WORK_PROFILE_CONTACT_SEARCH_SUMMARY); + strings.add(CROSS_PROFILE_CALENDAR_TITLE); + strings.add(CROSS_PROFILE_CALENDAR_SUMMARY); + strings.add(ALWAYS_ON_VPN_PERSONAL_PROFILE); + strings.add(ALWAYS_ON_VPN_DEVICE); + strings.add(ALWAYS_ON_VPN_WORK_PROFILE); + strings.add(CA_CERTS_PERSONAL_PROFILE); + strings.add(CA_CERTS_WORK_PROFILE); + strings.add(CA_CERTS_DEVICE); + strings.add(ADMIN_CAN_LOCK_DEVICE); + strings.add(ADMIN_CAN_WIPE_DEVICE); + strings.add(ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_DEVICE); + strings.add(ADMIN_CONFIGURED_FAILED_PASSWORD_WIPE_WORK_PROFILE); + strings.add(DEVICE_MANAGED_WITHOUT_NAME); + strings.add(DEVICE_MANAGED_WITH_NAME); + strings.add(WORK_PROFILE_APP_SUBTEXT); + strings.add(PERSONAL_PROFILE_APP_SUBTEXT); + strings.add(FINGERPRINT_FOR_WORK); + strings.add(FACE_UNLOCK_DISABLED); + strings.add(FINGERPRINT_UNLOCK_DISABLED); + strings.add(FINGERPRINT_UNLOCK_DISABLED_EXPLANATION); + strings.add(PIN_RECENTLY_USED); + strings.add(PASSWORD_RECENTLY_USED); + strings.add(MANAGE_DEVICE_ADMIN_APPS); + strings.add(NUMBER_OF_DEVICE_ADMINS_NONE); + strings.add(NUMBER_OF_DEVICE_ADMINS); + strings.add(FORGOT_PASSWORD_TITLE); + strings.add(FORGOT_PASSWORD_TEXT); + strings.add(ERROR_MOVE_DEVICE_ADMIN); + strings.add(DEVICE_ADMIN_SETTINGS_TITLE); + strings.add(REMOVE_DEVICE_ADMIN); + strings.add(UNINSTALL_DEVICE_ADMIN); + strings.add(REMOVE_AND_UNINSTALL_DEVICE_ADMIN); + strings.add(SELECT_DEVICE_ADMIN_APPS); + strings.add(NO_DEVICE_ADMINS); + strings.add(ACTIVATE_DEVICE_ADMIN_APP); + strings.add(ACTIVATE_THIS_DEVICE_ADMIN_APP); + strings.add(ACTIVATE_DEVICE_ADMIN_APP_TITLE); + strings.add(NEW_DEVICE_ADMIN_WARNING); + strings.add(NEW_DEVICE_ADMIN_WARNING_SIMPLIFIED); + strings.add(ACTIVE_DEVICE_ADMIN_WARNING); + strings.add(SET_PROFILE_OWNER_TITLE); + strings.add(SET_PROFILE_OWNER_DIALOG_TITLE); + strings.add(SET_PROFILE_OWNER_POSTSETUP_WARNING); + strings.add(OTHER_OPTIONS_DISABLED_BY_ADMIN); + strings.add(REMOVE_ACCOUNT_FAILED_ADMIN_RESTRICTION); + strings.add(IT_ADMIN_POLICY_DISABLING_INFO_URL); + strings.add(SHARE_REMOTE_BUGREPORT_DIALOG_TITLE); + strings.add(SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT); + strings.add(SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT); + strings.add(SHARING_REMOTE_BUGREPORT_MESSAGE); + strings.add(MANAGED_DEVICE_INFO); + strings.add(MANAGED_DEVICE_INFO_SUMMARY); + strings.add(MANAGED_DEVICE_INFO_SUMMARY_WITH_NAME); + strings.add(ENTERPRISE_PRIVACY_HEADER); + strings.add(INFORMATION_YOUR_ORGANIZATION_CAN_SEE_TITLE); + strings.add(CHANGES_MADE_BY_YOUR_ORGANIZATION_ADMIN_TITLE); + strings.add(YOUR_ACCESS_TO_THIS_DEVICE_TITLE); + strings.add(ADMIN_CAN_SEE_WORK_DATA_WARNING); + strings.add(ADMIN_CAN_SEE_APPS_WARNING); + strings.add(ADMIN_CAN_SEE_USAGE_WARNING); + strings.add(ADMIN_CAN_SEE_NETWORK_LOGS_WARNING); + strings.add(ADMIN_CAN_SEE_BUG_REPORT_WARNING); + strings.add(ADMIN_CAN_SEE_SECURITY_LOGS_WARNING); + strings.add(ADMIN_ACTION_NONE); + strings.add(ADMIN_ACTION_APPS_INSTALLED); + strings.add(ADMIN_ACTION_APPS_COUNT_ESTIMATED); + strings.add(ADMIN_ACTIONS_APPS_COUNT_MINIMUM); + strings.add(ADMIN_ACTION_ACCESS_LOCATION); + strings.add(ADMIN_ACTION_ACCESS_MICROPHONE); + strings.add(ADMIN_ACTION_ACCESS_CAMERA); + strings.add(ADMIN_ACTION_SET_DEFAULT_APPS); + strings.add(ADMIN_ACTIONS_APPS_COUNT); + strings.add(ADMIN_ACTION_SET_CURRENT_INPUT_METHOD); + strings.add(ADMIN_ACTION_SET_INPUT_METHOD_NAME); + strings.add(ADMIN_ACTION_SET_HTTP_PROXY); + strings.add(WORK_PROFILE_PRIVACY_POLICY_INFO_SUMMARY); + strings.add(WORK_PROFILE_PRIVACY_POLICY_INFO); + strings.add(CONNECTED_APPS_SEARCH_KEYWORDS); + strings.add(WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS); + strings.add(ACCOUNTS_SEARCH_KEYWORDS); + strings.add(CONTROLLED_BY_ADMIN_SUMMARY); + strings.add(WORK_PROFILE_USER_LABEL); + strings.add(WORK_CATEGORY_HEADER); + strings.add(PERSONAL_CATEGORY_HEADER); + return strings; + } + } + + /** + * Class containing the identifiers used to update device management-related system strings * in the Launcher package. * * @hide */ public static final class Launcher { - private Launcher(){} + private Launcher() { + } private static final String PREFIX = "Launcher."; @@ -576,6 +2016,7 @@ public final class DevicePolicyResources { private SystemUi() { } + private static final String PREFIX = "SystemUi."; /** @@ -649,9 +2090,9 @@ public final class DevicePolicyResources { PREFIX + "QS_MSG_NAMED_WORK_PROFILE_MONITORING"; /** - * Disclosure at the bottom of Quick Settings to indicate network activity is visible to + * Disclosure at the bottom of Quick Settings to indicate network activity is visible to * admin. - */ + */ public static final String QS_MSG_WORK_PROFILE_NETWORK = PREFIX + "QS_MSG_WORK_PROFILE_NETWORK"; diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java index 8c232c012f50..1f7ae4ad35de 100644 --- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java +++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java @@ -25,6 +25,7 @@ import android.annotation.SystemApi; import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; +import android.os.PersistableBundle; import android.stats.devicepolicy.DevicePolicyEnums; import java.util.Locale; @@ -52,6 +53,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { @SuppressLint("UseIcu") @Nullable private final Locale mLocale; private final boolean mDeviceOwnerCanGrantSensorsPermissions; + @NonNull private final PersistableBundle mAdminExtras; private FullyManagedDeviceProvisioningParams( @NonNull ComponentName deviceAdminComponentName, @@ -60,7 +62,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { @Nullable String timeZone, long localTime, @Nullable @SuppressLint("UseIcu") Locale locale, - boolean deviceOwnerCanGrantSensorsPermissions) { + boolean deviceOwnerCanGrantSensorsPermissions, + @NonNull PersistableBundle adminExtras) { this.mDeviceAdminComponentName = requireNonNull(deviceAdminComponentName); this.mOwnerName = requireNonNull(ownerName); this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled; @@ -69,6 +72,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { this.mLocale = locale; this.mDeviceOwnerCanGrantSensorsPermissions = deviceOwnerCanGrantSensorsPermissions; + this.mAdminExtras = adminExtras; } private FullyManagedDeviceProvisioningParams( @@ -78,14 +82,16 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { @Nullable String timeZone, long localTime, @Nullable String localeStr, - boolean deviceOwnerCanGrantSensorsPermissions) { + boolean deviceOwnerCanGrantSensorsPermissions, + @Nullable PersistableBundle adminExtras) { this(deviceAdminComponentName, ownerName, leaveAllSystemAppsEnabled, timeZone, localTime, getLocale(localeStr), - deviceOwnerCanGrantSensorsPermissions); + deviceOwnerCanGrantSensorsPermissions, + adminExtras); } @Nullable @@ -151,6 +157,15 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { } /** + * Returns a copy of the admin extras bundle. + * + * @see DevicePolicyManager#EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE + */ + public @NonNull PersistableBundle getAdminExtras() { + return new PersistableBundle(mAdminExtras); + } + + /** * Logs the provisioning params using {@link DevicePolicyEventLogger}. * * @hide @@ -188,6 +203,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { @Nullable private Locale mLocale; // Default to allowing control over sensor permission grants. boolean mDeviceOwnerCanGrantSensorsPermissions = true; + @NonNull private PersistableBundle mAdminExtras; /** * Initialize a new {@link Builder} to construct a @@ -262,6 +278,17 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { } /** + * Sets a {@link PersistableBundle} that contains admin-specific extras. + */ + @NonNull + public Builder setAdminExtras(@NonNull PersistableBundle adminExtras) { + mAdminExtras = adminExtras != null + ? new PersistableBundle(adminExtras) + : new PersistableBundle(); + return this; + } + + /** * Combines all of the attributes that have been set on this {@code Builder} * * @return a new {@link FullyManagedDeviceProvisioningParams} object. @@ -275,7 +302,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { mTimeZone, mLocalTime, mLocale, - mDeviceOwnerCanGrantSensorsPermissions); + mDeviceOwnerCanGrantSensorsPermissions, + mAdminExtras); } } @@ -298,6 +326,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { + ", mLocale=" + (mLocale == null ? "null" : mLocale) + ", mDeviceOwnerCanGrantSensorsPermissions=" + mDeviceOwnerCanGrantSensorsPermissions + + ", mAdminExtras=" + mAdminExtras + '}'; } @@ -310,6 +339,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { dest.writeLong(mLocalTime); dest.writeString(mLocale == null ? null : mLocale.toLanguageTag()); dest.writeBoolean(mDeviceOwnerCanGrantSensorsPermissions); + dest.writePersistableBundle(mAdminExtras); } @NonNull @@ -324,6 +354,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { long localtime = in.readLong(); String locale = in.readString(); boolean deviceOwnerCanGrantSensorsPermissions = in.readBoolean(); + PersistableBundle adminExtras = in.readPersistableBundle(); return new FullyManagedDeviceProvisioningParams( componentName, @@ -332,7 +363,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { timeZone, localtime, locale, - deviceOwnerCanGrantSensorsPermissions); + deviceOwnerCanGrantSensorsPermissions, + adminExtras); } @Override diff --git a/core/java/android/app/admin/ManagedProfileProvisioningParams.java b/core/java/android/app/admin/ManagedProfileProvisioningParams.java index ccbef7321be6..f91d60a6a9fa 100644 --- a/core/java/android/app/admin/ManagedProfileProvisioningParams.java +++ b/core/java/android/app/admin/ManagedProfileProvisioningParams.java @@ -23,8 +23,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.ComponentName; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.PersistableBundle; import android.stats.devicepolicy.DevicePolicyEnums; /** @@ -49,7 +51,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable { private final boolean mLeaveAllSystemAppsEnabled; private final boolean mOrganizationOwnedProvisioning; private final boolean mKeepAccountOnMigration; - + @NonNull private final PersistableBundle mAdminExtras; private ManagedProfileProvisioningParams( @NonNull ComponentName profileAdminComponentName, @@ -58,7 +60,8 @@ public final class ManagedProfileProvisioningParams implements Parcelable { @Nullable Account accountToMigrate, boolean leaveAllSystemAppsEnabled, boolean organizationOwnedProvisioning, - boolean keepAccountOnMigration) { + boolean keepAccountOnMigration, + @NonNull PersistableBundle adminExtras) { this.mProfileAdminComponentName = requireNonNull(profileAdminComponentName); this.mOwnerName = requireNonNull(ownerName); this.mProfileName = profileName; @@ -66,6 +69,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable { this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled; this.mOrganizationOwnedProvisioning = organizationOwnedProvisioning; this.mKeepAccountOnMigration = keepAccountOnMigration; + this.mAdminExtras = adminExtras; } /** @@ -124,6 +128,15 @@ public final class ManagedProfileProvisioningParams implements Parcelable { } /** + * Returns a copy of the admin extras bundle. + * + * @see DevicePolicyManager#EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE + */ + public @NonNull PersistableBundle getAdminExtras() { + return new PersistableBundle(mAdminExtras); + } + + /** * Logs the provisioning params using {@link DevicePolicyEventLogger}. * * @hide @@ -160,6 +173,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable { private boolean mLeaveAllSystemAppsEnabled; private boolean mOrganizationOwnedProvisioning; private boolean mKeepingAccountOnMigration; + @Nullable private PersistableBundle mAdminExtras; /** * Initialize a new {@link Builder) to construct a {@link ManagedProfileProvisioningParams}. @@ -235,6 +249,17 @@ public final class ManagedProfileProvisioningParams implements Parcelable { } /** + * Sets a {@link Bundle} that contains admin-specific extras. + */ + @NonNull + public Builder setAdminExtras(@NonNull PersistableBundle adminExtras) { + mAdminExtras = adminExtras != null + ? new PersistableBundle(adminExtras) + : new PersistableBundle(); + return this; + } + + /** * Combines all of the attributes that have been set on this {@code Builder}. * * @return a new {@link ManagedProfileProvisioningParams} object. @@ -248,7 +273,8 @@ public final class ManagedProfileProvisioningParams implements Parcelable { mAccountToMigrate, mLeaveAllSystemAppsEnabled, mOrganizationOwnedProvisioning, - mKeepingAccountOnMigration); + mKeepingAccountOnMigration, + mAdminExtras); } } @@ -270,6 +296,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable { + ", mLeaveAllSystemAppsEnabled=" + mLeaveAllSystemAppsEnabled + ", mOrganizationOwnedProvisioning=" + mOrganizationOwnedProvisioning + ", mKeepAccountOnMigration=" + mKeepAccountOnMigration + + ", mAdminExtras=" + mAdminExtras + '}'; } @@ -282,6 +309,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable { dest.writeBoolean(mLeaveAllSystemAppsEnabled); dest.writeBoolean(mOrganizationOwnedProvisioning); dest.writeBoolean(mKeepAccountOnMigration); + dest.writePersistableBundle(mAdminExtras); } public static final @NonNull Creator<ManagedProfileProvisioningParams> CREATOR = @@ -295,6 +323,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable { boolean leaveAllSystemAppsEnabled = in.readBoolean(); boolean organizationOwnedProvisioning = in.readBoolean(); boolean keepAccountMigrated = in.readBoolean(); + PersistableBundle adminExtras = in.readPersistableBundle(); return new ManagedProfileProvisioningParams( componentName, @@ -303,7 +332,8 @@ public final class ManagedProfileProvisioningParams implements Parcelable { account, leaveAllSystemAppsEnabled, organizationOwnedProvisioning, - keepAccountMigrated); + keepAccountMigrated, + adminExtras); } @Override diff --git a/core/java/android/app/admin/ParcelableResource.java b/core/java/android/app/admin/ParcelableResource.java index dba362820b1d..0b1b166add40 100644 --- a/core/java/android/app/admin/ParcelableResource.java +++ b/core/java/android/app/admin/ParcelableResource.java @@ -175,7 +175,7 @@ public final class ParcelableResource implements Parcelable { * <p>Returns the default drawable by calling the {@code defaultDrawableLoader} if the updated * drawable was not found or could not be loaded.</p> */ - @NonNull + @Nullable public Drawable getDrawable( Context context, int density, @@ -200,7 +200,7 @@ public final class ParcelableResource implements Parcelable { * <p>Returns the default string by calling {@code defaultStringLoader} if the updated * string was not found or could not be loaded.</p> */ - @NonNull + @Nullable public String getString( Context context, @NonNull Callable<String> defaultStringLoader) { @@ -267,17 +267,11 @@ public final class ParcelableResource implements Parcelable { /** * returns the {@link Drawable} loaded from calling {@code defaultDrawableLoader}. */ - @NonNull + @Nullable public static Drawable loadDefaultDrawable(@NonNull Callable<Drawable> defaultDrawableLoader) { try { Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null"); - - Drawable drawable = defaultDrawableLoader.call(); - Objects.requireNonNull(drawable, "defaultDrawable can't be null"); - - return drawable; - } catch (NullPointerException rethrown) { - throw rethrown; + return defaultDrawableLoader.call(); } catch (Exception e) { throw new RuntimeException("Couldn't load default drawable: ", e); } @@ -286,17 +280,11 @@ public final class ParcelableResource implements Parcelable { /** * returns the {@link String} loaded from calling {@code defaultStringLoader}. */ - @NonNull + @Nullable public static String loadDefaultString(@NonNull Callable<String> defaultStringLoader) { try { Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null"); - - String string = defaultStringLoader.call(); - Objects.requireNonNull(string, "defaultString can't be null"); - - return string; - } catch (NullPointerException rethrown) { - throw rethrown; + return defaultStringLoader.call(); } catch (Exception e) { throw new RuntimeException("Couldn't load default string: ", e); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 0b8a8a23aee4..a0864d6459d3 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -37,6 +37,7 @@ import android.annotation.TestApi; import android.annotation.UiContext; import android.annotation.UserIdInt; import android.app.ActivityManager; +import android.app.BroadcastOptions; import android.app.GameManager; import android.app.IApplicationThread; import android.app.IServiceConnection; @@ -2260,6 +2261,27 @@ public abstract class Context { } /** + * Version of {@link #sendBroadcastMultiplePermissions(Intent, String[])} that allows you to + * specify the {@link android.app.BroadcastOptions}. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermissions Array of names of permissions that a receiver must hold + * in order to receive your broadcast. + * If empty, no permissions are required. + * @param options Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * @see #sendBroadcastMultiplePermissions(Intent, String[]) + * @see android.app.BroadcastOptions + * @hide + */ + @SystemApi + public void sendBroadcastMultiplePermissions(@NonNull Intent intent, + @NonNull String[] receiverPermissions, @Nullable BroadcastOptions options) { + sendBroadcastMultiplePermissions(intent, receiverPermissions, options.toBundle()); + } + + /** * 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 @@ -5636,6 +5658,15 @@ public abstract class Context { public static final String OVERLAY_SERVICE = "overlay"; /** + * Use with {@link #getSystemService(String)} to manage resources. + * + * @see #getSystemService(String) + * @see com.android.server.resources.ResourcesManagerService + * @hide + */ + public static final String RESOURCES_SERVICE = "resources"; + + /** * Use with {@link #getSystemService(String)} to retrieve a * {android.os.IIdmap2} for managing idmap files (used by overlay * packages). @@ -6675,21 +6706,27 @@ public abstract class Context { @NonNull Configuration overrideConfiguration); /** - * Returns a new <code>Context</code> object from the current context but with resources - * adjusted to match the metrics of <code>display</code>. Each call to this method + * Returns a new {@code Context} object from the current context but with resources + * adjusted to match the metrics of {@code display}. Each call to this method * returns a new instance of a context object. Context objects are not shared; however, * common state (such as the {@link ClassLoader} and other resources for the same - * configuration) can be shared, so the <code>Context</code> itself is lightweight. + * configuration) can be shared, so the {@code Context} itself is lightweight. + * + * <p><b>Note:</b> + * This {@code Context} is <b>not</b> expected to be updated with new configuration if the + * underlying display configuration changes and the cached {@code Resources} it returns + * could be stale. It is suggested to use + * {@link android.hardware.display.DisplayManager.DisplayListener} to listen for + * changes and re-create an instance if necessary. </p> * <p> + * This {@code Context} is <b>not</b> a UI context, do not use it to access UI components + * or obtain a {@link WindowManager} instance. + * </p><p> * To obtain an instance of {@link WindowManager} configured to show windows on the given * display, call {@link #createWindowContext(int, Bundle)} on the returned display context, * then call {@link #getSystemService(String)} or {@link #getSystemService(Class)} on the * returned window context. - * <p> - * <b>Note:</b> The context returned by <code>createDisplayContext(Display)</code> is not a UI - * context. Do not access UI components or obtain a {@link WindowManager} from the context - * created by <code>createDisplayContext(Display)</code>. - * + * </p> * @param display The display to which the current context's resources are adjusted. * * @return A context for the display. diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index fb186fd5dfb3..3e527f8d5215 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3986,7 +3986,7 @@ public class Intent implements Parcelable, Cloneable { * {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @SystemApi public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED"; @@ -6196,6 +6196,8 @@ public class Intent implements Parcelable, Cloneable { * * @hide */ + @SystemApi + @SuppressLint("ActionValue") public static final String EXTRA_USER_HANDLE = "android.intent.extra.user_handle"; @@ -7059,6 +7061,7 @@ public class Intent implements Parcelable, Cloneable { * * @hide */ + @SystemApi public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000; /** * If set, the broadcast will never go to manifest receivers in background (cached diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 1e887582e5c3..94f056110bf7 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -282,7 +282,8 @@ public class CrossProfileApps { final boolean isManagedProfile = mUserManager.isManagedProfile(userHandle.getIdentifier()); if (isManagedProfile) { - return mResources.getDrawable(R.drawable.ic_corp_badge, null); + return mContext.getPackageManager().getUserBadgeForDensityNoBackground( + userHandle, /* density= */ 0); } else { return UserIcons.getDefaultUserIcon( mResources, UserHandle.USER_SYSTEM, true /* light */); diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java index 9735f8157a57..410e106ce584 100644 --- a/core/java/android/content/pm/PackageInfoLite.java +++ b/core/java/android/content/pm/PackageInfoLite.java @@ -21,7 +21,7 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; /** * Basic information about a package as specified in its manifest. @@ -80,10 +80,10 @@ public class PackageInfoLite implements Parcelable { /** * Specifies the recommended install location. Can be one of - * {@link PackageHelper#RECOMMEND_INSTALL_INTERNAL} to install on internal storage, - * {@link PackageHelper#RECOMMEND_INSTALL_EXTERNAL} to install on external media, - * {@link PackageHelper#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors, - * or {@link PackageHelper#RECOMMEND_FAILED_INVALID_APK} for parse errors. + * {@link InstallLocationUtils#RECOMMEND_INSTALL_INTERNAL} to install on internal storage, + * {@link InstallLocationUtils#RECOMMEND_INSTALL_EXTERNAL} to install on external media, + * {@link InstallLocationUtils#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors, + * or {@link InstallLocationUtils#RECOMMEND_FAILED_INVALID_APK} for parse errors. */ public int recommendedInstallLocation; public int installLocation; diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING index a503d14b6de4..dea083422612 100644 --- a/core/java/android/content/pm/TEST_MAPPING +++ b/core/java/android/content/pm/TEST_MAPPING @@ -162,5 +162,68 @@ { "name": "CtsInstallHostTestCases" } + ], + "staged-platinum-postsubmit": [ + { + "name": "CtsIncrementalInstallHostTestCases" + }, + { + "name": "CtsInstallHostTestCases" + }, + { + "name": "CtsStagedInstallHostTestCases" + }, + { + "name": "CtsExtractNativeLibsHostTestCases" + }, + { + "name": "CtsAppSecurityHostTestCases", + "options": [ + { + "include-filter": "com.android.cts.splitapp.SplitAppTest" + }, + { + "include-filter": "android.appsecurity.cts.EphemeralTest" + } + ] + }, + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.pm.PackageParserTest" + } + ] + }, + { + "name": "CtsRollbackManagerHostTestCases" + }, + { + "name": "CtsOsHostTestCases", + "options": [ + { + "include-filter": "com.android.server.pm.PackageParserTest" + } + ] + }, + { + "name": "CtsContentTestCases", + "options": [ + { + "include-filter": "android.content.cts.IntentFilterTest" + } + ] + }, + { + "name": "CtsAppEnumerationTestCases" + }, + { + "name": "PackageManagerServiceUnitTests", + "options": [ + { + "include-filter": "com.android.server.pm.test.verify.domain" + } + ] + } ] } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index 6fd2d05ad135..7a5ac8ede4a4 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -28,6 +28,7 @@ import com.android.internal.annotations.GuardedBy; import java.io.FileDescriptor; import java.io.IOException; +import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; @@ -438,6 +439,12 @@ public final class ApkAssets { } } + void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "class=" + getClass()); + pw.println(prefix + "debugName=" + getDebugName()); + pw.println(prefix + "assetPath=" + getAssetPath()); + } + private static native long nativeLoad(@FormatType int format, @NonNull String path, @PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException; private static native long nativeLoadEmpty(@PropertyFlags int flags, diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index bfd9fd0a4ef9..a05f5c927b29 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -43,6 +43,7 @@ import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.PrintWriter; import java.lang.ref.Reference; import java.util.ArrayList; import java.util.Arrays; @@ -1531,6 +1532,15 @@ public final class AssetManager implements AutoCloseable { } } + synchronized void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "class=" + getClass()); + pw.println(prefix + "apkAssets="); + for (int i = 0; i < mApkAssets.length; i++) { + pw.println(prefix + i); + mApkAssets[i].dump(pw, prefix + " "); + } + } + // AssetManager setup native methods. private static native long nativeCreate(); private static native void nativeDestroy(long ptr); diff --git a/core/java/android/content/res/IResourcesManager.aidl b/core/java/android/content/res/IResourcesManager.aidl new file mode 100644 index 000000000000..d1373788f1c5 --- /dev/null +++ b/core/java/android/content/res/IResourcesManager.aidl @@ -0,0 +1,30 @@ +/* + * 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 android.content.res; + +import android.os.RemoteCallback; + +/** + * Api for getting information about resources. + * + * {@hide} + */ +interface IResourcesManager { + boolean dumpResources(in String process, + in ParcelFileDescriptor fd, + in RemoteCallback finishCallback); +}
\ No newline at end of file diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 5fd0d841f0e3..ebef0535f077 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -53,6 +53,7 @@ import android.graphics.drawable.Drawable.ConstantState; import android.graphics.drawable.DrawableInflater; import android.os.Build; import android.os.Bundle; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.AttributeSet; import android.util.DisplayMetrics; @@ -78,11 +79,15 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; +import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; /** * Class for accessing an application's resources. This sits on top of the @@ -172,6 +177,11 @@ public class Resources { private int mBaseApkAssetsSize; + /** @hide */ + private static Set<Resources> sResourcesHistory = Collections.synchronizedSet( + Collections.newSetFromMap( + new WeakHashMap<>())); + /** * Returns the most appropriate default theme for the specified target SDK version. * <ul> @@ -318,6 +328,7 @@ public class Resources { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public Resources(@Nullable ClassLoader classLoader) { mClassLoader = classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader; + sResourcesHistory.add(this); } /** @@ -2649,4 +2660,29 @@ public class Resources { } } } + + /** @hide */ + public void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "class=" + getClass()); + pw.println(prefix + "resourcesImpl"); + mResourcesImpl.dump(pw, prefix + " "); + } + + /** @hide */ + public static void dumpHistory(PrintWriter pw, String prefix) { + pw.println(prefix + "history"); + // Putting into a map keyed on the apk assets to deduplicate resources that are different + // objects but ultimately represent the same assets + Map<List<ApkAssets>, Resources> history = new ArrayMap<>(); + for (Resources r : sResourcesHistory) { + history.put(Arrays.asList(r.mResourcesImpl.mAssets.getApkAssets()), r); + } + int i = 0; + for (Resources r : history.values()) { + if (r != null) { + pw.println(prefix + i++); + r.dump(pw, prefix + " "); + } + } + } } diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 4d850b0ccfd5..ff072916292b 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -61,6 +61,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; +import java.io.PrintWriter; import java.util.Arrays; import java.util.Locale; @@ -1271,6 +1272,12 @@ public class ResourcesImpl { NativeAllocationRegistry.createMalloced(ResourcesImpl.class.getClassLoader(), AssetManager.getThemeFreeFunction()); + void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "class=" + getClass()); + pw.println(prefix + "assets"); + mAssets.dump(pw, prefix + " "); + } + public class ThemeImpl { /** * Unique key for the series of styles applied to this theme. diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 60f5135aed6c..7ff74c68fc53 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -1341,7 +1341,6 @@ public class UsbManager { * * @hide */ - @SystemApi @RequiresPermission(Manifest.permission.MANAGE_USB) boolean resetUsbPort(@NonNull UsbPort port, int operationId, IUsbOperationInternal callback) { diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index f55c41594389..223b8ccf44c8 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -475,7 +475,7 @@ public class InputMethodService extends AbstractInputMethodService { private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations(); @NonNull - final NavigationBarController mNavigationBarController = + private final NavigationBarController mNavigationBarController = new NavigationBarController(this); @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -1504,7 +1504,7 @@ public class InputMethodService extends AbstractInputMethodService { Context.LAYOUT_INFLATER_SERVICE); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow"); mWindow = new SoftInputWindow(this, mTheme, mDispatcherState); - + mNavigationBarController.onSoftInputWindowCreated(mWindow); { final Window window = mWindow.getWindow(); { diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index 508172d13aa3..e5c22e4de08e 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -29,6 +29,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; +import android.graphics.Color; import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; @@ -65,6 +66,9 @@ final class NavigationBarController { @NonNull ViewTreeObserver.InternalInsetsInfo dest) { } + default void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) { + } + default void onViewInitialized() { } @@ -78,9 +82,6 @@ final class NavigationBarController { boolean shouldShowImeSwitcherWhenImeIsShown) { } - default void onSystemBarAppearanceChanged(@Appearance int appearance) { - } - default String toDebugString() { return "No-op implementation"; } @@ -101,6 +102,10 @@ final class NavigationBarController { mImpl.updateTouchableInsets(originalInsets, dest); } + void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) { + mImpl.onSoftInputWindowCreated(softInputWindow); + } + void onViewInitialized() { mImpl.onViewInitialized(); } @@ -117,15 +122,11 @@ final class NavigationBarController { mImpl.setShouldShowImeSwitcherWhenImeIsShown(shouldShowImeSwitcherWhenImeIsShown); } - void onSystemBarAppearanceChanged(@Appearance int appearance) { - mImpl.onSystemBarAppearanceChanged(appearance); - } - String toDebugString() { return mImpl.toDebugString(); } - private static final class Impl implements Callback { + private static final class Impl implements Callback, Window.DecorCallback { private static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700; // Copied from com.android.systemui.animation.Interpolators#LEGACY_DECELERATE @@ -158,6 +159,8 @@ final class NavigationBarController { @Nullable private ValueAnimator mTintAnimator; + private boolean mDrawLegacyNavigationBarBackground; + Impl(@NonNull InputMethodService inputMethodService) { mService = inputMethodService; } @@ -226,9 +229,14 @@ final class NavigationBarController { mLastInsets = systemInsets; } - mNavigationBarFrame.setBackground(null); + if (mDrawLegacyNavigationBarBackground) { + mNavigationBarFrame.setBackgroundColor(Color.BLACK); + } else { + mNavigationBarFrame.setBackground(null); + } - setIconTintInternal(calculateTargetDarkIntensity(mAppearance)); + setIconTintInternal(calculateTargetDarkIntensity(mAppearance, + mDrawLegacyNavigationBarBackground)); } private void uninstallNavigationBarFrameIfNecessary() { @@ -362,6 +370,13 @@ final class NavigationBarController { } @Override + public void onSoftInputWindowCreated(@NonNull SoftInputWindow softInputWindow) { + final Window window = softInputWindow.getWindow(); + mAppearance = window.getSystemBarAppearance(); + window.setDecorCallback(this); + } + + @Override public void onViewInitialized() { if (mDestroyed) { return; @@ -471,7 +486,8 @@ final class NavigationBarController { return; } - final float targetDarkIntensity = calculateTargetDarkIntensity(mAppearance); + final float targetDarkIntensity = calculateTargetDarkIntensity(mAppearance, + mDrawLegacyNavigationBarBackground); if (mTintAnimator != null) { mTintAnimator.cancel(); @@ -499,18 +515,41 @@ final class NavigationBarController { } @FloatRange(from = 0.0f, to = 1.0f) - private static float calculateTargetDarkIntensity(@Appearance int appearance) { - final boolean lightNavBar = (appearance & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0; + private static float calculateTargetDarkIntensity(@Appearance int appearance, + boolean drawLegacyNavigationBarBackground) { + final boolean lightNavBar = !drawLegacyNavigationBarBackground + && (appearance & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0; return lightNavBar ? 1.0f : 0.0f; } @Override + public boolean onDrawLegacyNavigationBarBackgroundChanged( + boolean drawLegacyNavigationBarBackground) { + if (mDestroyed) { + return false; + } + + if (drawLegacyNavigationBarBackground != mDrawLegacyNavigationBarBackground) { + mDrawLegacyNavigationBarBackground = drawLegacyNavigationBarBackground; + if (mDrawLegacyNavigationBarBackground) { + mNavigationBarFrame.setBackgroundColor(Color.BLACK); + } else { + mNavigationBarFrame.setBackground(null); + } + scheduleRelayout(); + onSystemBarAppearanceChanged(mAppearance); + } + return drawLegacyNavigationBarBackground; + } + + @Override public String toDebugString() { return "{mRenderGesturalNavButtons=" + mRenderGesturalNavButtons + " mNavigationBarFrame=" + mNavigationBarFrame + " mShouldShowImeSwitcherWhenImeIsShown" + mShouldShowImeSwitcherWhenImeIsShown + " mAppearance=0x" + Integer.toHexString(mAppearance) + " mDarkIntensity=" + mDarkIntensity + + " mDrawLegacyNavigationBarBackground=" + mDrawLegacyNavigationBarBackground + "}"; } } diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java index 0893d2aad740..5704dac7a327 100644 --- a/core/java/android/inputmethodservice/SoftInputWindow.java +++ b/core/java/android/inputmethodservice/SoftInputWindow.java @@ -31,7 +31,6 @@ import android.util.proto.ProtoOutputStream; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.WindowInsetsController; import android.view.WindowManager; import java.lang.annotation.Retention; @@ -264,11 +263,6 @@ final class SoftInputWindow extends Dialog { } } - @Override - public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) { - mService.mNavigationBarController.onSystemBarAppearanceChanged(appearance); - } - void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); mBounds.dumpDebug(proto, BOUNDS); diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index c936bfa05118..9122adfece53 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -523,9 +523,11 @@ public class NetworkPolicyManager { * * @param subId the subscriber to get the subscription plans for. * @param callingPackage the name of the package making the call. + * @return the active {@link SubscriptionPlan}s for the given subscription id, or + * {@code null} if not found. * @hide */ - @NonNull + @Nullable public SubscriptionPlan[] getSubscriptionPlans(int subId, @NonNull String callingPackage) { try { return mService.getSubscriptionPlans(subId, callingPackage); @@ -538,7 +540,7 @@ public class NetworkPolicyManager { * Get subscription plan for the given networkTemplate. * * @param template the networkTemplate to get the subscription plan for. - * @return the active {@link SubscriptionPlan} for the given template, or + * @return the active {@link SubscriptionPlan}s for the given template, or * {@code null} if not found. * @hide */ diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index df5b7bc237df..4fe6524dee27 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -21,15 +21,16 @@ import android.os.BatterySaverPolicyConfig; import android.os.ParcelDuration; import android.os.PowerSaveState; import android.os.WorkSource; +import android.os.IWakeLockCallback; /** @hide */ interface IPowerManager { void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws, - String historyTag, int displayId); + String historyTag, int displayId, IWakeLockCallback callback); void acquireWakeLockWithUid(IBinder lock, int flags, String tag, String packageName, - int uidtoblame, int displayId); + int uidtoblame, int displayId, IWakeLockCallback callback); @UnsupportedAppUsage void releaseWakeLock(IBinder lock, int flags); void updateWakeLockUids(IBinder lock, in int[] uids); @@ -40,6 +41,7 @@ interface IPowerManager boolean setPowerModeChecked(int mode, boolean enabled); void updateWakeLockWorkSource(IBinder lock, in WorkSource ws, String historyTag); + void updateWakeLockCallback(IBinder lock, IWakeLockCallback callback); boolean isWakeLockLevelSupported(int level); void userActivity(int displayId, long time, int event, int flags); diff --git a/core/java/android/os/IWakeLockCallback.aidl b/core/java/android/os/IWakeLockCallback.aidl new file mode 100644 index 000000000000..89615d22421e --- /dev/null +++ b/core/java/android/os/IWakeLockCallback.aidl @@ -0,0 +1,24 @@ +/* + * 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 android.os; + +/** + * @hide + */ +oneway interface IWakeLockCallback { + oneway void onStateChanged(boolean enabled); +} diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 5bd8588d8970..315eef78724e 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -2770,6 +2770,23 @@ public final class PowerManager { public static final int PRE_IDLE_TIMEOUT_MODE_SHORT = 2; /** + * A listener interface to get notified when the wakelock is enabled/disabled. + */ + public interface WakeLockStateListener { + /** + * Frameworks could disable the wakelock because either device's power allowlist has + * changed, or the app's wakelock has exceeded its quota, or the app goes into cached + * state. + * <p> + * This callback is called whenever the wakelock's state has changed. + * </p> + * + * @param enabled true is enabled, false is disabled. + */ + void onStateChanged(boolean enabled); + } + + /** * A wake lock is a mechanism to indicate that your application needs * to have the device stay on. * <p> @@ -2800,6 +2817,8 @@ public final class PowerManager { private String mHistoryTag; private final String mTraceName; private final int mDisplayId; + private WakeLockStateListener mListener; + private IWakeLockCallback mCallback; private final Runnable mReleaser = () -> release(RELEASE_FLAG_TIMEOUT); @@ -2890,7 +2909,7 @@ public final class PowerManager { Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0); try { mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource, - mHistoryTag, mDisplayId); + mHistoryTag, mDisplayId, mCallback); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3083,6 +3102,45 @@ public final class PowerManager { } }; } + + /** + * Set the listener to get notified when the wakelock is enabled/disabled. + * + * @param executor {@link Executor} to handle listener callback. + * @param listener listener to be added, set the listener to null to cancel a listener. + */ + public void setStateListener(@NonNull @CallbackExecutor Executor executor, + @Nullable WakeLockStateListener listener) { + Preconditions.checkNotNull(executor, "executor cannot be null"); + synchronized (mToken) { + if (listener != mListener) { + mListener = listener; + if (listener != null) { + mCallback = new IWakeLockCallback.Stub() { + public void onStateChanged(boolean enabled) { + final long token = Binder.clearCallingIdentity(); + try { + executor.execute(() -> { + listener.onStateChanged(enabled); + }); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + } else { + mCallback = null; + } + if (mHeld) { + try { + mService.updateWakeLockCallback(mToken, mCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + } + } } /** diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index 9598410d8f33..21c64876c24c 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -410,9 +410,9 @@ public abstract class VibrationEffect implements Parcelable { * * <p>The waveform will start the first transition from the vibrator off state, with the * resonant frequency by default. To provide an initial state, use - * {@link #startWaveform(VibrationParameter)}. + * {@link #startWaveform(VibrationEffect.VibrationParameter)}. * - * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters. + * @see VibrationEffect.WaveformBuilder */ @NonNull public static WaveformBuilder startWaveform() { @@ -421,14 +421,16 @@ public abstract class VibrationEffect implements Parcelable { /** * Start building a waveform vibration with an initial state specified by a - * {@link VibrationParameter}. + * {@link VibrationEffect.VibrationParameter}. * * <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing * control over vibration amplitude and frequency via smooth transitions between values. * - * @param initialParameter The initial {@link VibrationParameter} value to be applied at the - * beginning of the vibration. + * @param initialParameter The initial {@link VibrationEffect.VibrationParameter} value to be + * applied at the beginning of the vibration. * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters. + * + * @see VibrationEffect.WaveformBuilder */ @NonNull public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter) { @@ -439,17 +441,19 @@ public abstract class VibrationEffect implements Parcelable { /** * Start building a waveform vibration with an initial state specified by two - * {@link VibrationParameter VibrationParameters}. + * {@link VibrationEffect.VibrationParameter VibrationParameters}. * * <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing * control over vibration amplitude and frequency via smooth transitions between values. * - * @param initialParameter1 The initial {@link VibrationParameter} value to be applied at the - * beginning of the vibration. - * @param initialParameter2 The initial {@link VibrationParameter} value to be applied at the - * beginning of the vibration, must be a different type of parameter - * than the one specified by the first argument. + * @param initialParameter1 The initial {@link VibrationEffect.VibrationParameter} value to be + * applied at the beginning of the vibration. + * @param initialParameter2 The initial {@link VibrationEffect.VibrationParameter} value to be + * applied at the beginning of the vibration, must be a different type + * of parameter than the one specified by the first argument. * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters. + * + * @see VibrationEffect.WaveformBuilder */ @NonNull public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter1, @@ -805,7 +809,46 @@ public abstract class VibrationEffect implements Parcelable { } /** - * A composition of haptic primitives that, when combined, create a single haptic effect. + * A composition of haptic elements that are combined to be playable as a single + * {@link VibrationEffect}. + * + * <p>The haptic primitives are available as {@code Composition.PRIMITIVE_*} constants and + * can be added to a composition to create a custom vibration effect. Here is an example of an + * effect that grows in intensity and then dies off, with a longer rising portion for emphasis + * and an extra tick 100ms after: + * + * <code> + * VibrationEffect effect = VibrationEffect.startComposition() + * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 0.5f) + * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.5f) + * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1.0f, 100) + * .compose(); + * </code> + * + * <p>Composition elements can also be {@link VibrationEffect} instances, including other + * compositions, and off durations, which are periods of time when the vibrator will be + * turned off. Here is an example of a composition that "warms up" with a light tap, + * a stronger double tap, then repeats a vibration pattern indefinitely: + * + * <code> + * VibrationEffect repeatingEffect = VibrationEffect.startComposition() + * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK) + * .addOffDuration(Duration.ofMillis(10)) + * .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK)) + * .addOffDuration(Duration.ofMillis(50)) + * .addEffect(VibrationEffect.createWaveform(pattern, repeatIndex)) + * .compose(); + * </code> + * + * <p>When choosing to play a composed effect, you should check that individual components are + * supported by the device by using the appropriate vibrator method: + * + * <ul> + * <li>Primitive support can be checked using {@link Vibrator#arePrimitivesSupported}. + * <li>Effect support can be checked using {@link Vibrator#areEffectsSupported}. + * <li>Amplitude control for one-shot and waveforms with amplitude values can be checked + * using {@link Vibrator#hasAmplitudeControl}. + * </ul> * * @see VibrationEffect#startComposition() */ @@ -1091,16 +1134,77 @@ public abstract class VibrationEffect implements Parcelable { * A builder for waveform haptic effects. * * <p>Waveform vibrations constitute of one or more timed transitions to new sets of vibration - * parameters. These parameters can be the vibration amplitude or frequency, for example. + * parameters. These parameters can be the vibration amplitude, frequency, or both. + * + * <p>The following example ramps a vibrator turned off to full amplitude at 120Hz, over 100ms + * starting at 60Hz, then holds that state for 200ms and ramps back down again over 100ms: + * + * <code> + * import static android.os.VibrationEffect.VibrationParameter.targetAmplitude; + * import static android.os.VibrationEffect.VibrationParameter.targetFrequency; + * + * VibrationEffect effect = VibrationEffect.startWaveform(targetFrequency(60)) + * .addTransition(Duration.ofMillis(100), targetAmplitude(1), targetFrequency(120)) + * .addSustain(Duration.ofMillis(200)) + * .addTransition(Duration.ofMillis(100), targetAmplitude(0), targetFrequency(60)) + * .build(); + * </code> + * + * <p>The initial state of the waveform can be set via + * {@link VibrationEffect#startWaveform(VibrationParameter)} or + * {@link VibrationEffect#startWaveform(VibrationParameter, VibrationParameter)}. If the initial + * parameters are not set then the {@link WaveformBuilder} will start with the vibrator off, + * represented by zero amplitude, at the vibrator's resonant frequency. + * + * <p>Repeating waveforms can be created by building the repeating block separately and adding + * it to the end of a composition with + * {@link Composition#repeatEffectIndefinitely(VibrationEffect)}: * * <p>Note that physical vibration actuators have different reaction times for changing * amplitude and frequency. Durations specified here represent a timeline for the target * parameters, and quality of effects may be improved if the durations allow time for a * transition to be smoothly applied. * - * <p>Repeating waveforms can be built by constructing the repeating block separately and adding - * it to the end of a composition using - * {@link Composition#repeatEffectIndefinitely(VibrationEffect)}. + * <p>The following example illustrates both an initial state and a repeating section, using + * a {@link VibrationEffect.Composition}. The resulting effect will have a tick followed by a + * repeated beating effect with a rise that stretches out and a sharp finish. + * + * <code> + * VibrationEffect patternToBeRepeated = VibrationEffect.startWaveform(targetAmplitude(0.2f)) + * .addSustain(Duration.ofMillis(10)) + * .addTransition(Duration.ofMillis(20), targetAmplitude(0.4f)) + * .addSustain(Duration.ofMillis(30)) + * .addTransition(Duration.ofMillis(40), targetAmplitude(0.8f)) + * .addSustain(Duration.ofMillis(50)) + * .addTransition(Duration.ofMillis(60), targetAmplitude(0.2f)) + * .build(); + * + * VibrationEffect effect = VibrationEffect.startComposition() + * .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK) + * .addOffDuration(Duration.ofMillis(20)) + * .repeatEffectIndefinitely(patternToBeRepeated) + * .compose(); + * </code> + * + * <p>The amplitude step waveforms that can be created via + * {@link VibrationEffect#createWaveform(long[], int[], int)} can also be created with + * {@link WaveformBuilder} by adding zero duration transitions: + * + * <code> + * // These two effects are the same + * VibrationEffect waveform = VibrationEffect.createWaveform( + * new long[] { 10, 20, 30 }, // timings in milliseconds + * new int[] { 51, 102, 204 }, // amplitudes in [0,255] + * -1); // repeat index + * + * VibrationEffect sameWaveform = VibrationEffect.startWaveform(targetAmplitude(0.2f)) + * .addSustain(Duration.ofMillis(10)) + * .addTransition(Duration.ZERO, targetAmplitude(0.4f)) + * .addSustain(Duration.ofMillis(20)) + * .addTransition(Duration.ZERO, targetAmplitude(0.8f)) + * .addSustain(Duration.ofMillis(30)) + * .build(); + * </code> * * @see VibrationEffect#startWaveform */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4758d838468a..c4fe1a44d161 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2436,6 +2436,23 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_MMS_MESSAGE_SETTING = "android.settings.MMS_MESSAGE_SETTING"; + /** + * Activity Action: Show a screen of bedtime settings, which is provided by the wellbeing app. + * <p> + * The handler of this intent action may not exist. + * <p> + * To start an activity with this intent, apps should set the wellbeing package explicitly in + * the intent together with this action. The wellbeing package is defined in + * {@code com.android.internal.R.string.config_defaultWellbeingPackage}. + * <p> + * Output: Nothing + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_BEDTIME_SETTINGS = "android.settings.BEDTIME_SETTINGS"; + // End of Intent actions for Settings /** @@ -10048,6 +10065,13 @@ public final class Settings { public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles"; /** + * The duration of timeout, in milliseconds, to switch from a non-primary user to the + * primary user when the device is docked. + * @hide + */ + public static final String TIMEOUT_TO_USER_ZERO = "timeout_to_user_zero"; + + /** * Backup manager behavioral parameters. * This is encoded as a key=value list, separated by commas. Ex: * @@ -10281,6 +10305,15 @@ public final class Settings { public static final String NEARBY_SHARING_SLICE_URI = "nearby_sharing_slice_uri"; /** + * Current provider of Fast Pair saved devices page. + * Default value in @string/config_defaultNearbyFastPairSettingsDevicesComponent. + * No VALIDATOR as this setting will not be backed up. + * @hide + */ + public static final String NEARBY_FAST_PAIR_SETTINGS_DEVICES_COMPONENT = + "nearby_fast_pair_settings_devices_component"; + + /** * Controls whether aware is enabled. * @hide */ diff --git a/core/java/android/service/games/GameService.java b/core/java/android/service/games/GameService.java index 870a7e3f2646..9df83587f125 100644 --- a/core/java/android/service/games/GameService.java +++ b/core/java/android/service/games/GameService.java @@ -16,9 +16,11 @@ package android.service.games; +import android.Manifest; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.app.IGameManagerService; @@ -173,6 +175,7 @@ public class GameService extends Service { * * @param taskId The taskId of the game. */ + @RequiresPermission(Manifest.permission.MANAGE_GAME_ACTIVITY) public final void createGameSession(@IntRange(from = 0) int taskId) { if (mGameServiceController == null) { throw new IllegalStateException("Can not call before connected()"); diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java index f4baedc18acf..9590933cf2da 100644 --- a/core/java/android/service/games/GameSession.java +++ b/core/java/android/service/games/GameSession.java @@ -278,7 +278,7 @@ public abstract class GameSession { * * @return {@code true} if the game was successfully restarted; otherwise, {@code false}. */ - @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) + @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final boolean restartGame() { try { mGameSessionController.restartGame(mTaskId); diff --git a/core/java/android/view/IDisplayWindowListener.aidl b/core/java/android/view/IDisplayWindowListener.aidl index 449e9b325904..67ae7430e0b7 100644 --- a/core/java/android/view/IDisplayWindowListener.aidl +++ b/core/java/android/view/IDisplayWindowListener.aidl @@ -63,5 +63,5 @@ oneway interface IDisplayWindowListener { /** * Called when the keep clear ares on a display have changed. */ - void onKeepClearAreasChanged(int displayId, in List<Rect> keepClearAreas); + void onKeepClearAreasChanged(int displayId, in List<Rect> restricted, in List<Rect> unrestricted); } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 039b50a637be..8401b7cdb243 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -295,6 +295,9 @@ public abstract class Window { private OnWindowDismissedCallback mOnWindowDismissedCallback; private OnWindowSwipeDismissedCallback mOnWindowSwipeDismissedCallback; private WindowControllerCallback mWindowControllerCallback; + @WindowInsetsController.Appearance + private int mSystemBarAppearance; + private DecorCallback mDecorCallback; private OnRestrictedCaptionAreaChangedListener mOnRestrictedCaptionAreaChangedListener; private Rect mRestrictedCaptionAreaRect; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -607,17 +610,6 @@ public abstract class Window { * @param hasCapture True if the window has pointer capture. */ default public void onPointerCaptureChanged(boolean hasCapture) { }; - - /** - * Called from - * {@link com.android.internal.policy.DecorView#onSystemBarAppearanceChanged(int)}. - * - * @param appearance The newly applied appearance. - * @hide - */ - default void onSystemBarAppearanceChanged( - @WindowInsetsController.Appearance int appearance) { - } } /** @hide */ @@ -672,6 +664,35 @@ public abstract class Window { void updateNavigationBarColor(int color); } + /** @hide */ + public interface DecorCallback { + /** + * Called from + * {@link com.android.internal.policy.DecorView#onSystemBarAppearanceChanged(int)}. + * + * @param appearance The newly applied appearance. + */ + void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance); + + /** + * Called from + * {@link com.android.internal.policy.DecorView#updateColorViews(WindowInsets, boolean)} + * when {@link com.android.internal.policy.DecorView#mDrawLegacyNavigationBarBackground} is + * being updated. + * + * @param drawLegacyNavigationBarBackground the new value that is being set to + * {@link com.android.internal.policy.DecorView#mDrawLegacyNavigationBarBackground}. + * @return The value to be set to + * {@link com.android.internal.policy.DecorView#mDrawLegacyNavigationBarBackgroundHandled} + * on behalf of the {@link com.android.internal.policy.DecorView}. + * {@code true} to tell that the Window can render the legacy navigation bar + * background on behalf of the {@link com.android.internal.policy.DecorView}. + * {@code false} to let {@link com.android.internal.policy.DecorView} handle it. + */ + boolean onDrawLegacyNavigationBarBackgroundChanged( + boolean drawLegacyNavigationBarBackground); + } + /** * Callback for clients that want to be aware of where caption draws content. */ @@ -996,6 +1017,36 @@ public abstract class Window { return mWindowControllerCallback; } + /** @hide */ + public final void setDecorCallback(DecorCallback decorCallback) { + mDecorCallback = decorCallback; + } + + /** @hide */ + @WindowInsetsController.Appearance + public final int getSystemBarAppearance() { + return mSystemBarAppearance; + } + + /** @hide */ + public final void dispatchOnSystemBarAppearanceChanged( + @WindowInsetsController.Appearance int appearance) { + mSystemBarAppearance = appearance; + if (mDecorCallback != null) { + mDecorCallback.onSystemBarAppearanceChanged(appearance); + } + } + + /** @hide */ + public final boolean onDrawLegacyNavigationBarBackgroundChanged( + boolean drawLegacyNavigationBarBackground) { + if (mDecorCallback == null) { + return false; + } + return mDecorCallback.onDrawLegacyNavigationBarBackgroundChanged( + drawLegacyNavigationBarBackground); + } + /** * Set a callback for changes of area where caption will draw its content. * diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java index 115e9e891408..02c8945d9fce 100644 --- a/core/java/android/view/WindowCallbackWrapper.java +++ b/core/java/android/view/WindowCallbackWrapper.java @@ -163,10 +163,5 @@ public class WindowCallbackWrapper implements Window.Callback { public void onPointerCaptureChanged(boolean hasCapture) { mWrapped.onPointerCaptureChanged(hasCapture); } - - @Override - public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) { - mWrapped.onSystemBarAppearanceChanged(appearance); - } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 41c540116928..7c939aca09cf 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4461,7 +4461,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * pixel" units. This size is adjusted based on the current density and * user font size preference. * - * <p>Note: if this TextView has the auto-size feature enabled than this function is no-op. + * <p>Note: if this TextView has the auto-size feature enabled, then this function is no-op. * * @param size The scaled pixel size. * @@ -4476,7 +4476,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * Set the default text size to a given unit and value. See {@link * TypedValue} for the possible dimension units. * - * <p>Note: if this TextView has the auto-size feature enabled than this function is no-op. + * <p>Note: if this TextView has the auto-size feature enabled, then this function is no-op. * * @param unit The desired dimension unit. * @param size The desired size in the given units. diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/InstallLocationUtils.java index c2f20526f8fd..c456cf3a4bb6 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/InstallLocationUtils.java @@ -16,6 +16,7 @@ package com.android.internal.content; +import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL; import android.content.Context; @@ -53,7 +54,7 @@ import java.util.UUID; * and media container service transports. * Some utility methods to invoke StorageManagerService api. */ -public class PackageHelper { +public class InstallLocationUtils { public static final int RECOMMEND_INSTALL_INTERNAL = 1; public static final int RECOMMEND_INSTALL_EXTERNAL = 2; public static final int RECOMMEND_INSTALL_EPHEMERAL = 3; @@ -89,9 +90,13 @@ public class PackageHelper { */ public static abstract class TestableInterface { abstract public StorageManager getStorageManager(Context context); + abstract public boolean getForceAllowOnExternalSetting(Context context); + abstract public boolean getAllow3rdPartyOnInternalConfig(Context context); + abstract public ApplicationInfo getExistingAppInfo(Context context, String packageName); + abstract public File getDataDirectory(); } @@ -150,11 +155,11 @@ public class PackageHelper { /** * Given a requested {@link PackageInfo#installLocation} and calculated * install size, pick the actual volume to install the app. Only considers - * internal and private volumes, and prefers to keep an existing package on + * internal and private volumes, and prefers to keep an existing package onocation * its current volume. * * @return the {@link VolumeInfo#fsUuid} to install onto, or {@code null} - * for internal storage. + * for internal storage. */ public static String resolveInstallVolume(Context context, SessionParams params) throws IOException { @@ -316,21 +321,6 @@ public class PackageHelper { && params.sizeBytes <= storage.getStorageBytesUntilLow(primary.getPathFile()); } - @Deprecated - public static int resolveInstallLocation(Context context, String packageName, - int installLocation, long sizeBytes, int installFlags) { - final SessionParams params = new SessionParams(SessionParams.MODE_INVALID); - params.appPackageName = packageName; - params.installLocation = installLocation; - params.sizeBytes = sizeBytes; - params.installFlags = installFlags; - try { - return resolveInstallLocation(context, params); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - /** * Given a requested {@link PackageInfo#installLocation} and calculated * install size, pick the actual location to install the app. @@ -393,24 +383,24 @@ public class PackageHelper { // and will fall through to return INSUFFICIENT_STORAGE if (fitsOnInternal) { return (ephemeral) - ? PackageHelper.RECOMMEND_INSTALL_EPHEMERAL - : PackageHelper.RECOMMEND_INSTALL_INTERNAL; + ? InstallLocationUtils.RECOMMEND_INSTALL_EPHEMERAL + : InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL; } } else if (prefer == RECOMMEND_INSTALL_EXTERNAL) { if (fitsOnExternal) { - return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; + return InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL; } } if (checkBoth) { if (fitsOnInternal) { - return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL; } else if (fitsOnExternal) { - return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; + return InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL; } } - return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; + return InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } @Deprecated @@ -476,4 +466,48 @@ public class PackageHelper { return 0; } } + + public static int installLocationPolicy(int installLocation, int recommendedInstallLocation, + int installFlags, boolean installedPkgIsSystem, boolean installedPackageOnExternal) { + if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) == 0) { + // Invalid install. Return error code + return RECOMMEND_FAILED_ALREADY_EXISTS; + } + // Check for updated system application. + if (installedPkgIsSystem) { + return RECOMMEND_INSTALL_INTERNAL; + } + // If current upgrade specifies particular preference + if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { + // Application explicitly specified internal. + return RECOMMEND_INSTALL_INTERNAL; + } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { + // App explicitly prefers external. Let policy decide + return recommendedInstallLocation; + } else { + // Prefer previous location + if (installedPackageOnExternal) { + return RECOMMEND_INSTALL_EXTERNAL; + } + return RECOMMEND_INSTALL_INTERNAL; + } + } + + public static int getInstallationErrorCode(int loc) { + if (loc == RECOMMEND_FAILED_INVALID_LOCATION) { + return PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; + } else if (loc == RECOMMEND_FAILED_ALREADY_EXISTS) { + return PackageManager.INSTALL_FAILED_ALREADY_EXISTS; + } else if (loc == RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + } else if (loc == RECOMMEND_FAILED_INVALID_APK) { + return PackageManager.INSTALL_FAILED_INVALID_APK; + } else if (loc == RECOMMEND_FAILED_INVALID_URI) { + return PackageManager.INSTALL_FAILED_INVALID_URI; + } else if (loc == RECOMMEND_MEDIA_UNAVAILABLE) { + return PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; + } else { + return INSTALL_SUCCEEDED; + } + } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 4fc977f670f0..c0fec62bdd94 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -2013,17 +2013,15 @@ public class BatteryStatsImpl extends BatteryStats { private final TimeBase mTimeBase; private final LongArrayMultiStateCounter mCounter; - private TimeInFreqMultiStateCounter(TimeBase timeBase, Parcel in, long timestampMs) { - mTimeBase = timeBase; - mCounter = LongArrayMultiStateCounter.CREATOR.createFromParcel(in); - mCounter.setEnabled(mTimeBase.isRunning(), timestampMs); - timeBase.add(this); + private TimeInFreqMultiStateCounter(TimeBase timeBase, int stateCount, int cpuFreqCount, + long timestampMs) { + this(timeBase, new LongArrayMultiStateCounter(stateCount, cpuFreqCount), timestampMs); } - private TimeInFreqMultiStateCounter(TimeBase timeBase, int stateCount, int cpuFreqCount, + private TimeInFreqMultiStateCounter(TimeBase timeBase, LongArrayMultiStateCounter counter, long timestampMs) { mTimeBase = timeBase; - mCounter = new LongArrayMultiStateCounter(stateCount, cpuFreqCount); + mCounter = counter; mCounter.setEnabled(mTimeBase.isRunning(), timestampMs); timeBase.add(this); } @@ -2032,6 +2030,19 @@ public class BatteryStatsImpl extends BatteryStats { mCounter.writeToParcel(out, 0); } + @Nullable + private static TimeInFreqMultiStateCounter readFromParcel(Parcel in, TimeBase timeBase, + int stateCount, int cpuFreqCount, long timestampMs) { + // Read the object from the Parcel, whether it's usable or not + LongArrayMultiStateCounter counter = + LongArrayMultiStateCounter.CREATOR.createFromParcel(in); + if (counter.getStateCount() != stateCount + || counter.getArrayLength() != cpuFreqCount) { + return null; + } + return new TimeInFreqMultiStateCounter(timeBase, counter, timestampMs); + } + @Override public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) { mCounter.setEnabled(true, elapsedRealtimeUs / 1000); @@ -10784,25 +10795,18 @@ public class BatteryStatsImpl extends BatteryStats { stateCount = in.readInt(); if (stateCount != 0) { - // Read the object from the Parcel, whether it's usable or not - TimeInFreqMultiStateCounter counter = new TimeInFreqMultiStateCounter( - mBsi.mOnBatteryTimeBase, in, timestampMs); - if (stateCount == PROC_STATE_TIME_COUNTER_STATE_COUNT) { - mProcStateTimeMs = counter; - } + mProcStateTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in, + mBsi.mOnBatteryTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT, + mBsi.getCpuFreqCount(), mBsi.mClock.elapsedRealtime()); } else { mProcStateTimeMs = null; } stateCount = in.readInt(); if (stateCount != 0) { - // Read the object from the Parcel, whether it's usable or not - TimeInFreqMultiStateCounter counter = - new TimeInFreqMultiStateCounter( - mBsi.mOnBatteryScreenOffTimeBase, in, timestampMs); - if (stateCount == PROC_STATE_TIME_COUNTER_STATE_COUNT) { - mProcStateScreenOffTimeMs = counter; - } + mProcStateScreenOffTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in, + mBsi.mOnBatteryScreenOffTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT, + mBsi.getCpuFreqCount(), mBsi.mClock.elapsedRealtime()); } else { mProcStateScreenOffTimeMs = null; } @@ -16361,6 +16365,11 @@ public class BatteryStatsImpl extends BatteryStats { BATTERY_CHARGED_DELAY_MS = delay >= 0 ? delay : mParser.getInt( KEY_BATTERY_CHARGED_DELAY_MS, DEFAULT_BATTERY_CHARGED_DELAY_MS); + + if (mHandler.hasCallbacks(mDeferSetCharging)) { + mHandler.removeCallbacks(mDeferSetCharging); + mHandler.postDelayed(mDeferSetCharging, BATTERY_CHARGED_DELAY_MS); + } } private void updateKernelUidReadersThrottleTime(long oldTimeMs, long newTimeMs) { @@ -17160,12 +17169,10 @@ public class BatteryStatsImpl extends BatteryStats { stateCount = in.readInt(); if (stateCount != 0) { - // Read the object from the Parcel, whether it's usable or not - TimeInFreqMultiStateCounter counter = new TimeInFreqMultiStateCounter( - mOnBatteryTimeBase, in, mClock.elapsedRealtime()); - if (stateCount == PROC_STATE_TIME_COUNTER_STATE_COUNT) { - u.mProcStateTimeMs = counter; - } + detachIfNotNull(u.mProcStateTimeMs); + u.mProcStateTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in, + mOnBatteryTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT, + getCpuFreqCount(), mClock.elapsedRealtime()); } detachIfNotNull(u.mProcStateScreenOffTimeMs); @@ -17174,13 +17181,9 @@ public class BatteryStatsImpl extends BatteryStats { stateCount = in.readInt(); if (stateCount != 0) { detachIfNotNull(u.mProcStateScreenOffTimeMs); - // Read the object from the Parcel, whether it's usable or not - TimeInFreqMultiStateCounter counter = - new TimeInFreqMultiStateCounter( - mOnBatteryScreenOffTimeBase, in, mClock.elapsedRealtime()); - if (stateCount == PROC_STATE_TIME_COUNTER_STATE_COUNT) { - u.mProcStateScreenOffTimeMs = counter; - } + u.mProcStateScreenOffTimeMs = TimeInFreqMultiStateCounter.readFromParcel(in, + mOnBatteryScreenOffTimeBase, PROC_STATE_TIME_COUNTER_STATE_COUNT, + getCpuFreqCount(), mClock.elapsedRealtime()); } if (in.readInt() != 0) { diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index a50282ee0aa6..2925341cd948 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -285,6 +285,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private Insets mBackgroundInsets = Insets.NONE; private Insets mLastBackgroundInsets = Insets.NONE; private boolean mDrawLegacyNavigationBarBackground; + private boolean mDrawLegacyNavigationBarBackgroundHandled; private PendingInsetsController mPendingInsetsController = new PendingInsetsController(); @@ -1035,10 +1036,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) { updateColorViews(null /* insets */, true /* animate */); if (mWindow != null) { - final Window.Callback callback = mWindow.getCallback(); - if (callback != null) { - callback.onSystemBarAppearanceChanged(appearance); - } + mWindow.dispatchOnSystemBarAppearanceChanged(appearance); } } @@ -1174,6 +1172,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0; if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) { + mDrawLegacyNavigationBarBackgroundHandled = + mWindow.onDrawLegacyNavigationBarBackgroundChanged( + mDrawLegacyNavigationBarBackground); if (viewRoot != null) { viewRoot.requestInvalidateRootRenderNode(); } @@ -1266,7 +1267,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } - if (forceConsumingNavBar) { + if (forceConsumingNavBar && !mDrawLegacyNavigationBarBackgroundHandled) { mBackgroundInsets = Insets.of(mLastLeftInset, 0, mLastRightInset, mLastBottomInset); } else { mBackgroundInsets = Insets.NONE; @@ -2491,7 +2492,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } private void drawLegacyNavigationBarBackground(RecordingCanvas canvas) { - if (!mDrawLegacyNavigationBarBackground) { + if (!mDrawLegacyNavigationBarBackground || mDrawLegacyNavigationBarBackgroundHandled) { return; } View v = mNavigationColorViewState.view; diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java index faea7706ee14..ece6f2f3571b 100644 --- a/core/java/com/android/internal/policy/TransitionAnimation.java +++ b/core/java/com/android/internal/policy/TransitionAnimation.java @@ -29,7 +29,6 @@ import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN; import static android.view.WindowManager.TRANSIT_OPEN; -import android.annotation.DrawableRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -915,7 +914,7 @@ public class TransitionAnimation { * animation. */ public HardwareBuffer createCrossProfileAppsThumbnail( - @DrawableRes int thumbnailDrawableRes, Rect frame) { + Drawable thumbnailDrawable, Rect frame) { final int width = frame.width(); final int height = frame.height(); @@ -924,14 +923,13 @@ public class TransitionAnimation { canvas.drawColor(Color.argb(0.6f, 0, 0, 0)); final int thumbnailSize = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.cross_profile_apps_thumbnail_size); - final Drawable drawable = mContext.getDrawable(thumbnailDrawableRes); - drawable.setBounds( + thumbnailDrawable.setBounds( (width - thumbnailSize) / 2, (height - thumbnailSize) / 2, (width + thumbnailSize) / 2, (height + thumbnailSize) / 2); - drawable.setTint(mContext.getColor(android.R.color.white)); - drawable.draw(canvas); + thumbnailDrawable.setTint(mContext.getColor(android.R.color.white)); + thumbnailDrawable.draw(canvas); picture.endRecording(); return Bitmap.createBitmap(picture).getHardwareBuffer(); diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 430d84ebb3e1..8bb9a0a0d6ff 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -195,7 +195,6 @@ cc_library_shared { "android_util_FileObserver.cpp", "android/opengl/poly_clip.cpp", // TODO: .arm "android/opengl/util.cpp", - "android_server_NetworkManagementSocketTagger.cpp", "android_ddm_DdmHandleNativeHeap.cpp", "android_backup_BackupDataInput.cpp", "android_backup_BackupDataOutput.cpp", @@ -310,6 +309,8 @@ cc_library_shared { "libdl_android", "libtimeinstate", "server_configurable_flags", + // TODO: delete when ConnectivityT moves to APEX. + "libframework-connectivity-tiramisu-jni", ], export_shared_lib_headers: [ // our headers include libnativewindow's public headers diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index f4296becf484..cde71cf6b30f 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -164,7 +164,6 @@ extern int register_android_text_AndroidCharacter(JNIEnv *env); extern int register_android_text_Hyphenator(JNIEnv *env); extern int register_android_opengl_classes(JNIEnv *env); extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env); -extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env); extern int register_android_backup_BackupDataInput(JNIEnv *env); extern int register_android_backup_BackupDataOutput(JNIEnv *env); extern int register_android_backup_FileBackupHelperBase(JNIEnv *env); @@ -1623,7 +1622,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_media_midi), REG_JNI(register_android_opengl_classes), - REG_JNI(register_android_server_NetworkManagementSocketTagger), REG_JNI(register_android_ddm_DdmHandleNativeHeap), REG_JNI(register_android_backup_BackupDataInput), REG_JNI(register_android_backup_BackupDataOutput), diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 6b82ba80a26a..edc8c5b99ebe 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -204,6 +204,12 @@ jclass gClsAudioRecordRoutingProxy; jclass gAudioProfileClass; jmethodID gAudioProfileCstor; +static struct { + jfieldID mSamplingRates; + jfieldID mChannelMasks; + jfieldID mChannelIndexMasks; + jfieldID mEncapsulationType; +} gAudioProfileFields; jclass gVibratorClass; static struct { @@ -1340,81 +1346,50 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, jStatus = (jint)AUDIO_JAVA_ERROR; goto exit; } - for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) { - size_t numPositionMasks = 0; - size_t numIndexMasks = 0; - // count up how many masks are positional and indexed - for (size_t index = 0; index < nAudioPort->audio_profiles[i].num_channel_masks; index++) { - const audio_channel_mask_t mask = nAudioPort->audio_profiles[i].channel_masks[index]; - if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) { - numIndexMasks++; - } else { - numPositionMasks++; - } - } - ScopedLocalRef<jintArray> jSamplingRates(env, - env->NewIntArray(nAudioPort->audio_profiles[i] - .num_sample_rates)); - ScopedLocalRef<jintArray> jChannelMasks(env, env->NewIntArray(numPositionMasks)); - ScopedLocalRef<jintArray> jChannelIndexMasks(env, env->NewIntArray(numIndexMasks)); - if (!jSamplingRates.get() || !jChannelMasks.get() || !jChannelIndexMasks.get()) { + for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) { + jobject jAudioProfile = nullptr; + jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &nAudioPort->audio_profiles[i], + useInMask); + if (jStatus != NO_ERROR) { jStatus = (jint)AUDIO_JAVA_ERROR; goto exit; } + env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, jAudioProfile); - if (nAudioPort->audio_profiles[i].num_sample_rates) { - env->SetIntArrayRegion(jSamplingRates.get(), 0 /*start*/, - nAudioPort->audio_profiles[i].num_sample_rates, - (jint *)nAudioPort->audio_profiles[i].sample_rates); - } - - // put the masks in the output arrays - for (size_t maskIndex = 0, posMaskIndex = 0, indexedMaskIndex = 0; - maskIndex < nAudioPort->audio_profiles[i].num_channel_masks; maskIndex++) { - const audio_channel_mask_t mask = - nAudioPort->audio_profiles[i].channel_masks[maskIndex]; - if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) { - jint jMask = audio_channel_mask_get_bits(mask); - env->SetIntArrayRegion(jChannelIndexMasks.get(), indexedMaskIndex++, 1, &jMask); - } else { - jint jMask = - useInMask ? inChannelMaskFromNative(mask) : outChannelMaskFromNative(mask); - env->SetIntArrayRegion(jChannelMasks.get(), posMaskIndex++, 1, &jMask); - } - } - - int encapsulationType; - if (audioEncapsulationTypeFromNative(nAudioPort->audio_profiles[i].encapsulation_type, - &encapsulationType) != NO_ERROR) { - ALOGW("Unknown encapsualtion type for JAVA API: %u", - nAudioPort->audio_profiles[i].encapsulation_type); - continue; - } - - ScopedLocalRef<jobject> - jAudioProfile(env, - env->NewObject(gAudioProfileClass, gAudioProfileCstor, - audioFormatFromNative( - nAudioPort->audio_profiles[i].format), - jSamplingRates.get(), jChannelMasks.get(), - jChannelIndexMasks.get(), encapsulationType)); - if (jAudioProfile == nullptr) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; - } - env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, jAudioProfile.get()); if (nAudioPort->audio_profiles[i].format == AUDIO_FORMAT_PCM_FLOAT) { hasFloat = true; } else if (jPcmFloatProfileFromExtendedInteger.get() == nullptr && audio_is_linear_pcm(nAudioPort->audio_profiles[i].format) && audio_bytes_per_sample(nAudioPort->audio_profiles[i].format) > 2) { + ScopedLocalRef<jintArray> + jSamplingRates(env, + (jintArray) + env->GetObjectField(jAudioProfile, + gAudioProfileFields.mSamplingRates)); + ScopedLocalRef<jintArray> + jChannelMasks(env, + (jintArray) + env->GetObjectField(jAudioProfile, + gAudioProfileFields.mChannelMasks)); + ScopedLocalRef<jintArray> + jChannelIndexMasks(env, + (jintArray)env->GetObjectField(jAudioProfile, + gAudioProfileFields + .mChannelIndexMasks)); + int encapsulationType = + env->GetIntField(jAudioProfile, gAudioProfileFields.mEncapsulationType); + jPcmFloatProfileFromExtendedInteger.reset( env->NewObject(gAudioProfileClass, gAudioProfileCstor, audioFormatFromNative(AUDIO_FORMAT_PCM_FLOAT), jSamplingRates.get(), jChannelMasks.get(), jChannelIndexMasks.get(), encapsulationType)); } + + if (jAudioProfile != nullptr) { + env->DeleteLocalRef(jAudioProfile); + } } if (!hasFloat && jPcmFloatProfileFromExtendedInteger.get() != nullptr) { // R and earlier compatibility - add ENCODING_PCM_FLOAT to the end @@ -3285,6 +3260,14 @@ int register_android_media_AudioSystem(JNIEnv *env) jclass audioProfileClass = FindClassOrDie(env, "android/media/AudioProfile"); gAudioProfileClass = MakeGlobalRefOrDie(env, audioProfileClass); gAudioProfileCstor = GetMethodIDOrDie(env, audioProfileClass, "<init>", "(I[I[I[II)V"); + gAudioProfileFields.mSamplingRates = + GetFieldIDOrDie(env, audioProfileClass, "mSamplingRates", "[I"); + gAudioProfileFields.mChannelMasks = + GetFieldIDOrDie(env, audioProfileClass, "mChannelMasks", "[I"); + gAudioProfileFields.mChannelIndexMasks = + GetFieldIDOrDie(env, audioProfileClass, "mChannelIndexMasks", "[I"); + gAudioProfileFields.mEncapsulationType = + GetFieldIDOrDie(env, audioProfileClass, "mEncapsulationType", "I"); jclass audioDescriptorClass = FindClassOrDie(env, "android/media/AudioDescriptor"); gAudioDescriptorClass = MakeGlobalRefOrDie(env, audioDescriptorClass); diff --git a/core/jni/android_server_NetworkManagementSocketTagger.cpp b/core/jni/android_server_NetworkManagementSocketTagger.cpp deleted file mode 100644 index 9734ab9b9e1d..000000000000 --- a/core/jni/android_server_NetworkManagementSocketTagger.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2011, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "NMST_QTagUidNative" - -#include <android/multinetwork.h> -#include <cutils/qtaguid.h> -#include <errno.h> -#include <fcntl.h> -#include <nativehelper/JNIPlatformHelp.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <utils/Log.h> -#include <utils/misc.h> - -#include "jni.h" - -namespace android { - -static jint tagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor, - jint tagNum, jint uid) { - int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor); - - if (env->ExceptionCheck()) { - ALOGE("Can't get FileDescriptor num"); - return (jint)-1; - } - - int res = android_tag_socket_with_uid(userFd, tagNum, uid); - if (res < 0) { - return (jint)-errno; - } - return (jint)res; -} - -static jint untagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor) { - int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor); - - if (env->ExceptionCheck()) { - ALOGE("Can't get FileDescriptor num"); - return (jint)-1; - } - - int res = android_untag_socket(userFd); - if (res < 0) { - return (jint)-errno; - } - return (jint)res; -} - -static const JNINativeMethod gQTagUidMethods[] = { - { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)tagSocketFd}, - { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)untagSocketFd}, -}; - -int register_android_server_NetworkManagementSocketTagger(JNIEnv* env) { - return jniRegisterNativeMethods(env, "com/android/server/NetworkManagementSocketTagger", gQTagUidMethods, NELEM(gQTagUidMethods)); -} - -}; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6e54197463eb..9bcd7d2d514c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -977,61 +977,6 @@ android:permissionFlags="softRestricted|immutablyRestricted" android:protectionLevel="dangerous" /> - <!-- Required to be able to read audio files from shared storage. - <p>Protection level: dangerous --> - <permission-group android:name="android.permission-group.READ_MEDIA_AURAL" - android:icon="@drawable/perm_group_read_media_aural" - android:label="@string/permgrouplab_readMediaAural" - android:description="@string/permgroupdesc_readMediaAural" - android:priority="950" /> - - <!-- Allows an application to read audio files from external storage. - <p>This permission is enforced starting in API level - {@link android.os.Build.VERSION_CODES#TIRAMISU}. - For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code - targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission - must not be used and the READ_EXTERNAL_STORAGE permission must be used instead. - <p>Protection level: dangerous --> - <permission android:name="android.permission.READ_MEDIA_AUDIO" - android:permissionGroup="android.permission-group.UNDEFINED" - android:label="@string/permlab_readMediaAudio" - android:description="@string/permdesc_readMediaAudio" - android:protectionLevel="dangerous" /> - - <!-- Required to be able to read image and video files from shared storage. - <p>Protection level: dangerous --> - <permission-group android:name="android.permission-group.READ_MEDIA_VISUAL" - android:icon="@drawable/perm_group_read_media_visual" - android:label="@string/permgrouplab_readMediaVisual" - android:description="@string/permgroupdesc_readMediaVisual" - android:priority="1000" /> - - <!-- Allows an application to read audio files from external storage. - <p>This permission is enforced starting in API level - {@link android.os.Build.VERSION_CODES#TIRAMISU}. - For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code - targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission - must not be used and the READ_EXTERNAL_STORAGE permission must be used instead. - <p>Protection level: dangerous --> - <permission android:name="android.permission.READ_MEDIA_VIDEO" - android:permissionGroup="android.permission-group.UNDEFINED" - android:label="@string/permlab_readMediaVideo" - android:description="@string/permdesc_readMediaVideo" - android:protectionLevel="dangerous" /> - - <!-- Allows an application to read image files from external storage. - <p>This permission is enforced starting in API level - {@link android.os.Build.VERSION_CODES#TIRAMISU}. - For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code - targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission - must not be used and the READ_EXTERNAL_STORAGE permission must be used instead. - <p>Protection level: dangerous --> - <permission android:name="android.permission.READ_MEDIA_IMAGE" - android:permissionGroup="android.permission-group.UNDEFINED" - android:label="@string/permlab_readMediaImage" - android:description="@string/permdesc_readMediaImage" - android:protectionLevel="dangerous" /> - <!-- Allows an application to write to external storage. <p class="note"><strong>Note:</strong> If <em>both</em> your <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code @@ -1657,7 +1602,7 @@ android:protectionLevel="normal" android:permissionFlags="removed"/> - <!-- @hide We need to keep this around for backwards compatibility --> + <!-- @SystemApi @hide We need to keep this around for backwards compatibility --> <permission android:name="android.permission.WRITE_SMS" android:protectionLevel="normal" android:permissionFlags="removed"/> @@ -1735,7 +1680,7 @@ <permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST" android:protectionLevel="signature|privileged" /> - <!-- Allows an application to monitor incoming Bluetooth MAP messages, to record + <!-- @SystemApi Allows an application to monitor incoming Bluetooth MAP messages, to record or perform processing on them. --> <!-- @hide --> <permission android:name="android.permission.RECEIVE_BLUETOOTH_MAP" @@ -4682,6 +4627,13 @@ <permission android:name="android.permission.READ_FRAME_BUFFER" android:protectionLevel="signature|recents" /> + <!-- @SystemApi Allows an application to change the touch mode state. + Without this permission, an app can only change the touch mode + if it currently has focus. + @hide --> + <permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE" + android:protectionLevel="signature" /> + <!-- Allows an application to use InputFlinger's low level features. @hide --> <permission android:name="android.permission.ACCESS_INPUT_FLINGER" @@ -6174,6 +6126,12 @@ <permission android:name="android.permission.ACCESS_FPS_COUNTER" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows the GameService provider to create GameSession and call GameSession + APIs and overlay a view on top of the game's Activity. + @hide --> + <permission android:name="android.permission.MANAGE_GAME_ACTIVITY" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows the holder to register callbacks to inform the RebootReadinessManager when they are performing reboot-blocking work. @hide --> @@ -6300,6 +6258,15 @@ <permission android:name="android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE" android:protectionLevel="signature" /> + <!-- @SystemApi Allows an app to set keep-clear areas without restrictions on the size or + number of keep-clear areas (see {@link android.view.View#setPreferKeepClearRects}). + When the system arranges floating windows onscreen, it might decide to ignore keep-clear + areas from windows, whose owner does not have this permission. + @hide + --> + <permission android:name="android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS" + android:protectionLevel="signature|privileged" /> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/core/res/res/drawable-car/car_checkbox.xml b/core/res/res/drawable-car/car_checkbox.xml deleted file mode 100644 index 083a7aa193aa..000000000000 --- a/core/res/res/drawable-car/car_checkbox.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright 2019 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item - android:width="@*android:dimen/car_primary_icon_size" - android:height="@*android:dimen/car_primary_icon_size" - android:drawable="@drawable/btn_check_material_anim"/> - <item - android:width="@*android:dimen/car_primary_icon_size" - android:height="@*android:dimen/car_primary_icon_size" - android:drawable="@drawable/car_checkbox_background"/> -</layer-list> diff --git a/core/res/res/drawable-car/car_checkbox_background.xml b/core/res/res/drawable-car/car_checkbox_background.xml deleted file mode 100644 index 69dcdbb0e94c..000000000000 --- a/core/res/res/drawable-car/car_checkbox_background.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright 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. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_focused="true" android:state_pressed="true"> - <shape android:shape="rectangle"> - <solid android:color="#8A0041BE" /> - <stroke android:width="4dp" android:color="#0041BE" /> - </shape> - </item> - <item android:state_focused="true"> - <shape android:shape="rectangle"> - <solid android:color="#3D0059B3" /> - <stroke android:width="8dp" android:color="#0059B3" /> - </shape> - </item> -</selector> diff --git a/core/res/res/drawable/perm_group_read_media_aural.xml b/core/res/res/drawable/perm_group_read_media_aural.xml deleted file mode 100644 index 6fc9c69254cc..000000000000 --- a/core/res/res/drawable/perm_group_read_media_aural.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2015 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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - <path - android:fillColor="@android:color/white" - android:pathData="M10,21q-1.65,0 -2.825,-1.175Q6,18.65 6,17q0,-1.65 1.175,-2.825Q8.35,13 10,13q0.575,0 1.063,0.137 0.487,0.138 0.937,0.413V3h6v4h-4v10q0,1.65 -1.175,2.825Q11.65,21 10,21z"/> -</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/perm_group_read_media_visual.xml b/core/res/res/drawable/perm_group_read_media_visual.xml deleted file mode 100644 index a5db2718c983..000000000000 --- a/core/res/res/drawable/perm_group_read_media_visual.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2015 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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - <path - android:fillColor="@android:color/white" - android:pathData="M9,14h10l-3.45,-4.5 -2.3,3 -1.55,-2zM8,18q-0.825,0 -1.412,-0.587Q6,16.825 6,16L6,4q0,-0.825 0.588,-1.413Q7.175,2 8,2h12q0.825,0 1.413,0.587Q22,3.175 22,4v12q0,0.825 -0.587,1.413Q20.825,18 20,18zM8,16h12L20,4L8,4v12zM4,22q-0.825,0 -1.413,-0.587Q2,20.825 2,20L2,6h2v14h14v2zM8,4v12L8,4z"/> -</vector>
\ No newline at end of file diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index f05f0272f400..a06b2cb28037 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2612,6 +2612,10 @@ will be locked. --> <bool name="config_multiuserDelayUserDataLocking">false</bool> + <!-- Whether to automatically switch a non-primary user back to the primary user after a + timeout when the device is docked. --> + <bool name="config_enableTimeoutToUserZeroWhenDocked">false</bool> + <!-- Whether to only install system packages on a user if they're allowlisted for that user type. These are flags and can be freely combined. 0 - disable allowlist (install all system packages; no logging) @@ -4979,6 +4983,10 @@ <!-- URI used for Nearby Share SliceProvider scanning. --> <string translatable="false" name="config_defaultNearbySharingSliceUri"></string> + <!-- Component name that accepts settings intents for saved devices. + Used by FastPairSettingsFragment. --> + <string translatable="false" name="config_defaultNearbyFastPairSettingsDevicesComponent"></string> + <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing check after reboot or airplane mode toggling --> <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool> @@ -5141,6 +5149,9 @@ If given value is outside of this range, the option 1 (center) is assummed. --> <integer name="config_letterboxDefaultPositionForReachability">1</integer> + <!-- Whether displaying letterbox education is enabled for letterboxed fullscreen apps. --> + <bool name="config_letterboxIsEducationEnabled">false</bool> + <!-- Whether a camera compat controller is enabled to allow the user to apply or revert treatment for stretched issues in camera viewfinder. --> <bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool> @@ -5642,4 +5653,13 @@ <!-- The amount of time after becoming non-interactive (in ms) after which Low Power Standby can activate. --> <integer name="config_lowPowerStandbyNonInteractiveTimeout">5000</integer> + + + <!-- Mapping to select an Intent.EXTRA_DOCK_STATE value from extcon state + key-value pairs. Each entry is evaluated in order and is of the form: + "[EXTRA_DOCK_STATE value],key1=value1,key2=value2[,...]" + An entry with no key-value pairs is valid and can be used as a wildcard. + --> + <string-array name="config_dockExtconStateMapping"> + </string-array> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 49a12d12ed21..610c6a69822c 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -880,16 +880,6 @@ <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgroupdesc_storage">access photos, media, and files on your device</string> - <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]--> - <string name="permgrouplab_readMediaAural">Music & other audio</string> - <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]--> - <string name="permgroupdesc_readMediaAural">access audio files on your device</string> - - <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]--> - <string name="permgrouplab_readMediaVisual">Photos & videos</string> - <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]--> - <string name="permgroupdesc_readMediaVisual">access images and video files on your device</string> - <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_microphone">Microphone</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> @@ -1903,21 +1893,6 @@ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> <string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string> - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> - <string name="permlab_readMediaAudio">read audio files from shared storage</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> - <string name="permdesc_readMediaAudio">Allows the app to read audio files from your shared storage.</string> - - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> - <string name="permlab_readMediaVideo">read video files from shared storage</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> - <string name="permdesc_readMediaVideo">Allows the app to read video files from your shared storage.</string> - - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> - <string name="permlab_readMediaImage">read image files from shared storage</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> - <string name="permdesc_readMediaImage">Allows the app to read image files from your shared storage.</string> - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] --> <string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e1a5eda05d7c..4c1cc4dc8f70 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -466,6 +466,7 @@ <java-symbol type="integer" name="config_multiuserMaximumUsers" /> <java-symbol type="integer" name="config_multiuserMaxRunningUsers" /> <java-symbol type="bool" name="config_multiuserDelayUserDataLocking" /> + <java-symbol type="bool" name="config_enableTimeoutToUserZeroWhenDocked" /> <java-symbol type="integer" name="config_userTypePackageWhitelistMode"/> <java-symbol type="xml" name="config_user_types" /> <java-symbol type="integer" name="config_safe_media_volume_index" /> @@ -4100,6 +4101,7 @@ <java-symbol type="layout" name="chooser_action_button" /> <java-symbol type="dimen" name="chooser_action_button_icon_size" /> <java-symbol type="string" name="config_defaultNearbySharingComponent" /> + <java-symbol type="string" name="config_defaultNearbyFastPairSettingsDevicesComponent" /> <java-symbol type="bool" name="config_disable_all_cb_messages" /> <java-symbol type="drawable" name="ic_close" /> @@ -4330,6 +4332,7 @@ <java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" /> <java-symbol type="bool" name="config_letterboxIsReachabilityEnabled" /> <java-symbol type="integer" name="config_letterboxDefaultPositionForReachability" /> + <java-symbol type="bool" name="config_letterboxIsEducationEnabled" /> <java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" /> <java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" /> @@ -4677,6 +4680,8 @@ <java-symbol type="string" name="config_deviceSpecificDeviceStatePolicyProvider" /> + <java-symbol type="array" name="config_dockExtconStateMapping" /> + <java-symbol type="string" name="notification_channel_abusive_bg_apps"/> <java-symbol type="string" name="notification_title_abusive_bg_apps"/> <java-symbol type="string" name="notification_content_abusive_bg_apps"/> diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index b2c427408d01..5c9044c56f95 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -646,6 +646,10 @@ public class TransactionParcelTests { } @Override + public void dumpResources(ParcelFileDescriptor fd, RemoteCallback finishCallback) { + } + + @Override public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) { } diff --git a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java index b66642c20808..fa657f7a8928 100644 --- a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java +++ b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java @@ -71,6 +71,8 @@ public class CrossProfileAppsTest { private Resources mResources; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Drawable mDrawable; + @Mock + private PackageManager mPackageManager; private CrossProfileApps mCrossProfileApps; @Before @@ -87,6 +89,7 @@ public class CrossProfileAppsTest { Context.DEVICE_POLICY_SERVICE); when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn( mDevicePolicyManager); + when(mContext.getPackageManager()).thenReturn(mPackageManager); } @Before @@ -131,7 +134,8 @@ public class CrossProfileAppsTest { setValidTargetProfile(MANAGED_PROFILE); mCrossProfileApps.getProfileSwitchingIconDrawable(MANAGED_PROFILE); - verify(mResources).getDrawable(R.drawable.ic_corp_badge, null); + verify(mPackageManager).getUserBadgeForDensityNoBackground( + MANAGED_PROFILE, /* density= */0); } @Test diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/InstallLocationUtilsTests.java index 947da0b07234..0629a999e6bf 100644 --- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java +++ b/core/tests/coretests/src/android/content/pm/InstallLocationUtilsTests.java @@ -25,7 +25,7 @@ import android.platform.test.annotations.Presubmit; import android.test.AndroidTestCase; import android.util.Log; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import org.mockito.Mockito; @@ -36,10 +36,9 @@ import java.util.List; import java.util.UUID; @Presubmit -public class PackageHelperTests extends AndroidTestCase { +public class InstallLocationUtilsTests extends AndroidTestCase { private static final boolean localLOGV = true; public static final String TAG = "PackageHelperTests"; - protected final String PREFIX = "android.content.pm"; private static final String sInternalVolPath = "/data"; private static final String sAdoptedVolPath = "/mnt/expand/123"; @@ -88,11 +87,14 @@ public class PackageHelperTests extends AndroidTestCase { UUID internalUuid = UUID.randomUUID(); UUID adoptedUuid = UUID.randomUUID(); UUID publicUuid = UUID.randomUUID(); - Mockito.when(storageManager.getStorageBytesUntilLow(internalFile)).thenReturn(sInternalSize); + Mockito.when(storageManager.getStorageBytesUntilLow(internalFile)) + .thenReturn(sInternalSize); Mockito.when(storageManager.getStorageBytesUntilLow(adoptedFile)).thenReturn(sAdoptedSize); Mockito.when(storageManager.getStorageBytesUntilLow(publicFile)).thenReturn(sPublicSize); - Mockito.when(storageManager.getUuidForPath(Mockito.eq(internalFile))).thenReturn(internalUuid); - Mockito.when(storageManager.getUuidForPath(Mockito.eq(adoptedFile))).thenReturn(adoptedUuid); + Mockito.when(storageManager.getUuidForPath(Mockito.eq(internalFile))) + .thenReturn(internalUuid); + Mockito.when(storageManager.getUuidForPath(Mockito.eq(adoptedFile))) + .thenReturn(adoptedUuid); Mockito.when(storageManager.getUuidForPath(Mockito.eq(publicFile))).thenReturn(publicUuid); Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(internalUuid), Mockito.anyInt())) .thenReturn(sInternalSize); @@ -103,7 +105,7 @@ public class PackageHelperTests extends AndroidTestCase { return storageManager; } - private static final class MockedInterface extends PackageHelper.TestableInterface { + private static final class MockedInterface extends InstallLocationUtils.TestableInterface { private boolean mForceAllowOnExternal = false; private boolean mAllow3rdPartyOnInternal = true; private ApplicationInfo mApplicationInfo = null; @@ -164,25 +166,25 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/, true /*allow 3rd party on internal*/); String volume = null; - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location*/, 1000 /*size bytes*/, mockedInterface); assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume); mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/, true /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location*/, 1000 /*size bytes*/, mockedInterface); assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume); mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/, false /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location*/, 1000 /*size bytes*/, mockedInterface); assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume); mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/, false /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location*/, 1000 /*size bytes*/, mockedInterface); assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume); @@ -192,7 +194,7 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/, true /*allow 3rd party on internal*/); try { - PackageHelper.resolveInstallVolume(getContext(), "package.name", + InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location*/, 1000000 /*size bytes*/, mockedInterface); fail("Expected exception in resolveInstallVolume was not thrown"); } catch(IOException e) { @@ -202,7 +204,7 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/, true /*allow 3rd party on internal*/); try { - PackageHelper.resolveInstallVolume(getContext(), "package.name", + InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location*/, 1000000 /*size bytes*/, mockedInterface); fail("Expected exception in resolveInstallVolume was not thrown"); } catch(IOException e) { @@ -212,7 +214,7 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/, false /*allow 3rd party on internal*/); try { - PackageHelper.resolveInstallVolume(getContext(), "package.name", + InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location*/, 1000000 /*size bytes*/, mockedInterface); fail("Expected exception in resolveInstallVolume was not thrown"); } catch(IOException e) { @@ -222,7 +224,7 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/, false /*allow 3rd party on internal*/); try { - PackageHelper.resolveInstallVolume(getContext(), "package.name", + InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location*/, 1000000 /*size bytes*/, mockedInterface); fail("Expected exception in resolveInstallVolume was not thrown"); } catch(IOException e) { @@ -240,13 +242,13 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(appInfo, false /*force allow on external*/, true /*allow 3rd party on internal*/); String volume = null; - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location*/, 1000 /*size bytes*/, mockedInterface); assertEquals(sInternalVolUuid, volume); mockedInterface.setMockValues(appInfo, true /*force allow on external*/, true /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location*/, 1000 /*size bytes*/, mockedInterface); assertEquals(sInternalVolUuid, volume); } @@ -260,25 +262,25 @@ public class PackageHelperTests extends AndroidTestCase { appInfo.volumeUuid = sAdoptedVolUuid; mockedInterface.setMockValues(appInfo, false /*force allow on external*/, true /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location*/, 1000 /*size bytes*/, mockedInterface); assertEquals(sAdoptedVolUuid, volume); mockedInterface.setMockValues(appInfo, true /*force allow on external*/, true /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location*/, 1000 /*size bytes*/, mockedInterface); assertEquals(sAdoptedVolUuid, volume); mockedInterface.setMockValues(appInfo, false /*force allow on external*/, false /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location*/, 1000 /*size bytes*/, mockedInterface); assertEquals(sAdoptedVolUuid, volume); mockedInterface.setMockValues(appInfo, true /*force allow on external*/, false /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location*/, 1000 /*size bytes*/, mockedInterface); assertEquals(sAdoptedVolUuid, volume); } @@ -292,7 +294,7 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(appInfo, false /*force allow on external*/, true /*allow 3rd party on internal*/); try { - PackageHelper.resolveInstallVolume(getContext(), "package.name", + InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface); fail("Expected exception was not thrown " + appInfo.volumeUuid); } catch (IOException e) { @@ -302,7 +304,7 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(appInfo, false /*force allow on external*/, false /*allow 3rd party on internal*/); try { - PackageHelper.resolveInstallVolume(getContext(), "package.name", + InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface); fail("Expected exception was not thrown " + appInfo.volumeUuid); } catch (IOException e) { @@ -312,7 +314,7 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(appInfo, true /*force allow on external*/, false /*allow 3rd party on internal*/); try { - PackageHelper.resolveInstallVolume(getContext(), "package.name", + InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface); fail("Expected exception was not thrown " + appInfo.volumeUuid); } catch (IOException e) { @@ -322,7 +324,7 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(appInfo, true /*force allow on external*/, true /*allow 3rd party on internal*/); try { - PackageHelper.resolveInstallVolume(getContext(), "package.name", + InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface); fail("Expected exception was not thrown " + appInfo.volumeUuid); } catch (IOException e) { @@ -336,28 +338,28 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(appInfo, false /*force allow on external*/, true /*allow 3rd party on internal*/); String volume = null; - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface); // Should return the volume with bigger available space. assertEquals(sInternalVolUuid, volume); mockedInterface.setMockValues(appInfo, true /*force allow on external*/, true /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface); // Should return the volume with bigger available space. assertEquals(sInternalVolUuid, volume); mockedInterface.setMockValues(appInfo, true /*force allow on external*/, false /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface); // Should return the volume with bigger available space. assertEquals(sAdoptedVolUuid, volume); mockedInterface.setMockValues(appInfo, false /*force allow on external*/, false /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface); // Should return the volume with bigger available space. assertEquals(sAdoptedVolUuid, volume); @@ -371,20 +373,20 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(appInfo, false /*force allow on external*/, true /*allow 3rd party on internal*/); String volume = null; - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location internal ONLY*/, 1000 /*size bytes*/, mockedInterface); assertEquals(sInternalVolUuid, volume); mockedInterface.setMockValues(appInfo, true /*force allow on external*/, true /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location internal ONLY*/, 1000 /*size bytes*/, mockedInterface); assertEquals(sInternalVolUuid, volume); mockedInterface.setMockValues(appInfo, false /*force allow on external*/, false /*allow 3rd party on internal*/); try { - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface); fail("Expected exception in resolveInstallVolume was not thrown"); } catch (IOException e) { @@ -395,7 +397,7 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(appInfo, true /*force allow on external*/, false /*allow 3rd party on internal*/); volume = null; - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface); assertEquals(sAdoptedVolUuid, volume); } @@ -407,7 +409,7 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(appInfo, false /*force allow on external*/, false /*allow 3rd party on internal*/); String volume = null; - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface); // Should return the non-internal volume. assertEquals(sAdoptedVolUuid, volume); @@ -415,7 +417,7 @@ public class PackageHelperTests extends AndroidTestCase { appInfo = null; mockedInterface.setMockValues(appInfo, true /*force allow on external*/, false /*allow 3rd party on internal*/); - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface); // Should return the non-internal volume. assertEquals(sAdoptedVolUuid, volume); @@ -428,7 +430,7 @@ public class PackageHelperTests extends AndroidTestCase { true /*allow 3rd party on internal*/); String volume = null; try { - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location internal ONLY*/, 1000000 /*size too big*/, mockedInterface); fail("Expected exception in resolveInstallVolume was not thrown"); @@ -445,7 +447,7 @@ public class PackageHelperTests extends AndroidTestCase { false /*allow 3rd party on internal*/); String volume = null; try { - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface); fail("Expected exception in resolveInstallVolume was not thrown"); } catch (IOException e) { @@ -456,7 +458,7 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(appInfo, true /*force allow on external*/, false /*allow 3rd party on internal*/); volume = null; - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface); assertEquals(sAdoptedVolUuid, volume); @@ -474,7 +476,7 @@ public class PackageHelperTests extends AndroidTestCase { mockedInterface.setMockValues(appInfo, true /*force allow on external*/, false /*allow 3rd party on internal*/); String volume = null; - volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", + volume = InstallLocationUtils.resolveInstallVolume(getContext(), "package.name", 1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface); assertEquals(sAdoptedVolUuid, volume); } diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java index 104f077e5ad2..f7ca822c36e2 100644 --- a/core/tests/coretests/src/android/os/VibrationEffectTest.java +++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java @@ -146,6 +146,7 @@ public class VibrationEffectTest { @Test public void testValidateWaveformBuilder() { + // Cover builder methods VibrationEffect.startWaveform(targetAmplitude(1)) .addTransition(Duration.ofSeconds(1), targetAmplitude(0.5f), targetFrequency(100)) .addTransition(Duration.ZERO, targetAmplitude(0f), targetFrequency(200)) @@ -158,6 +159,39 @@ public class VibrationEffectTest { .build() .validate(); + // Make sure class summary javadoc examples compile and are valid. + // NOTE: IF THIS IS UPDATED, PLEASE ALSO UPDATE WaveformBuilder javadocs. + VibrationEffect.startWaveform(targetFrequency(60)) + .addTransition(Duration.ofMillis(100), targetAmplitude(1), targetFrequency(120)) + .addSustain(Duration.ofMillis(200)) + .addTransition(Duration.ofMillis(100), targetAmplitude(0), targetFrequency(60)) + .build() + .validate(); + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK) + .addOffDuration(Duration.ofMillis(20)) + .repeatEffectIndefinitely( + VibrationEffect.startWaveform(targetAmplitude(0.2f)) + .addSustain(Duration.ofMillis(10)) + .addTransition(Duration.ofMillis(20), targetAmplitude(0.4f)) + .addSustain(Duration.ofMillis(30)) + .addTransition(Duration.ofMillis(40), targetAmplitude(0.8f)) + .addSustain(Duration.ofMillis(50)) + .addTransition(Duration.ofMillis(60), targetAmplitude(0.2f)) + .build()) + .compose() + .validate(); + VibrationEffect.createWaveform(new long[]{10, 20, 30}, new int[]{51, 102, 204}, -1) + .validate(); + VibrationEffect.startWaveform(targetAmplitude(0.2f)) + .addSustain(Duration.ofMillis(10)) + .addTransition(Duration.ZERO, targetAmplitude(0.4f)) + .addSustain(Duration.ofMillis(20)) + .addTransition(Duration.ZERO, targetAmplitude(0.8f)) + .addSustain(Duration.ofMillis(30)) + .build() + .validate(); + assertThrows(IllegalStateException.class, () -> VibrationEffect.startWaveform().build().validate()); assertThrows(IllegalArgumentException.class, () -> targetAmplitude(-2)); @@ -171,6 +205,7 @@ public class VibrationEffectTest { @Test public void testValidateComposed() { + // Cover builder methods VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) .addEffect(TEST_ONE_SHOT) @@ -178,14 +213,31 @@ public class VibrationEffectTest { .addOffDuration(Duration.ofMillis(100)) .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10) .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) + .addEffect(VibrationEffect.createWaveform(new long[]{10, 20}, /* repeat= */ 0)) .compose() .validate(); - VibrationEffect.startComposition() .repeatEffectIndefinitely(TEST_ONE_SHOT) .compose() .validate(); + // Make sure class summary javadoc examples compile and are valid. + // NOTE: IF THIS IS UPDATED, PLEASE ALSO UPDATE Composition javadocs. + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 0.5f) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.5f) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1.0f, 100) + .compose() + .validate(); + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK) + .addOffDuration(Duration.ofMillis(10)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK)) + .addOffDuration(Duration.ofMillis(50)) + .addEffect(VibrationEffect.createWaveform(new long[]{10, 20}, /* repeat= */ 0)) + .compose() + .validate(); + assertThrows(IllegalStateException.class, () -> VibrationEffect.startComposition().compose().validate()); assertThrows(IllegalStateException.class, diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 88920c865511..92fca3661fbc 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -231,18 +231,6 @@ targetSdk="29"> <new-permission name="android.permission.ACCESS_MEDIA_LOCATION" /> </split-permission> - <split-permission name="android.permission.READ_EXTERNAL_STORAGE" - targetSdk="33"> - <new-permission name="android.permission.READ_MEDIA_AUDIO" /> - </split-permission> - <split-permission name="android.permission.READ_EXTERNAL_STORAGE" - targetSdk="33"> - <new-permission name="android.permission.READ_MEDIA_VIDEO" /> - </split-permission> - <split-permission name="android.permission.READ_EXTERNAL_STORAGE" - targetSdk="33"> - <new-permission name="android.permission.READ_MEDIA_IMAGE" /> - </split-permission> <split-permission name="android.permission.BLUETOOTH" targetSdk="31"> <new-permission name="android.permission.BLUETOOTH_SCAN" /> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java index e2bc36028405..9384e2b4dfdf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java @@ -245,7 +245,8 @@ public class DisplayController { } } - private void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) { + private void onKeepClearAreasChanged(int displayId, List<Rect> restricted, + List<Rect> unrestricted) { synchronized (mDisplays) { if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { Slog.w(TAG, "Skipping onKeepClearAreasChanged on unknown" @@ -253,7 +254,8 @@ public class DisplayController { return; } for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { - mDisplayChangedListeners.get(i).onKeepClearAreasChanged(displayId, keepClearAreas); + mDisplayChangedListeners.get(i) + .onKeepClearAreasChanged(displayId, restricted, unrestricted); } } } @@ -318,9 +320,10 @@ public class DisplayController { } @Override - public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) { + public void onKeepClearAreasChanged(int displayId, List<Rect> restricted, + List<Rect> unrestricted) { mMainExecutor.execute(() -> { - DisplayController.this.onKeepClearAreasChanged(displayId, keepClearAreas); + DisplayController.this.onKeepClearAreasChanged(displayId, restricted, unrestricted); }); } } @@ -361,6 +364,7 @@ public class DisplayController { /** * Called when keep-clear areas on a display have changed. */ - default void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {} + default void onKeepClearAreasChanged(int displayId, List<Rect> restricted, + List<Rect> unrestricted) {} } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index f61e62444366..9f4ff7c8dc06 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -471,9 +471,10 @@ public abstract class WMShellBaseModule { static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool, DisplayController displayController, Context context, @ShellMainThread ShellExecutor mainExecutor, + @ShellMainThread Handler mainHandler, @ShellAnimationThread ShellExecutor animExecutor) { return new Transitions(organizer, pool, displayController, context, mainExecutor, - animExecutor); + mainHandler, animExecutor); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index d44db498451e..7307ba30fd67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -102,7 +102,7 @@ public class DragLayout extends LinearLayout { MATCH_PARENT)); ((LayoutParams) mDropZoneView1.getLayoutParams()).weight = 1; ((LayoutParams) mDropZoneView2.getLayoutParams()).weight = 1; - updateContainerMargins(); + updateContainerMargins(getResources().getConfiguration().orientation); } @Override @@ -127,20 +127,18 @@ public class DragLayout extends LinearLayout { } public void onConfigChanged(Configuration newConfig) { - final int orientation = getResources().getConfiguration().orientation; - if (orientation == Configuration.ORIENTATION_LANDSCAPE + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE && getOrientation() != HORIZONTAL) { setOrientation(LinearLayout.HORIZONTAL); - updateContainerMargins(); - } else if (orientation == Configuration.ORIENTATION_PORTRAIT + updateContainerMargins(newConfig.orientation); + } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT && getOrientation() != VERTICAL) { setOrientation(LinearLayout.VERTICAL); - updateContainerMargins(); + updateContainerMargins(newConfig.orientation); } } - private void updateContainerMargins() { - final int orientation = getResources().getConfiguration().orientation; + private void updateContainerMargins(int orientation) { final float halfMargin = mDisplayMargin / 2f; if (orientation == Configuration.ORIENTATION_LANDSCAPE) { mDropZoneView1.setContainerMargin( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index e592101d2b20..a2c2f591cde0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -879,6 +879,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mMainUnfoldController != null && mSideUnfoldController != null) { mMainUnfoldController.onSplitVisibilityChanged(mDividerVisible); mSideUnfoldController.onSplitVisibilityChanged(mDividerVisible); + updateUnfoldBounds(); } } 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 79c8a87acb5b..27b6dc5b304f 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 @@ -25,6 +25,11 @@ import static android.app.ActivityOptions.ANIM_SCALE_UP; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED; +import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE; +import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION; +import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE; +import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; @@ -57,11 +62,17 @@ import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread; +import android.app.admin.DevicePolicyManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.hardware.HardwareBuffer; +import android.os.Handler; import android.os.IBinder; import android.os.SystemProperties; import android.os.UserHandle; @@ -118,6 +129,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; private final TransitionAnimation mTransitionAnimation; + private final DevicePolicyManager mDevicePolicyManager; private final SurfaceSession mSurfaceSession = new SurfaceSession(); @@ -132,9 +144,24 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { private ScreenRotationAnimation mRotationAnimation; + private Drawable mEnterpriseThumbnailDrawable; + + private BroadcastReceiver mEnterpriseResourceUpdatedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + boolean isDrawable = intent.getBooleanExtra( + EXTRA_RESOURCE_TYPE_DRAWABLE, /* default= */ false); + if (!isDrawable) { + return; + } + updateEnterpriseThumbnailDrawable(); + } + }; + DefaultTransitionHandler(@NonNull DisplayController displayController, @NonNull TransactionPool transactionPool, Context context, - @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { + @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, + @NonNull ShellExecutor animExecutor) { mDisplayController = displayController; mTransactionPool = transactionPool; mContext = context; @@ -143,9 +170,23 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG); mCurrentUserId = UserHandle.myUserId(); + mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class); + updateEnterpriseThumbnailDrawable(); + mContext.registerReceiver( + mEnterpriseResourceUpdatedReceiver, + new IntentFilter(ACTION_DEVICE_POLICY_RESOURCE_UPDATED), + /* broadcastPermission = */ null, + mainHandler); + AttributeCache.init(context); } + private void updateEnterpriseThumbnailDrawable() { + mEnterpriseThumbnailDrawable = mDevicePolicyManager.getDrawable( + WORK_PROFILE_ICON, OUTLINE, PROFILE_SWITCH_ANIMATION, + () -> mContext.getDrawable(R.drawable.ic_corp_badge)); + } + @VisibleForTesting static boolean isRotationSeamless(@NonNull TransitionInfo info, DisplayController displayController) { @@ -632,7 +673,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { final boolean isClose = Transitions.isClosingType(change.getMode()); if (isOpen) { if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) { - attachCrossProfileThunmbnailAnimation(animations, finishCallback, change, + attachCrossProfileThumbnailAnimation(animations, finishCallback, change, cornerRadius); } else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) { attachThumbnailAnimation(animations, finishCallback, change, options, cornerRadius); @@ -642,13 +683,14 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } } - private void attachCrossProfileThunmbnailAnimation(@NonNull ArrayList<Animator> animations, + private void attachCrossProfileThumbnailAnimation(@NonNull ArrayList<Animator> animations, @NonNull Runnable finishCallback, TransitionInfo.Change change, float cornerRadius) { - final int thumbnailDrawableRes = change.getTaskInfo().userId == mCurrentUserId - ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge; final Rect bounds = change.getEndAbsBounds(); + // Show the right drawable depending on the user we're transitioning to. + final Drawable thumbnailDrawable = change.getTaskInfo().userId == mCurrentUserId + ? mContext.getDrawable(R.drawable.ic_account_circle) : mEnterpriseThumbnailDrawable; final HardwareBuffer thumbnail = mTransitionAnimation.createCrossProfileAppsThumbnail( - thumbnailDrawableRes, bounds); + thumbnailDrawable, bounds); if (thumbnail == null) { return; } 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 33a98b2fd80e..86b73fc30ca8 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 @@ -33,6 +33,7 @@ import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; +import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; @@ -123,7 +124,8 @@ public class Transitions implements RemoteCallable<Transitions> { public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, @NonNull DisplayController displayController, @NonNull Context context, - @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { + @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, + @NonNull ShellExecutor animExecutor) { mOrganizer = organizer; mContext = context; mMainExecutor = mainExecutor; @@ -132,7 +134,7 @@ public class Transitions implements RemoteCallable<Transitions> { mPlayerImpl = new TransitionPlayerImpl(); // The very last handler (0 in the list) should be the default one. mHandlers.add(new DefaultTransitionHandler(displayController, pool, context, mainExecutor, - animExecutor)); + mainHandler, animExecutor)); // Next lowest priority is remote transitions. mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor); mHandlers.add(mRemoteTransitionHandler); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index 825320b4e784..a6caefe6d3e7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -363,6 +363,45 @@ public class ShellTaskOrganizerTests { } @Test + public void testOnEligibleForLetterboxEducationActivityChanged() { + final RunningTaskInfo taskInfo1 = createTaskInfo(12, WINDOWING_MODE_FULLSCREEN); + taskInfo1.displayId = DEFAULT_DISPLAY; + taskInfo1.topActivityEligibleForLetterboxEducation = false; + final TrackingTaskListener taskListener = new TrackingTaskListener(); + mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN); + mOrganizer.onTaskAppeared(taskInfo1, null); + + // Task listener sent to compat UI is null if top activity isn't eligible for letterbox + // education. + verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */); + + // Task listener is non-null if top activity is eligible for letterbox education and task + // is visible. + clearInvocations(mCompatUI); + final RunningTaskInfo taskInfo2 = + createTaskInfo(taskInfo1.taskId, WINDOWING_MODE_FULLSCREEN); + taskInfo2.displayId = taskInfo1.displayId; + taskInfo2.topActivityEligibleForLetterboxEducation = true; + taskInfo2.isVisible = true; + mOrganizer.onTaskInfoChanged(taskInfo2); + verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener); + + // Task listener is null if task is invisible. + clearInvocations(mCompatUI); + final RunningTaskInfo taskInfo3 = + createTaskInfo(taskInfo1.taskId, WINDOWING_MODE_FULLSCREEN); + taskInfo3.displayId = taskInfo1.displayId; + taskInfo3.topActivityEligibleForLetterboxEducation = true; + taskInfo3.isVisible = false; + mOrganizer.onTaskInfoChanged(taskInfo3); + verify(mCompatUI).onCompatInfoChanged(taskInfo3, null /* taskListener */); + + clearInvocations(mCompatUI); + mOrganizer.onTaskVanished(taskInfo1); + verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */); + } + + @Test public void testOnCameraCompatActivityChanged() { final RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN); taskInfo1.displayId = DEFAULT_DISPLAY; @@ -375,7 +414,7 @@ public class ShellTaskOrganizerTests { // compat control. verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */); - // Task linster is non-null when request a camera compat control for a visible task. + // Task listener is non-null when request a camera compat control for a visible task. clearInvocations(mCompatUI); final RunningTaskInfo taskInfo2 = createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode()); 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 e39171343bb9..0f4a06f22986 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 @@ -54,7 +54,9 @@ import static org.mockito.Mockito.verify; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.RemoteException; import android.view.IDisplayWindowListener; import android.view.IWindowManager; @@ -84,8 +86,6 @@ import com.android.wm.shell.common.TransactionPool; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import java.util.ArrayList; @@ -106,6 +106,7 @@ public class ShellTransitionTests { private final TestShellExecutor mMainExecutor = new TestShellExecutor(); private final ShellExecutor mAnimExecutor = new TestShellExecutor(); private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler(); + private final Handler mMainHandler = new Handler(Looper.getMainLooper()); @Before public void setUp() { @@ -752,7 +753,7 @@ public class ShellTransitionTests { private Transitions createTestTransitions() { return new Transitions(mOrganizer, mTransactionPool, createTestDisplayController(), - mContext, mMainExecutor, mAnimExecutor); + mContext, mMainExecutor, mMainHandler, mAnimExecutor); } // // private class TestDisplayController extends DisplayController { diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 1b4c0bc64f78..15a398de9021 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -562,6 +562,7 @@ public class AudioManager { * Indicates the volume set/adjust call is for Bluetooth absolute volume * @hide */ + @SystemApi public static final int FLAG_BLUETOOTH_ABS_VOLUME = 1 << 6; /** diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java index d1bb41e70b54..86dc6e0f09c5 100644 --- a/media/java/android/media/BtProfileConnectionInfo.java +++ b/media/java/android/media/BtProfileConnectionInfo.java @@ -15,39 +15,24 @@ */ package android.media; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.bluetooth.BluetoothProfile; import android.os.Parcel; import android.os.Parcelable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - /** * Contains information about Bluetooth profile connection state changed * {@hide} */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public final class BtProfileConnectionInfo implements Parcelable { - /** @hide */ - @IntDef({ - BluetoothProfile.A2DP, - BluetoothProfile.A2DP_SINK, - BluetoothProfile.HEADSET, // Can only be set by BtHelper - BluetoothProfile.HEARING_AID, - BluetoothProfile.LE_AUDIO, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface BtProfile {} - - private final @BtProfile int mProfile; + private final int mProfile; private final boolean mSupprNoisy; private final int mVolume; private final boolean mIsLeOutput; - private BtProfileConnectionInfo(@BtProfile int profile, boolean suppressNoisyIntent, int volume, + private BtProfileConnectionInfo(int profile, boolean suppressNoisyIntent, int volume, boolean isLeOutput) { mProfile = profile; mSupprNoisy = suppressNoisyIntent; @@ -59,7 +44,7 @@ public final class BtProfileConnectionInfo implements Parcelable { * Constructor used by BtHelper when a profile is connected * {@hide} */ - public BtProfileConnectionInfo(@BtProfile int profile) { + public BtProfileConnectionInfo(int profile) { this(profile, false, -1, false); } @@ -142,7 +127,7 @@ public final class BtProfileConnectionInfo implements Parcelable { /** * @return The profile connection */ - public @BtProfile int getProfile() { + public int getProfile() { return mProfile; } diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl index 5113dc2058e0..71dc2a781ba9 100644 --- a/media/java/android/media/IMediaRouter2Manager.aidl +++ b/media/java/android/media/IMediaRouter2Manager.aidl @@ -18,6 +18,7 @@ package android.media; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2Info; +import android.media.RouteDiscoveryPreference; import android.media.RoutingSessionInfo; /** @@ -27,7 +28,8 @@ oneway interface IMediaRouter2Manager { void notifySessionCreated(int requestId, in RoutingSessionInfo session); void notifySessionUpdated(in RoutingSessionInfo session); void notifySessionReleased(in RoutingSessionInfo session); - void notifyPreferredFeaturesChanged(String packageName, in List<String> preferredFeatures); + void notifyDiscoveryPreferenceChanged(String packageName, + in RouteDiscoveryPreference discoveryPreference); void notifyRoutesAdded(in List<MediaRoute2Info> routes); void notifyRoutesRemoved(in List<MediaRoute2Info> routes); void notifyRoutesChanged(in List<MediaRoute2Info> routes); diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 2427fa64562d..ee0293d629b1 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -34,6 +34,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Describes the properties of a route. @@ -340,10 +341,12 @@ public final class MediaRoute2Info implements Parcelable { @ConnectionState final int mConnectionState; final String mClientPackageName; + final String mPackageName; final int mVolumeHandling; final int mVolumeMax; final int mVolume; final String mAddress; + final Set<String> mDeduplicationIds; final Bundle mExtras; final String mProviderId; @@ -357,10 +360,12 @@ public final class MediaRoute2Info implements Parcelable { mDescription = builder.mDescription; mConnectionState = builder.mConnectionState; mClientPackageName = builder.mClientPackageName; + mPackageName = builder.mPackageName; mVolumeHandling = builder.mVolumeHandling; mVolumeMax = builder.mVolumeMax; mVolume = builder.mVolume; mAddress = builder.mAddress; + mDeduplicationIds = builder.mDeduplicationIds; mExtras = builder.mExtras; mProviderId = builder.mProviderId; } @@ -375,10 +380,12 @@ public final class MediaRoute2Info implements Parcelable { mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mConnectionState = in.readInt(); mClientPackageName = in.readString(); + mPackageName = in.readString(); mVolumeHandling = in.readInt(); mVolumeMax = in.readInt(); mVolume = in.readInt(); mAddress = in.readString(); + mDeduplicationIds = Set.of(in.readStringArray()); mExtras = in.readBundle(); mProviderId = in.readString(); } @@ -486,6 +493,17 @@ public final class MediaRoute2Info implements Parcelable { } /** + * Gets the package name of the provider that published the route. + * <p> + * It is set by the system service. + * @hide + */ + @Nullable + public String getPackageName() { + return mPackageName; + } + + /** * Gets information about how volume is handled on the route. * * @return {@link #PLAYBACK_VOLUME_FIXED} or {@link #PLAYBACK_VOLUME_VARIABLE} @@ -518,6 +536,18 @@ public final class MediaRoute2Info implements Parcelable { return mAddress; } + /** + * Gets the Deduplication ID of the route if available. + * @see RouteDiscoveryPreference#shouldRemoveDuplicates() + */ + @NonNull + public Set<String> getDeduplicationIds() { + return mDeduplicationIds; + } + + /** + * Gets an optional bundle with extra data. + */ @Nullable public Bundle getExtras() { return mExtras == null ? null : new Bundle(mExtras); @@ -549,7 +579,7 @@ public final class MediaRoute2Info implements Parcelable { * Returns if the route has at least one of the specified route features. * * @param features the list of route features to consider - * @return true if the route has at least one feature in the list + * @return {@code true} if the route has at least one feature in the list * @hide */ public boolean hasAnyFeatures(@NonNull Collection<String> features) { @@ -563,6 +593,21 @@ public final class MediaRoute2Info implements Parcelable { } /** + * Returns if the route has all the specified route features. + * + * @hide + */ + public boolean hasAllFeatures(@NonNull Collection<String> features) { + Objects.requireNonNull(features, "features must not be null"); + for (String feature : features) { + if (!getFeatures().contains(feature)) { + return false; + } + } + return true; + } + + /** * Returns true if the route info has all of the required field. * A route is valid if and only if it is obtained from * {@link com.android.server.media.MediaRouterService}. @@ -596,10 +641,12 @@ public final class MediaRoute2Info implements Parcelable { && Objects.equals(mDescription, other.mDescription) && (mConnectionState == other.mConnectionState) && Objects.equals(mClientPackageName, other.mClientPackageName) + && Objects.equals(mPackageName, other.mPackageName) && (mVolumeHandling == other.mVolumeHandling) && (mVolumeMax == other.mVolumeMax) && (mVolume == other.mVolume) && Objects.equals(mAddress, other.mAddress) + && Objects.equals(mDeduplicationIds, other.mDeduplicationIds) && Objects.equals(mProviderId, other.mProviderId); } @@ -607,8 +654,8 @@ public final class MediaRoute2Info implements Parcelable { public int hashCode() { // Note: mExtras is not included. return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription, - mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume, - mAddress, mProviderId); + mConnectionState, mClientPackageName, mPackageName, mVolumeHandling, mVolumeMax, + mVolume, mAddress, mDeduplicationIds, mProviderId); } @Override @@ -626,6 +673,7 @@ public final class MediaRoute2Info implements Parcelable { .append(", volumeHandling=").append(getVolumeHandling()) .append(", volumeMax=").append(getVolumeMax()) .append(", volume=").append(getVolume()) + .append(", deduplicationIds=").append(String.join(",", getDeduplicationIds())) .append(", providerId=").append(getProviderId()) .append(" }"); return result.toString(); @@ -647,10 +695,12 @@ public final class MediaRoute2Info implements Parcelable { TextUtils.writeToParcel(mDescription, dest, flags); dest.writeInt(mConnectionState); dest.writeString(mClientPackageName); + dest.writeString(mPackageName); dest.writeInt(mVolumeHandling); dest.writeInt(mVolumeMax); dest.writeInt(mVolume); dest.writeString(mAddress); + dest.writeStringArray(mDeduplicationIds.toArray(new String[mDeduplicationIds.size()])); dest.writeBundle(mExtras); dest.writeString(mProviderId); } @@ -671,10 +721,12 @@ public final class MediaRoute2Info implements Parcelable { @ConnectionState int mConnectionState; String mClientPackageName; + String mPackageName; int mVolumeHandling = PLAYBACK_VOLUME_FIXED; int mVolumeMax; int mVolume; String mAddress; + Set<String> mDeduplicationIds; Bundle mExtras; String mProviderId; @@ -698,6 +750,7 @@ public final class MediaRoute2Info implements Parcelable { mId = id; mName = name; mFeatures = new ArrayList<>(); + mDeduplicationIds = Set.of(); } /** @@ -733,10 +786,12 @@ public final class MediaRoute2Info implements Parcelable { mDescription = routeInfo.mDescription; mConnectionState = routeInfo.mConnectionState; mClientPackageName = routeInfo.mClientPackageName; + mPackageName = routeInfo.mPackageName; mVolumeHandling = routeInfo.mVolumeHandling; mVolumeMax = routeInfo.mVolumeMax; mVolume = routeInfo.mVolume; mAddress = routeInfo.mAddress; + mDeduplicationIds = Set.copyOf(routeInfo.mDeduplicationIds); if (routeInfo.mExtras != null) { mExtras = new Bundle(routeInfo.mExtras); } @@ -860,6 +915,16 @@ public final class MediaRoute2Info implements Parcelable { } /** + * Sets the package name of the route. + * @hide + */ + @NonNull + public Builder setPackageName(@NonNull String packageName) { + mPackageName = packageName; + return this; + } + + /** * Sets the route's volume handling. */ @NonNull @@ -897,6 +962,20 @@ public final class MediaRoute2Info implements Parcelable { } /** + * Sets the deduplication ID of the route. + * Routes have the same ID could be removed even when + * they are from different providers. + * <p> + * If it's {@code null}, the route will not be removed. + * @see RouteDiscoveryPreference#shouldRemoveDuplicates() + */ + @NonNull + public Builder setDeduplicationIds(@NonNull Set<String> id) { + mDeduplicationIds = Set.copyOf(id); + return this; + } + + /** * Sets a bundle of extras for the route. * <p> * Note: The extras will not affect the result of {@link MediaRoute2Info#equals(Object)}. diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 4b32dbfc42f5..b485eb51380d 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -34,15 +34,18 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; @@ -302,8 +305,7 @@ public final class MediaRouter2 { mSystemController = new SystemRoutingController( ensureClientPackageNameForSystemSession( sManager.getSystemRoutingSession(clientPackageName))); - mDiscoveryPreference = new RouteDiscoveryPreference.Builder( - sManager.getPreferredFeatures(clientPackageName), true).build(); + mDiscoveryPreference = sManager.getDiscoveryPreference(clientPackageName); updateAllRoutesFromManager(); // Only used by non-system MediaRouter2. @@ -1060,11 +1062,48 @@ public final class MediaRouter2 { .build(); } + private List<MediaRoute2Info> getSortedRoutes(List<MediaRoute2Info> routes, + RouteDiscoveryPreference preference) { + if (!preference.shouldRemoveDuplicates()) { + return routes; + } + Map<String, Integer> packagePriority = new ArrayMap<>(); + int count = preference.getDeduplicationPackageOrder().size(); + for (int i = 0; i < count; i++) { + // the last package will have 1 as the priority + packagePriority.put(preference.getDeduplicationPackageOrder().get(i), count - i); + } + ArrayList<MediaRoute2Info> sortedRoutes = new ArrayList<>(routes); + // take the negative for descending order + sortedRoutes.sort(Comparator.comparingInt( + r -> -packagePriority.getOrDefault(r.getPackageName(), 0))); + return sortedRoutes; + } + private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes, - RouteDiscoveryPreference discoveryRequest) { - return routes.stream() - .filter(route -> route.hasAnyFeatures(discoveryRequest.getPreferredFeatures())) - .collect(Collectors.toList()); + RouteDiscoveryPreference discoveryPreference) { + + Set<String> deduplicationIdSet = new ArraySet<>(); + + List<MediaRoute2Info> filteredRoutes = new ArrayList<>(); + for (MediaRoute2Info route : getSortedRoutes(routes, discoveryPreference)) { + if (!route.hasAllFeatures(discoveryPreference.getRequiredFeatures()) + || !route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) { + continue; + } + if (!discoveryPreference.getAllowedPackages().isEmpty() + && !discoveryPreference.getAllowedPackages().contains(route.getPackageName())) { + continue; + } + if (discoveryPreference.shouldRemoveDuplicates()) { + if (Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) { + continue; + } + deduplicationIdSet.addAll(route.getDeduplicationIds()); + } + filteredRoutes.add(route); + } + return filteredRoutes; } private void updateAllRoutesFromManager() { diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 83fa7c255f9f..8635c0ea762c 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -29,6 +29,8 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -36,15 +38,18 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; import java.util.stream.Collectors; /** @@ -84,7 +89,8 @@ public final class MediaRouter2Manager { @GuardedBy("mRoutesLock") private final Map<String, MediaRoute2Info> mRoutes = new HashMap<>(); @NonNull - final ConcurrentMap<String, List<String>> mPreferredFeaturesMap = new ConcurrentHashMap<>(); + final ConcurrentMap<String, RouteDiscoveryPreference> mDiscoveryPreferenceMap = + new ConcurrentHashMap<>(); private final AtomicInteger mNextRequestId = new AtomicInteger(1); private final CopyOnWriteArrayList<TransferRequest> mTransferRequests = @@ -247,25 +253,8 @@ public final class MediaRouter2Manager { */ @NonNull public List<MediaRoute2Info> getAvailableRoutes(@NonNull RoutingSessionInfo sessionInfo) { - Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); - - List<MediaRoute2Info> routes = new ArrayList<>(); - - String packageName = sessionInfo.getClientPackageName(); - List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName); - if (preferredFeatures == null) { - preferredFeatures = Collections.emptyList(); - } - synchronized (mRoutesLock) { - for (MediaRoute2Info route : mRoutes.values()) { - if (route.hasAnyFeatures(preferredFeatures) - || sessionInfo.getSelectedRoutes().contains(route.getId()) - || sessionInfo.getTransferableRoutes().contains(route.getId())) { - routes.add(route); - } - } - } - return routes; + return getFilteredRoutes(sessionInfo, /*includeSelectedRoutes=*/true, + null); } /** @@ -281,27 +270,70 @@ public final class MediaRouter2Manager { */ @NonNull public List<MediaRoute2Info> getTransferableRoutes(@NonNull RoutingSessionInfo sessionInfo) { + return getFilteredRoutes(sessionInfo, /*includeSelectedRoutes=*/false, + (route) -> sessionInfo.isSystemSession() ^ route.isSystemRoute()); + } + + private List<MediaRoute2Info> getSortedRoutes(RouteDiscoveryPreference preference) { + if (!preference.shouldRemoveDuplicates()) { + synchronized (mRoutesLock) { + return List.copyOf(mRoutes.values()); + } + } + Map<String, Integer> packagePriority = new ArrayMap<>(); + int count = preference.getDeduplicationPackageOrder().size(); + for (int i = 0; i < count; i++) { + // the last package will have 1 as the priority + packagePriority.put(preference.getDeduplicationPackageOrder().get(i), count - i); + } + ArrayList<MediaRoute2Info> routes; + synchronized (mRoutesLock) { + routes = new ArrayList<>(mRoutes.values()); + } + // take the negative for descending order + routes.sort(Comparator.comparingInt( + r -> -packagePriority.getOrDefault(r.getPackageName(), 0))); + return routes; + } + + private List<MediaRoute2Info> getFilteredRoutes(@NonNull RoutingSessionInfo sessionInfo, + boolean includeSelectedRoutes, + @Nullable Predicate<MediaRoute2Info> additionalFilter) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); List<MediaRoute2Info> routes = new ArrayList<>(); + Set<String> deduplicationIdSet = new ArraySet<>(); String packageName = sessionInfo.getClientPackageName(); - List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName); - if (preferredFeatures == null) { - preferredFeatures = Collections.emptyList(); - } - synchronized (mRoutesLock) { - for (MediaRoute2Info route : mRoutes.values()) { - if (sessionInfo.getTransferableRoutes().contains(route.getId())) { - routes.add(route); + RouteDiscoveryPreference discoveryPreference = + mDiscoveryPreferenceMap.getOrDefault(packageName, RouteDiscoveryPreference.EMPTY); + + for (MediaRoute2Info route : getSortedRoutes(discoveryPreference)) { + if (sessionInfo.getTransferableRoutes().contains(route.getId()) + || (includeSelectedRoutes + && sessionInfo.getSelectedRoutes().contains(route.getId()))) { + routes.add(route); + continue; + } + if (!route.hasAllFeatures(discoveryPreference.getRequiredFeatures()) + || !route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) { + continue; + } + if (!discoveryPreference.getAllowedPackages().isEmpty() + && !discoveryPreference.getAllowedPackages() + .contains(route.getPackageName())) { + continue; + } + if (additionalFilter != null && !additionalFilter.test(route)) { + continue; + } + if (discoveryPreference.shouldRemoveDuplicates()) { + if (Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) { continue; } - // Add Phone -> Cast and Cast -> Phone - if (route.hasAnyFeatures(preferredFeatures) - && (sessionInfo.isSystemSession() ^ route.isSystemRoute())) { - routes.add(route); - } + deduplicationIdSet.addAll(route.getDeduplicationIds()); } + routes.add(route); } return routes; } @@ -310,44 +342,10 @@ public final class MediaRouter2Manager { * Returns the preferred features of the specified package name. */ @NonNull - public List<String> getPreferredFeatures(@NonNull String packageName) { - Objects.requireNonNull(packageName, "packageName must not be null"); - - List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName); - if (preferredFeatures == null) { - preferredFeatures = Collections.emptyList(); - } - return preferredFeatures; - } - - /** - * Returns a list of routes which are related to the given package name in the given route list. - */ - @NonNull - public List<MediaRoute2Info> filterRoutesForPackage(@NonNull List<MediaRoute2Info> routes, - @NonNull String packageName) { - Objects.requireNonNull(routes, "routes must not be null"); + public RouteDiscoveryPreference getDiscoveryPreference(@NonNull String packageName) { Objects.requireNonNull(packageName, "packageName must not be null"); - List<RoutingSessionInfo> sessions = getRoutingSessions(packageName); - RoutingSessionInfo sessionInfo = sessions.get(sessions.size() - 1); - - List<MediaRoute2Info> result = new ArrayList<>(); - List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName); - if (preferredFeatures == null) { - preferredFeatures = Collections.emptyList(); - } - - synchronized (mRoutesLock) { - for (MediaRoute2Info route : routes) { - if (route.hasAnyFeatures(preferredFeatures) - || sessionInfo.getSelectedRoutes().contains(route.getId()) - || sessionInfo.getTransferableRoutes().contains(route.getId())) { - result.add(route); - } - } - } - return result; + return mDiscoveryPreferenceMap.getOrDefault(packageName, RouteDiscoveryPreference.EMPTY); } /** @@ -713,19 +711,19 @@ public final class MediaRouter2Manager { } } - void updatePreferredFeatures(String packageName, List<String> preferredFeatures) { - if (preferredFeatures == null) { - mPreferredFeaturesMap.remove(packageName); + void updateDiscoveryPreference(String packageName, RouteDiscoveryPreference preference) { + if (preference == null) { + mDiscoveryPreferenceMap.remove(packageName); return; } - List<String> prevFeatures = mPreferredFeaturesMap.put(packageName, preferredFeatures); - if ((prevFeatures == null && preferredFeatures.size() == 0) - || Objects.equals(preferredFeatures, prevFeatures)) { + RouteDiscoveryPreference prevPreference = + mDiscoveryPreferenceMap.put(packageName, preference); + if (Objects.equals(preference, prevPreference)) { return; } for (CallbackRecord record : mCallbackRecords) { record.mExecutor.execute(() -> record.mCallback - .onPreferredFeaturesChanged(packageName, preferredFeatures)); + .onDiscoveryPreferenceChanged(packageName, preference)); } } @@ -1047,6 +1045,17 @@ public final class MediaRouter2Manager { @NonNull List<String> preferredFeatures) {} /** + * Called when the preferred route features of an app is changed. + * + * @param packageName the package name of the application + * @param discoveryPreference the new discovery preference set by the application. + */ + default void onDiscoveryPreferenceChanged(@NonNull String packageName, + @NonNull RouteDiscoveryPreference discoveryPreference) { + onPreferredFeaturesChanged(packageName, discoveryPreference.getPreferredFeatures()); + } + + /** * Called when a previous request has failed. * * @param reason the reason that the request has failed. Can be one of followings: @@ -1125,9 +1134,10 @@ public final class MediaRouter2Manager { } @Override - public void notifyPreferredFeaturesChanged(String packageName, List<String> features) { - mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updatePreferredFeatures, - MediaRouter2Manager.this, packageName, features)); + public void notifyDiscoveryPreferenceChanged(String packageName, + RouteDiscoveryPreference discoveryPreference) { + mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updateDiscoveryPreference, + MediaRouter2Manager.this, packageName, discoveryPreference)); } @Override diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java index 37fee8466859..004501812ff6 100644 --- a/media/java/android/media/RouteDiscoveryPreference.java +++ b/media/java/android/media/RouteDiscoveryPreference.java @@ -64,6 +64,13 @@ public final class RouteDiscoveryPreference implements Parcelable { @NonNull private final List<String> mPreferredFeatures; + @NonNull + private final List<String> mRequiredFeatures; + @NonNull + private final List<String> mPackagesOrder; + @NonNull + private final List<String> mAllowedPackages; + private final boolean mShouldPerformActiveScan; @Nullable private final Bundle mExtras; @@ -78,12 +85,18 @@ public final class RouteDiscoveryPreference implements Parcelable { RouteDiscoveryPreference(@NonNull Builder builder) { mPreferredFeatures = builder.mPreferredFeatures; + mRequiredFeatures = builder.mRequiredFeatures; + mPackagesOrder = builder.mPackageOrder; + mAllowedPackages = builder.mAllowedPackages; mShouldPerformActiveScan = builder.mActiveScan; mExtras = builder.mExtras; } RouteDiscoveryPreference(@NonNull Parcel in) { mPreferredFeatures = in.createStringArrayList(); + mRequiredFeatures = in.createStringArrayList(); + mPackagesOrder = in.createStringArrayList(); + mAllowedPackages = in.createStringArrayList(); mShouldPerformActiveScan = in.readBoolean(); mExtras = in.readBundle(); } @@ -96,6 +109,8 @@ public final class RouteDiscoveryPreference implements Parcelable { * {@link MediaRoute2Info#FEATURE_LIVE_AUDIO}, {@link MediaRoute2Info#FEATURE_LIVE_VIDEO}, * or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK} or custom features defined by a provider. * </p> + * + * @see #getRequiredFeatures() */ @NonNull public List<String> getPreferredFeatures() { @@ -103,6 +118,47 @@ public final class RouteDiscoveryPreference implements Parcelable { } /** + * Gets the required features of routes that media router would like to discover. + * <p> + * Routes that have all the required features will be discovered. + * They may include predefined features such as + * {@link MediaRoute2Info#FEATURE_LIVE_AUDIO}, {@link MediaRoute2Info#FEATURE_LIVE_VIDEO}, + * or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK} or custom features defined by a provider. + * + * @see #getPreferredFeatures() + */ + @NonNull + public List<String> getRequiredFeatures() { + return mRequiredFeatures; + } + + /** + * Gets the ordered list of package names used to remove duplicate routes. + * <p> + * Duplicate route removal is enabled if the returned list is non-empty. Routes are deduplicated + * based on their {@link MediaRoute2Info#getDeduplicationIds() deduplication IDs}. If two routes + * have a deduplication ID in common, only the route from the provider whose package name is + * first in the provided list will remain. + * + * @see #shouldRemoveDuplicates() + */ + @NonNull + public List<String> getDeduplicationPackageOrder() { + return mPackagesOrder; + } + + /** + * Gets the list of allowed packages. + * <p> + * If it's not empty, it will only discover routes from the provider whose package name + * belongs to the list. + */ + @NonNull + public List<String> getAllowedPackages() { + return mAllowedPackages; + } + + /** * Gets whether active scanning should be performed. * <p> * If any of discovery preferences sets this as {@code true}, active scanning will @@ -114,6 +170,15 @@ public final class RouteDiscoveryPreference implements Parcelable { } /** + * Gets whether duplicate routes removal is enabled. + * + * @see #getDeduplicationPackageOrder() + */ + public boolean shouldRemoveDuplicates() { + return !mPackagesOrder.isEmpty(); + } + + /** * @hide */ public Bundle getExtras() { @@ -128,6 +193,9 @@ public final class RouteDiscoveryPreference implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeStringList(mPreferredFeatures); + dest.writeStringList(mRequiredFeatures); + dest.writeStringList(mPackagesOrder); + dest.writeStringList(mAllowedPackages); dest.writeBoolean(mShouldPerformActiveScan); dest.writeBundle(mExtras); } @@ -155,14 +223,17 @@ public final class RouteDiscoveryPreference implements Parcelable { return false; } RouteDiscoveryPreference other = (RouteDiscoveryPreference) o; - //TODO: Make this order-free return Objects.equals(mPreferredFeatures, other.mPreferredFeatures) + && Objects.equals(mRequiredFeatures, other.mRequiredFeatures) + && Objects.equals(mPackagesOrder, other.mPackagesOrder) + && Objects.equals(mAllowedPackages, other.mAllowedPackages) && mShouldPerformActiveScan == other.mShouldPerformActiveScan; } @Override public int hashCode() { - return Objects.hash(mPreferredFeatures, mShouldPerformActiveScan); + return Objects.hash(mPreferredFeatures, mRequiredFeatures, mPackagesOrder, mAllowedPackages, + mShouldPerformActiveScan); } /** @@ -170,13 +241,21 @@ public final class RouteDiscoveryPreference implements Parcelable { */ public static final class Builder { List<String> mPreferredFeatures; + List<String> mRequiredFeatures; + List<String> mPackageOrder; + List<String> mAllowedPackages; + boolean mActiveScan; + Bundle mExtras; public Builder(@NonNull List<String> preferredFeatures, boolean activeScan) { Objects.requireNonNull(preferredFeatures, "preferredFeatures must not be null"); mPreferredFeatures = preferredFeatures.stream().filter(str -> !TextUtils.isEmpty(str)) .collect(Collectors.toList()); + mRequiredFeatures = List.of(); + mPackageOrder = List.of(); + mAllowedPackages = List.of(); mActiveScan = activeScan; } @@ -184,12 +263,15 @@ public final class RouteDiscoveryPreference implements Parcelable { Objects.requireNonNull(preference, "preference must not be null"); mPreferredFeatures = preference.getPreferredFeatures(); + mRequiredFeatures = preference.getRequiredFeatures(); + mPackageOrder = preference.getDeduplicationPackageOrder(); + mAllowedPackages = preference.getAllowedPackages(); mActiveScan = preference.shouldPerformActiveScan(); mExtras = preference.getExtras(); } /** - * A constructor to combine all of the preferences into a single preference. + * A constructor to combine all the preferences into a single preference. * It ignores extras of preferences. * * @hide @@ -224,6 +306,30 @@ public final class RouteDiscoveryPreference implements Parcelable { } /** + * Sets the required route features to discover. + */ + @NonNull + public Builder setRequiredFeatures(@NonNull List<String> requiredFeatures) { + Objects.requireNonNull(requiredFeatures, "preferredFeatures must not be null"); + mRequiredFeatures = requiredFeatures.stream().filter(str -> !TextUtils.isEmpty(str)) + .collect(Collectors.toList()); + return this; + } + + /** + * Sets the list of package names of providers that media router would like to discover. + * <p> + * If it's non-empty, media router only discovers route from the provider in the list. + * The default value is empty, which discovers routes from all providers. + */ + @NonNull + public Builder setAllowedPackages(@NonNull List<String> allowedPackages) { + Objects.requireNonNull(allowedPackages, "allowedPackages must not be null"); + mAllowedPackages = List.copyOf(allowedPackages); + return this; + } + + /** * Sets if active scanning should be performed. * <p> * Since active scanning uses more system resources, set this as {@code true} only @@ -237,6 +343,24 @@ public final class RouteDiscoveryPreference implements Parcelable { } /** + * Sets the order of packages to use when removing duplicate routes. + * <p> + * Routes are deduplicated based on their + * {@link MediaRoute2Info#getDeduplicationIds() deduplication IDs}. + * If two routes have a deduplication ID in common, only the route from the provider whose + * package name is first in the provided list will remain. + * + * @param packageOrder ordered list of package names used to remove duplicate routes, or an + * empty list if deduplication should not be enabled. + */ + @NonNull + public Builder setDeduplicationPackageOrder(@NonNull List<String> packageOrder) { + Objects.requireNonNull(packageOrder, "packageOrder must not be null"); + mPackageOrder = List.copyOf(packageOrder); + return this; + } + + /** * Sets the extras of the route. * @hide */ diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp index 223bdcdd9c95..327b1fb2f5d8 100644 --- a/packages/ConnectivityT/framework-t/Android.bp +++ b/packages/ConnectivityT/framework-t/Android.bp @@ -39,7 +39,6 @@ filegroup { "src/android/net/TrafficStats.java", "src/android/net/UnderlyingNetworkInfo.*", "src/android/net/netstats/**/*.*", - "src/com/android/server/NetworkManagementSocketTagger.java", ], path: "src", visibility: [ @@ -176,3 +175,34 @@ filegroup { "//packages/modules/Connectivity:__subpackages__", ], } + +cc_library_shared { + name: "libframework-connectivity-tiramisu-jni", + min_sdk_version: "30", + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + // Don't warn about S API usage even with + // min_sdk 30: the library is only loaded + // on S+ devices + "-Wno-unguarded-availability", + "-Wthread-safety", + ], + srcs: [ + "jni/android_net_TrafficStats.cpp", + "jni/onload.cpp", + ], + shared_libs: [ + "liblog", + ], + static_libs: [ + "libnativehelper_compat_libc++", + ], + stl: "none", + apex_available: [ + "com.android.tethering", + // TODO: remove when ConnectivityT moves to APEX. + "//apex_available:platform", + ], +} diff --git a/packages/ConnectivityT/framework-t/jni/android_net_TrafficStats.cpp b/packages/ConnectivityT/framework-t/jni/android_net_TrafficStats.cpp new file mode 100644 index 000000000000..f3c58b112f0d --- /dev/null +++ b/packages/ConnectivityT/framework-t/jni/android_net_TrafficStats.cpp @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#include <android/file_descriptor_jni.h> +#include <android/multinetwork.h> +#include <nativehelper/JNIHelp.h> + +namespace android { + +static jint tagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor, jint tag, jint uid) { + int fd = AFileDescriptor_getFd(env, fileDescriptor); + if (fd == -1) return -EBADF; + return android_tag_socket_with_uid(fd, tag, uid); +} + +static jint untagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor) { + int fd = AFileDescriptor_getFd(env, fileDescriptor); + if (fd == -1) return -EBADF; + return android_untag_socket(fd); +} + +static const JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*) tagSocketFd }, + { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*) untagSocketFd }, +}; + +int register_android_net_TrafficStats(JNIEnv* env) { + return jniRegisterNativeMethods(env, "android/net/TrafficStats", gMethods, NELEM(gMethods)); +} + +}; // namespace android + diff --git a/packages/ConnectivityT/framework-t/jni/onload.cpp b/packages/ConnectivityT/framework-t/jni/onload.cpp new file mode 100644 index 000000000000..1fb42c63477e --- /dev/null +++ b/packages/ConnectivityT/framework-t/jni/onload.cpp @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#define LOG_TAG "FrameworkConnectivityJNI" + +#include <log/log.h> +#include <nativehelper/JNIHelp.h> + +namespace android { + +int register_android_net_TrafficStats(JNIEnv* env); + +extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed"); + return JNI_ERR; + } + + if (register_android_net_TrafficStats(env) < 0) return JNI_ERR; + + return JNI_VERSION_1_6; +} + +}; // namespace android + diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java index c2f0cdfb048c..bc836d857e3e 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java +++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java @@ -31,12 +31,9 @@ import android.media.MediaPlayer; import android.os.Binder; import android.os.Build; import android.os.RemoteException; +import android.os.StrictMode; import android.util.Log; -import com.android.server.NetworkManagementSocketTagger; - -import dalvik.system.SocketTagger; - import java.io.FileDescriptor; import java.io.IOException; import java.net.DatagramSocket; @@ -56,6 +53,10 @@ import java.net.SocketException; * use {@link NetworkStatsManager} instead. */ public class TrafficStats { + static { + System.loadLibrary("framework-connectivity-tiramisu-jni"); + } + private static final String TAG = TrafficStats.class.getSimpleName(); /** * The return value to indicate that the device does not support the statistic. @@ -232,9 +233,68 @@ public class TrafficStats { */ @SystemApi(client = MODULE_LIBRARIES) public static void attachSocketTagger() { - NetworkManagementSocketTagger.install(); + dalvik.system.SocketTagger.set(new SocketTagger()); + } + + private static class SocketTagger extends dalvik.system.SocketTagger { + + // TODO: set to false + private static final boolean LOGD = true; + + SocketTagger() { + } + + @Override + public void tag(FileDescriptor fd) throws SocketException { + final UidTag tagInfo = sThreadUidTag.get(); + if (LOGD) { + Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x" + + Integer.toHexString(tagInfo.tag) + ", statsUid=" + tagInfo.uid); + } + if (tagInfo.tag == -1) { + StrictMode.noteUntaggedSocket(); + } + + if (tagInfo.tag == -1 && tagInfo.uid == -1) return; + final int errno = native_tagSocketFd(fd, tagInfo.tag, tagInfo.uid); + if (errno < 0) { + Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", " + + tagInfo.tag + ", " + + tagInfo.uid + ") failed with errno" + errno); + } + } + + @Override + public void untag(FileDescriptor fd) throws SocketException { + if (LOGD) { + Log.i(TAG, "untagSocket(" + fd.getInt$() + ")"); + } + + final UidTag tagInfo = sThreadUidTag.get(); + if (tagInfo.tag == -1 && tagInfo.uid == -1) return; + + final int errno = native_untagSocketFd(fd); + if (errno < 0) { + Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno); + } + } + } + + private static native int native_tagSocketFd(FileDescriptor fd, int tag, int uid); + private static native int native_untagSocketFd(FileDescriptor fd); + + private static class UidTag { + public int tag = -1; + public int uid = -1; } + private static ThreadLocal<UidTag> sThreadUidTag = new ThreadLocal<UidTag>() { + @Override + protected UidTag initialValue() { + return new UidTag(); + } + }; + /** * Set active tag to use when accounting {@link Socket} traffic originating * from the current thread. Only one active tag per thread is supported. @@ -249,7 +309,7 @@ public class TrafficStats { * @see #clearThreadStatsTag() */ public static void setThreadStatsTag(int tag) { - NetworkManagementSocketTagger.setThreadSocketStatsTag(tag); + getAndSetThreadStatsTag(tag); } /** @@ -267,7 +327,9 @@ public class TrafficStats { * restore any existing values after a nested operation is finished */ public static int getAndSetThreadStatsTag(int tag) { - return NetworkManagementSocketTagger.setThreadSocketStatsTag(tag); + final int old = sThreadUidTag.get().tag; + sThreadUidTag.get().tag = tag; + return old; } /** @@ -327,7 +389,7 @@ public class TrafficStats { * @see #setThreadStatsTag(int) */ public static int getThreadStatsTag() { - return NetworkManagementSocketTagger.getThreadSocketStatsTag(); + return sThreadUidTag.get().tag; } /** @@ -337,7 +399,7 @@ public class TrafficStats { * @see #setThreadStatsTag(int) */ public static void clearThreadStatsTag() { - NetworkManagementSocketTagger.setThreadSocketStatsTag(-1); + sThreadUidTag.get().tag = -1; } /** @@ -357,7 +419,7 @@ public class TrafficStats { */ @SuppressLint("RequiresPermission") public static void setThreadStatsUid(int uid) { - NetworkManagementSocketTagger.setThreadSocketStatsUid(uid); + sThreadUidTag.get().uid = uid; } /** @@ -368,7 +430,7 @@ public class TrafficStats { * @see #setThreadStatsUid(int) */ public static int getThreadStatsUid() { - return NetworkManagementSocketTagger.getThreadSocketStatsUid(); + return sThreadUidTag.get().uid; } /** @@ -395,7 +457,7 @@ public class TrafficStats { */ @SuppressLint("RequiresPermission") public static void clearThreadStatsUid() { - NetworkManagementSocketTagger.setThreadSocketStatsUid(-1); + setThreadStatsUid(-1); } /** diff --git a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java deleted file mode 100644 index 8bb12a6defe5..000000000000 --- a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.os.StrictMode; -import android.util.Log; - -import dalvik.system.SocketTagger; - -import java.io.FileDescriptor; -import java.net.SocketException; - -/** - * Assigns tags to sockets for traffic stats. - * @hide - */ -public final class NetworkManagementSocketTagger extends SocketTagger { - private static final String TAG = "NetworkManagementSocketTagger"; - private static final boolean LOGD = false; - - private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() { - @Override - protected SocketTags initialValue() { - return new SocketTags(); - } - }; - - public static void install() { - SocketTagger.set(new NetworkManagementSocketTagger()); - } - - public static int setThreadSocketStatsTag(int tag) { - final int old = threadSocketTags.get().statsTag; - threadSocketTags.get().statsTag = tag; - return old; - } - - public static int getThreadSocketStatsTag() { - return threadSocketTags.get().statsTag; - } - - public static int setThreadSocketStatsUid(int uid) { - final int old = threadSocketTags.get().statsUid; - threadSocketTags.get().statsUid = uid; - return old; - } - - public static int getThreadSocketStatsUid() { - return threadSocketTags.get().statsUid; - } - - @Override - public void tag(FileDescriptor fd) throws SocketException { - final SocketTags options = threadSocketTags.get(); - if (LOGD) { - Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x" - + Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid); - } - if (options.statsTag == -1) { - StrictMode.noteUntaggedSocket(); - } - // TODO: skip tagging when options would be no-op - tagSocketFd(fd, options.statsTag, options.statsUid); - } - - private void tagSocketFd(FileDescriptor fd, int tag, int uid) { - if (tag == -1 && uid == -1) return; - - final int errno = native_tagSocketFd(fd, tag, uid); - if (errno < 0) { - Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", " - + tag + ", " - + uid + ") failed with errno" + errno); - } - } - - @Override - public void untag(FileDescriptor fd) throws SocketException { - if (LOGD) { - Log.i(TAG, "untagSocket(" + fd.getInt$() + ")"); - } - unTagSocketFd(fd); - } - - private void unTagSocketFd(FileDescriptor fd) { - final SocketTags options = threadSocketTags.get(); - if (options.statsTag == -1 && options.statsUid == -1) return; - - final int errno = native_untagSocketFd(fd); - if (errno < 0) { - Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno); - } - } - - public static class SocketTags { - public int statsTag = -1; - public int statsUid = -1; - } - - /** - * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming - * format like {@code 0x7fffffff00000000}. - */ - public static int kernelToTag(String string) { - int length = string.length(); - if (length > 10) { - return Long.decode(string.substring(0, length - 8)).intValue(); - } else { - return 0; - } - } - - private static native int native_tagSocketFd(FileDescriptor fd, int tag, int uid); - private static native int native_untagSocketFd(FileDescriptor fd); -} diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java index 17f3455d20a2..668d1cba921b 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java @@ -22,8 +22,6 @@ import static android.net.NetworkStats.TAG_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; -import static com.android.server.NetworkManagementSocketTagger.kernelToTag; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -470,6 +468,19 @@ public class NetworkStatsFactory { } /** + * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming + * format like {@code 0x7fffffff00000000}. + */ + public static int kernelToTag(String string) { + int length = string.length(); + if (length > 10) { + return Long.decode(string.substring(0, length - 8)).intValue(); + } else { + return 0; + } + } + + /** * Parse statistics from file into given {@link NetworkStats} object. Values * are expected to monotonically increase since device boot. */ diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java index 1d0ae9912d97..b65e976c4829 100755 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java @@ -37,7 +37,7 @@ import android.view.View; import android.widget.Button; import com.android.internal.app.AlertActivity; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import java.io.File; import java.io.FileInputStream; @@ -154,8 +154,8 @@ public class InstallInstalling extends AlertActivity { final PackageLite pkg = result.getResult(); params.setAppPackageName(pkg.getPackageName()); params.setInstallLocation(pkg.getInstallLocation()); - params.setSize( - PackageHelper.calculateInstalledSize(pkg, params.abiOverride)); + params.setSize(InstallLocationUtils.calculateInstalledSize(pkg, + params.abiOverride)); } } catch (IOException e) { Log.e(LOG_TAG, diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 45f8f1debe34..af6a658f362a 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1549,6 +1549,17 @@ <!-- Content description of the no calling for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_no_calling">No calling.</string> + <!-- Screensaver overlay which displays the time. [CHAR LIMIT=20] --> + <string name="dream_complication_title_time">Time</string> + <!-- Screensaver overlay which displays the date. [CHAR LIMIT=20] --> + <string name="dream_complication_title_date">Date</string> + <!-- Screensaver overlay which displays the weather. [CHAR LIMIT=20] --> + <string name="dream_complication_title_weather">Weather</string> + <!-- Screensaver overlay which displays air quality. [CHAR LIMIT=20] --> + <string name="dream_complication_title_aqi">Air Quality</string> + <!-- Screensaver overlay which displays cast info. [CHAR LIMIT=20] --> + <string name="dream_complication_title_cast_info">Cast Info</string> + <!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] --> <string name="avatar_picker_title">Choose a profile picture</string> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java index 3f322d61e036..f7b297461f15 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java @@ -16,8 +16,11 @@ package com.android.settingslib; +import static android.app.admin.DevicePolicyResources.Strings.Settings.CONTROLLED_BY_ADMIN_SUMMARY; + import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; +import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.res.TypedArray; import android.os.Build; @@ -102,8 +105,11 @@ public class RestrictedPreferenceHelper { if (mDisabledSummary) { final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary); if (summaryView != null) { - final CharSequence disabledText = summaryView.getContext().getText( - R.string.disabled_by_admin_summary_text); + final CharSequence disabledText = mContext + .getSystemService(DevicePolicyManager.class) + .getString(CONTROLLED_BY_ADMIN_SUMMARY, + () -> summaryView.getContext().getString( + R.string.disabled_by_admin_summary_text)); if (mDisabledByAdmin) { summaryView.setText(disabledText); } else if (mDisabledByAppOps) { diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index d73e45eba5dc..883e0806f5f5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -1,7 +1,10 @@ package com.android.settingslib; +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USER_LABEL; + import android.annotation.ColorInt; import android.annotation.Nullable; +import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -124,7 +127,8 @@ public class Utils { String name = info != null ? info.name : null; if (info.isManagedProfile()) { // We use predefined values for managed profiles - return context.getString(R.string.managed_user_title); + return context.getSystemService(DevicePolicyManager.class).getString( + WORK_PROFILE_USER_LABEL, () -> context.getString(R.string.managed_user_title)); } else if (info.isGuest()) { name = context.getString(R.string.user_guest); } diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java index 46e31ceb7485..6bf43e528009 100644 --- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java +++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java @@ -34,6 +34,7 @@ import android.os.ServiceManager; import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; @@ -50,6 +51,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -292,6 +294,11 @@ public class DreamBackend { } } + /** Returns whether a particular complication is enabled */ + public boolean isComplicationEnabled(@ComplicationType int complication) { + return getEnabledComplications().contains(complication); + } + /** Gets all complications which have been enabled by the user. */ public Set<Integer> getEnabledComplications() { final String enabledComplications = Settings.Secure.getString( @@ -331,6 +338,35 @@ public class DreamBackend { convertToString(enabledComplications)); } + /** + * Gets the title of a particular complication type to be displayed to the user. If there + * is no title, null is returned. + */ + @Nullable + public CharSequence getComplicationTitle(@ComplicationType int complicationType) { + int res = 0; + switch (complicationType) { + case COMPLICATION_TYPE_TIME: + res = R.string.dream_complication_title_time; + break; + case COMPLICATION_TYPE_DATE: + res = R.string.dream_complication_title_date; + break; + case COMPLICATION_TYPE_WEATHER: + res = R.string.dream_complication_title_weather; + break; + case COMPLICATION_TYPE_AIR_QUALITY: + res = R.string.dream_complication_title_aqi; + break; + case COMPLICATION_TYPE_CAST_INFO: + res = R.string.dream_complication_title_cast_info; + break; + default: + return null; + } + return mContext.getString(res); + } + private static String convertToString(Set<Integer> set) { return set.stream() .map(String::valueOf) @@ -338,6 +374,9 @@ public class DreamBackend { } private static Set<Integer> parseFromString(String string) { + if (TextUtils.isEmpty(string)) { + return new HashSet<>(); + } return Arrays.stream(string.split(",")) .map(Integer::parseInt) .collect(Collectors.toSet()); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index dc7632dbc4a9..b851232ace82 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -46,7 +46,7 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import com.android.internal.telephony.Phone; import com.android.internal.telephony.RILConstants; import com.android.internal.util.XmlUtils; @@ -783,7 +783,7 @@ class DatabaseHelper extends SQLiteOpenHelper { + " VALUES(?,?);"); loadSetting(stmt, Global.SET_INSTALL_LOCATION, 0); loadSetting(stmt, Global.DEFAULT_INSTALL_LOCATION, - PackageHelper.APP_INSTALL_AUTO); + InstallLocationUtils.APP_INSTALL_AUTO); db.setTransactionSuccessful(); } finally { db.endTransaction(); @@ -2534,7 +2534,7 @@ class DatabaseHelper extends SQLiteOpenHelper { loadSetting(stmt, Settings.Global.SET_INSTALL_LOCATION, 0); loadSetting(stmt, Settings.Global.DEFAULT_INSTALL_LOCATION, - PackageHelper.APP_INSTALL_AUTO); + InstallLocationUtils.APP_INSTALL_AUTO); // Set default cdma emergency tone loadSetting(stmt, Settings.Global.EMERGENCY_TONE, 0); diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index f3a87afbcd7c..55bf6e531fa2 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -301,6 +301,7 @@ <!-- For clipboard overlay --> <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" /> + <uses-permission android:name="android.permission.SET_CLIP_SOURCE" /> <!-- To change system language (HDMI CEC) --> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml index 2a3761e4ca05..7e31909613ee 100644 --- a/packages/SystemUI/res/layout/clipboard_overlay.xml +++ b/packages/SystemUI/res/layout/clipboard_overlay.xml @@ -17,6 +17,7 @@ <com.android.systemui.clipboardoverlay.DraggableConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" + android:theme="@style/Screenshot" android:alpha="0" android:layout_width="match_parent" android:layout_height="match_parent"> @@ -50,7 +51,8 @@ <LinearLayout android:id="@+id/actions" android:layout_width="wrap_content" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:animateLayoutChanges="true"> <include layout="@layout/screenshot_action_chip" android:id="@+id/remote_copy_chip"/> <include layout="@layout/screenshot_action_chip" @@ -64,7 +66,7 @@ android:layout_marginStart="@dimen/overlay_offset_x" android:layout_marginBottom="@dimen/overlay_offset_y" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toBottomOf="@id/actions_container_background" android:elevation="@dimen/overlay_preview_elevation" app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end" app:layout_constraintTop_toTopOf="@id/clipboard_preview_top" diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index db699242a061..de136de9dd5f 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -85,7 +85,7 @@ Contract: Pixel with fillColor blended over backgroundColor blended over translucent should equal to singleToneColor blended over translucent. --> <declare-styleable name="TonedIcon"> - <attr name="backgroundColor" format="integer" /> + <attr name="iconBackgroundColor" format="integer" /> <attr name="fillColor" format="integer" /> <attr name="singleToneColor" format="integer" /> <attr name="homeHandleColor" format="integer" /> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index ac9873938a29..57f1f3f1606c 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -404,19 +404,19 @@ </style> <style name="DualToneLightTheme"> - <item name="backgroundColor">@color/light_mode_icon_color_dual_tone_background</item> + <item name="iconBackgroundColor">@color/light_mode_icon_color_dual_tone_background</item> <item name="fillColor">@color/light_mode_icon_color_dual_tone_fill</item> <item name="singleToneColor">@color/light_mode_icon_color_single_tone</item> <item name="homeHandleColor">@color/navigation_bar_home_handle_light_color</item> </style> <style name="DualToneDarkTheme"> - <item name="backgroundColor">@color/dark_mode_icon_color_dual_tone_background</item> + <item name="iconBackgroundColor">@color/dark_mode_icon_color_dual_tone_background</item> <item name="fillColor">@color/dark_mode_icon_color_dual_tone_fill</item> <item name="singleToneColor">@color/dark_mode_icon_color_single_tone</item> <item name="homeHandleColor">@color/navigation_bar_home_handle_dark_color</item> </style> <style name="QSHeaderDarkTheme"> - <item name="backgroundColor">@color/dark_mode_qs_icon_color_dual_tone_background</item> + <item name="iconBackgroundColor">@color/dark_mode_qs_icon_color_dual_tone_background</item> <item name="fillColor">@color/dark_mode_qs_icon_color_dual_tone_fill</item> <item name="singleToneColor">@color/dark_mode_qs_icon_color_single_tone</item> </style> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt new file mode 100644 index 000000000000..ffab3cd79d7f --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.shared.animation + +import android.view.View +import android.view.ViewGroup +import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate +import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import java.lang.ref.WeakReference + +/** + * Translates items away/towards the hinge when the device is opened/closed, according to the + * direction specified in [ViewIdToTranslate.direction], for a maximum of [translationMax] when + * progresses are 0. + */ +class UnfoldConstantTranslateAnimator( + private val viewsIdToTranslate: Set<ViewIdToTranslate>, + private val progressProvider: UnfoldTransitionProgressProvider +) : TransitionProgressListener { + + private var viewsToTranslate = listOf<ViewToTranslate>() + private lateinit var rootView: ViewGroup + private var translationMax = 0f + + fun init(rootView: ViewGroup, translationMax: Float) { + this.rootView = rootView + this.translationMax = translationMax + progressProvider.addCallback(this) + } + + override fun onTransitionStarted() { + registerViewsForAnimation(rootView, viewsIdToTranslate) + } + + override fun onTransitionProgress(progress: Float) { + translateViews(progress) + } + + override fun onTransitionFinished() { + translateViews(progress = 1f) + } + + private fun translateViews(progress: Float) { + // progress == 0 -> -translationMax + // progress == 1 -> 0 + val xTrans = (progress - 1f) * translationMax + viewsToTranslate.forEach { (view, direction, shouldBeAnimated) -> + if (shouldBeAnimated()) { + view.get()?.translationX = xTrans * direction.multiplier + } + } + } + + /** Finds in [parent] all views specified by [ids] and register them for the animation. */ + private fun registerViewsForAnimation(parent: ViewGroup, ids: Set<ViewIdToTranslate>) { + viewsToTranslate = + ids.mapNotNull { (id, dir, pred) -> + parent.findViewById<View>(id)?.let { view -> + ViewToTranslate(WeakReference(view), dir, pred) + } + } + } + + /** Represents a view to animate. [rootView] should contain a view with [viewId] inside. */ + data class ViewIdToTranslate( + val viewId: Int, + val direction: Direction, + val shouldBeAnimated: () -> Boolean = { true } + ) + + private data class ViewToTranslate( + val view: WeakReference<View>, + val direction: Direction, + val shouldBeAnimated: () -> Boolean + ) + + /** Direction of the animation. */ + enum class Direction(val multiplier: Float) { + LEFT(-1f), + RIGHT(1f), + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt index cb25e1a2a40e..89d6fb5f062f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt @@ -17,11 +17,13 @@ package com.android.keyguard import android.content.Context -import android.view.View import android.view.ViewGroup import com.android.systemui.R +import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator +import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.LEFT +import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.RIGHT +import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate import com.android.systemui.unfold.SysUIUnfoldScope -import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider import javax.inject.Inject @@ -30,84 +32,37 @@ import javax.inject.Inject * the set of ids, which also dictact which direction to move and when, via a filter function. */ @SysUIUnfoldScope -class KeyguardUnfoldTransition @Inject constructor( - val context: Context, - val unfoldProgressProvider: NaturalRotationUnfoldProgressProvider +class KeyguardUnfoldTransition +@Inject +constructor( + private val context: Context, + unfoldProgressProvider: NaturalRotationUnfoldProgressProvider ) { - companion object { - final val LEFT = -1 - final val RIGHT = 1 - } + /** Certain views only need to move if they are not currently centered */ + var statusViewCentered = false private val filterSplitShadeOnly = { !statusViewCentered } private val filterNever = { true } - private val ids = setOf( - Triple(R.id.keyguard_status_area, LEFT, filterNever), - Triple(R.id.controls_button, LEFT, filterNever), - Triple(R.id.lockscreen_clock_view_large, LEFT, filterSplitShadeOnly), - Triple(R.id.lockscreen_clock_view, LEFT, filterNever), - Triple(R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly), - Triple(R.id.wallet_button, RIGHT, filterNever) - ) - private var parent: ViewGroup? = null - private var views = listOf<Triple<View, Int, () -> Boolean>>() - private var xTranslationMax = 0f - - /** - * Certain views only need to move if they are not currently centered - */ - var statusViewCentered = false - - init { - unfoldProgressProvider.addCallback( - object : TransitionProgressListener { - override fun onTransitionStarted() { - findViews() - } - - override fun onTransitionProgress(progress: Float) { - translateViews(progress) - } - - override fun onTransitionFinished() { - translateViews(1f) - } - } - ) + private val translateAnimator by lazy { + UnfoldConstantTranslateAnimator( + viewsIdToTranslate = + setOf( + ViewIdToTranslate(R.id.keyguard_status_area, LEFT, filterNever), + ViewIdToTranslate(R.id.controls_button, LEFT, filterNever), + ViewIdToTranslate(R.id.lockscreen_clock_view_large, LEFT, filterSplitShadeOnly), + ViewIdToTranslate(R.id.lockscreen_clock_view, LEFT, filterNever), + ViewIdToTranslate( + R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly), + ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever)), + progressProvider = unfoldProgressProvider) } - /** - * Relies on the [parent] to locate views to translate - */ + /** Relies on the [parent] to locate views to translate. */ fun setup(parent: ViewGroup) { - this.parent = parent - xTranslationMax = context.resources.getDimensionPixelSize( - R.dimen.keyguard_unfold_translation_x).toFloat() - } - - /** - * Manually translate views based on set direction. At the moment - * [UnfoldMoveFromCenterAnimator] exists but moves all views a dynamic distance - * from their mid-point. This code instead will only ever translate by a fixed amount. - */ - private fun translateViews(progress: Float) { - val xTrans = progress * xTranslationMax - xTranslationMax - views.forEach { - (view, direction, pred) -> if (pred()) { - view.setTranslationX(xTrans * direction) - } - } - } - - private fun findViews() { - parent?.let { p -> - views = ids.mapNotNull { - (id, direction, pred) -> p.findViewById<View>(id)?.let { - Triple(it, direction, pred) - } - } - } + val translationMax = + context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat() + translateAnimator.init(parent, translationMax) } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index f2d0427c39d3..cc10b02a8bb6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1196,6 +1196,21 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return fingerprintAllowed || faceAllowed; } + /** + * Returns whether the user is unlocked with a biometric that is currently bypassing + * the lock screen. + */ + public boolean getUserUnlockedWithBiometricAndIsBypassing(int userId) { + BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId); + BiometricAuthenticated face = mUserFaceAuthenticated.get(userId); + // fingerprint always bypasses + boolean fingerprintAllowed = fingerprint != null && fingerprint.mAuthenticated + && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric); + boolean faceAllowed = face != null && face.mAuthenticated + && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric); + return fingerprintAllowed || faceAllowed && mKeyguardBypassController.canBypass(); + } + public boolean getUserTrustIsManaged(int userId) { return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId); } diff --git a/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt b/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt index fdc3229ab8d7..2b8d3e046ffa 100644 --- a/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/DualToneHandler.kt @@ -54,11 +54,11 @@ class DualToneHandler(context: Context) { Utils.getThemeAttr(context, R.attr.lightIconTheme)) darkColor = Color( Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.singleToneColor), - Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.backgroundColor), + Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.iconBackgroundColor), Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.fillColor)) lightColor = Color( Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.singleToneColor), - Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.backgroundColor), + Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.iconBackgroundColor), Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.fillColor)) } diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java index 0e1cd51ce42c..72b40d42b7b8 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java @@ -63,7 +63,8 @@ public class ClipboardListener extends CoreStartable mClipboardOverlayController = new ClipboardOverlayController(mContext, new TimeoutHandler(mContext)); } - mClipboardOverlayController.setClipData(mClipboardManager.getPrimaryClip()); + mClipboardOverlayController.setClipData( + mClipboardManager.getPrimaryClip(), mClipboardManager.getPrimaryClipSource()); mClipboardOverlayController.setOnSessionCompleteListener(() -> { // Session is complete, free memory until it's needed again. mClipboardOverlayController = null; diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java index b6bcb871ff18..12759f489a26 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java @@ -182,6 +182,7 @@ public class ClipboardOverlayController { withWindowAttached(() -> { mWindow.setContentView(mView); updateInsets(mWindowManager.getCurrentWindowMetrics().getWindowInsets()); + mView.requestLayout(); mView.post(this::animateIn); }); @@ -213,7 +214,7 @@ public class ClipboardOverlayController { mContext.sendBroadcast(new Intent(COPY_OVERLAY_ACTION), SELF_PERMISSION); } - void setClipData(ClipData clipData) { + void setClipData(ClipData clipData, String clipSource) { reset(); if (clipData == null || clipData.getItemCount() == 0) { showTextPreview(mContext.getResources().getString( @@ -221,7 +222,7 @@ public class ClipboardOverlayController { } else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) { ClipData.Item item = clipData.getItemAt(0); if (item.getTextLinks() != null) { - AsyncTask.execute(() -> classifyText(clipData.getItemAt(0))); + AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource)); } showEditableText(item.getText()); } else if (clipData.getItemAt(0).getUri() != null) { @@ -238,7 +239,7 @@ public class ClipboardOverlayController { mOnSessionCompleteListener = runnable; } - private void classifyText(ClipData.Item item) { + private void classifyText(ClipData.Item item, String source) { ArrayList<RemoteAction> actions = new ArrayList<>(); for (TextLinks.TextLink link : item.getTextLinks().getLinks()) { TextClassification classification = mTextClassifier.classifyText( @@ -246,14 +247,14 @@ public class ClipboardOverlayController { actions.addAll(classification.getActions()); } mView.post(() -> { - for (ScreenshotActionChip chip : mActionChips) { - mActionContainer.removeView(chip); - } - mActionChips.clear(); + resetActionChips(); for (RemoteAction action : actions) { - ScreenshotActionChip chip = constructActionChip(action); - mActionContainer.addView(chip); - mActionChips.add(chip); + Intent targetIntent = action.getActionIntent().getIntent(); + if (!TextUtils.equals(source, targetIntent.getComponent().getPackageName())) { + ScreenshotActionChip chip = constructActionChip(action); + mActionContainer.addView(chip); + mActionChips.add(chip); + } } }); } @@ -451,13 +452,17 @@ public class ClipboardOverlayController { } } - private void reset() { - mView.setTranslationX(0); - mView.setAlpha(0); + private void resetActionChips() { for (ScreenshotActionChip chip : mActionChips) { mActionContainer.removeView(chip); } mActionChips.clear(); + } + + private void reset() { + mView.setTranslationX(0); + mView.setAlpha(0); + resetActionChips(); mTimeoutHandler.cancelTimeout(); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 4f4bd1e86e7c..be45a62eeba6 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -104,7 +104,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private static final int MAX_NUM_LOGGED_PREDICTIONS = 10; private static final int MAX_NUM_LOGGED_GESTURES = 10; - static final boolean DEBUG_MISSING_GESTURE = false; + // Temporary log until b/202433017 is resolved + static final boolean DEBUG_MISSING_GESTURE = true; static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture"; private ISystemGestureExclusionListener mGestureExclusionListener = @@ -318,7 +319,11 @@ public class EdgeBackGestureHandler extends CurrentUserTracker String recentsPackageName = recentsComponentName.getPackageName(); PackageManager manager = context.getPackageManager(); try { - Resources resources = manager.getResourcesForApplication(recentsPackageName); + Resources resources = manager.getResourcesForApplication( + manager.getApplicationInfo(recentsPackageName, + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.GET_SHARED_LIBRARY_FILES)); int resId = resources.getIdentifier( "gesture_blocking_activities", "array", recentsPackageName); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index abfdfaf2115f..391525e11866 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -27,6 +27,7 @@ import android.util.Log; import android.view.View; import android.view.ViewGroup; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; @@ -47,6 +48,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.Assert; import com.android.wm.shell.bubbles.Bubbles; @@ -98,6 +100,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle private final ForegroundServiceSectionController mFgsSectionController; private final NotifPipelineFlags mNotifPipelineFlags; private AssistantFeedbackController mAssistantFeedbackController; + private final KeyguardStateController mKeyguardStateController; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final Context mContext; private NotificationPresenter mPresenter; @@ -129,7 +133,9 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle DynamicChildBindController dynamicChildBindController, LowPriorityInflationHelper lowPriorityInflationHelper, AssistantFeedbackController assistantFeedbackController, - NotifPipelineFlags notifPipelineFlags) { + NotifPipelineFlags notifPipelineFlags, + KeyguardUpdateMonitor keyguardUpdateMonitor, + KeyguardStateController keyguardStateController) { mContext = context; mHandler = mainHandler; mFeatureFlags = featureFlags; @@ -149,6 +155,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle mDynamicChildBindController = dynamicChildBindController; mLowPriorityInflationHelper = lowPriorityInflationHelper; mAssistantFeedbackController = assistantFeedbackController; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mKeyguardStateController = keyguardStateController; } public void setUpWithPresenter(NotificationPresenter presenter, @@ -174,6 +182,11 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle beginUpdate(); + boolean dynamicallyUnlocked = mDynamicPrivacyController.isDynamicallyUnlocked() + && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD + && mKeyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing( + KeyguardUpdateMonitor.getCurrentUser())) + && !mKeyguardStateController.isKeyguardGoingAway(); List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications(); ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size()); final int N = activeNotifications.size(); @@ -192,7 +205,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle boolean devicePublic = mLockscreenUserManager.isLockscreenPublicMode(currentUserId); boolean userPublic = devicePublic || mLockscreenUserManager.isLockscreenPublicMode(userId); - if (userPublic && mDynamicPrivacyController.isDynamicallyUnlocked() + if (userPublic && dynamicallyUnlocked && (userId == currentUserId || userId == UserHandle.USER_ALL || !mLockscreenUserManager.needsSeparateWorkChallenge(userId))) { userPublic = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index bd948eceab9c..f56602ee2bcd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -518,6 +518,7 @@ public class StatusBarStateControllerImpl implements } private void recordHistoricalState(int newState, int lastState, boolean upcoming) { + Trace.traceCounter(Trace.TRACE_TAG_APP, "statusBarState", newState); mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE; HistoricalState state = mHistoricalRecords[mHistoryIndex]; state.mNewState = newState; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index d574cdabced6..e3d0d9802b8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -23,6 +23,7 @@ import android.os.Handler; import android.service.dreams.IDreamManager; import com.android.internal.statusbar.IStatusBarService; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.dagger.SysUISingleton; @@ -72,6 +73,7 @@ import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.tracing.ProtoTracer; @@ -214,7 +216,9 @@ public interface StatusBarDependenciesModule { DynamicChildBindController dynamicChildBindController, LowPriorityInflationHelper lowPriorityInflationHelper, AssistantFeedbackController assistantFeedbackController, - NotifPipelineFlags notifPipelineFlags) { + NotifPipelineFlags notifPipelineFlags, + KeyguardUpdateMonitor keyguardUpdateMonitor, + KeyguardStateController keyguardStateController) { return new NotificationViewHierarchyManager( context, mainHandler, @@ -231,7 +235,9 @@ public interface StatusBarDependenciesModule { dynamicChildBindController, lowPriorityInflationHelper, assistantFeedbackController, - notifPipelineFlags); + notifPipelineFlags, + keyguardUpdateMonitor, + keyguardStateController); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java index 0ce07cb99d52..22300d8c180d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java @@ -98,6 +98,8 @@ public class KeyguardCoordinator implements Coordinator { setupInvalidateNotifListCallbacks(); // Filter at the "finalize" stage so that views remain bound by PreparationCoordinator pipeline.addFinalizeFilter(mNotifFilter); + + updateSectionHeadersVisibility(); } private final NotifFilter mNotifFilter = new NotifFilter(TAG) { @@ -164,6 +166,8 @@ public class KeyguardCoordinator implements Coordinator { } } + // TODO(b/206118999): merge this class with SensitiveContentCoordinator which also depends on + // these same updates private void setupInvalidateNotifListCallbacks() { // register onKeyguardShowing callback mKeyguardStateController.addCallback(mKeyguardCallback); @@ -220,10 +224,7 @@ public class KeyguardCoordinator implements Coordinator { } private void invalidateListFromFilter(String reason) { - boolean onKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD; - boolean neverShowSections = mSectionHeaderVisibilityProvider.getNeverShowSectionHeaders(); - boolean showSections = !onKeyguard && !neverShowSections; - mSectionHeaderVisibilityProvider.setSectionHeadersVisible(showSections); + updateSectionHeadersVisibility(); mNotifFilter.invalidateList(); } @@ -235,6 +236,13 @@ public class KeyguardCoordinator implements Coordinator { 1) == 0; } + private void updateSectionHeadersVisibility() { + boolean onKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD; + boolean neverShowSections = mSectionHeaderVisibilityProvider.getNeverShowSectionHeaders(); + boolean showSections = !onKeyguard && !neverShowSections; + mSectionHeaderVisibilityProvider.setSectionHeadersVisible(showSections); + } + private final KeyguardStateController.Callback mKeyguardCallback = new KeyguardStateController.Callback() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt index a115e0400de3..9c82cb64a0d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt @@ -17,7 +17,10 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.os.UserHandle +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.notification.DynamicPrivacyController import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry @@ -26,6 +29,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator +import com.android.systemui.statusbar.policy.KeyguardStateController import dagger.Module import dagger.Provides @@ -36,9 +40,13 @@ object SensitiveContentCoordinatorModule { @CoordinatorScope fun provideCoordinator( dynamicPrivacyController: DynamicPrivacyController, - lockscreenUserManager: NotificationLockscreenUserManager + lockscreenUserManager: NotificationLockscreenUserManager, + keyguardUpdateMonitor: KeyguardUpdateMonitor, + statusBarStateController: StatusBarStateController, + keyguardStateController: KeyguardStateController ): SensitiveContentCoordinator = - SensitiveContentCoordinatorImpl(dynamicPrivacyController, lockscreenUserManager) + SensitiveContentCoordinatorImpl(dynamicPrivacyController, lockscreenUserManager, + keyguardUpdateMonitor, statusBarStateController, keyguardStateController) } /** Coordinates re-inflation and post-processing of sensitive notification content. */ @@ -46,7 +54,10 @@ interface SensitiveContentCoordinator : Coordinator private class SensitiveContentCoordinatorImpl( private val dynamicPrivacyController: DynamicPrivacyController, - private val lockscreenUserManager: NotificationLockscreenUserManager + private val lockscreenUserManager: NotificationLockscreenUserManager, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + private val statusBarStateController: StatusBarStateController, + private val keyguardStateController: KeyguardStateController ) : Invalidator("SensitiveContentInvalidator"), SensitiveContentCoordinator, DynamicPrivacyController.Listener, @@ -61,6 +72,19 @@ private class SensitiveContentCoordinatorImpl( override fun onDynamicPrivacyChanged(): Unit = invalidateList() override fun onBeforeRenderList(entries: List<ListEntry>) { + if (keyguardStateController.isKeyguardGoingAway() || + statusBarStateController.getState() == StatusBarState.KEYGUARD && + keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing( + KeyguardUpdateMonitor.getCurrentUser())) { + // don't update yet if: + // - the keyguard is currently going away + // - LS is about to be dismissed by a biometric that bypasses LS (avoid notif flash) + + // TODO(b/206118999): merge this class with KeyguardCoordinator which ensures the + // dependent state changes invalidate the pipeline + return + } + val currentUserId = lockscreenUserManager.currentUserId val devicePublic = lockscreenUserManager.isLockscreenPublicMode(currentUserId) val deviceSensitive = devicePublic && diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt index 28cd28594c3e..386e2d31380c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt @@ -104,19 +104,14 @@ class ShadeViewDiffer( views.remove(childNode.controller.view) } - if (childCompletelyRemoved && parentSpec == null) { - // If both the child and the parent are being removed at the same time, then - // keep the child attached to the parent for animation purposes - logger.logSkippingDetach(childNode.label, parentNode.label) - } else { - logger.logDetachingChild( - childNode.label, - !childCompletelyRemoved, - parentNode.label, - newParentNode?.label) - parentNode.removeChild(childNode, !childCompletelyRemoved) - childNode.parent = null - } + logger.logDetachingChild( + key = childNode.label, + isTransfer = !childCompletelyRemoved, + isParentRemoved = parentSpec == null, + oldParent = parentNode.label, + newParent = newParentNode?.label) + parentNode.removeChild(childNode, isTransfer = !childCompletelyRemoved) + childNode.parent = null } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt index d27455004c01..4c0357243d48 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt @@ -28,25 +28,18 @@ class ShadeViewDifferLogger @Inject constructor( fun logDetachingChild( key: String, isTransfer: Boolean, + isParentRemoved: Boolean, oldParent: String?, newParent: String? ) { buffer.log(TAG, LogLevel.DEBUG, { str1 = key bool1 = isTransfer + bool2 = isParentRemoved str2 = oldParent str3 = newParent }, { - "Detach $str1 isTransfer=$bool1 oldParent=$str2 newParent=$str3" - }) - } - - fun logSkippingDetach(key: String, parent: String?) { - buffer.log(TAG, LogLevel.DEBUG, { - str1 = key - str2 = parent - }, { - "Skipping detach of $str1 because its parent $str2 is also being detached" + "Detach $str1 isTransfer=$bool1 isParentRemoved=$bool2 oldParent=$str2 newParent=$str3" }) } 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 dbd22db333ff..1f7d93012e39 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 @@ -49,6 +49,7 @@ import android.os.Build; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.Trace; import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.ArraySet; @@ -1246,6 +1247,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } private void reInflateViews() { + Trace.beginSection("ExpandableNotificationRow#reInflateViews"); // Let's update our childrencontainer. This is intentionally not guarded with // mIsSummaryWithChildren since we might have had children but not anymore. if (mChildrenContainer != null) { @@ -1277,6 +1279,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry); params.setNeedsReinflation(true); mRowContentBindStage.requestRebind(mEntry, null /* callback */); + Trace.endSection(); } @Override @@ -1737,6 +1740,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + Trace.beginSection(appendTraceStyleTag("ExpNotRow#onMeasure")); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + Trace.endSection(); + } + + /** Generates and appends "(MessagingStyle)" type tag to passed string for tracing. */ + @NonNull + private String appendTraceStyleTag(@NonNull String traceTag) { + if (!Trace.isEnabled()) { + return traceTag; + } + + Class<? extends Notification.Style> style = + getEntry().getSbn().getNotification().getNotificationStyle(); + if (style == null) { + return traceTag + "(nostyle)"; + } else { + return traceTag + "(" + style.getSimpleName() + ")"; + } + } + + @Override protected void onFinishInflate() { super.onFinishInflate(); mPublicLayout = findViewById(R.id.expandedPublic); @@ -2542,6 +2568,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + Trace.beginSection(appendTraceStyleTag("ExpNotRow#onLayout")); int intrinsicBefore = getIntrinsicHeight(); super.onLayout(changed, left, top, right, bottom); if (intrinsicBefore != getIntrinsicHeight() @@ -2555,6 +2582,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (mLayoutListener != null) { mLayoutListener.onLayout(); } + Trace.endSection(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index 89b5aef00656..7c9df426aba6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -344,9 +344,11 @@ public class StackStateAnimator { for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) { final ExpandableView changingView = (ExpandableView) event.mChangingView; boolean loggable = false; + boolean isHeadsUp = false; String key = null; if (changingView instanceof ExpandableNotificationRow && mLogger != null) { loggable = true; + isHeadsUp = ((ExpandableNotificationRow) changingView).isHeadsUp(); key = ((ExpandableNotificationRow) changingView).getEntry().getKey(); } if (event.animationType == @@ -358,6 +360,9 @@ public class StackStateAnimator { // The position for this child was never generated, let's continue. continue; } + if (loggable && isHeadsUp) { + mLogger.logHUNViewAppearingWithAddEvent(key); + } viewState.applyToView(changingView); mNewAddChildren.add(changingView); @@ -399,9 +404,18 @@ public class StackStateAnimator { translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f); } + Runnable postAnimation = changingView::removeFromTransientContainer; + if (loggable && isHeadsUp) { + mLogger.logHUNViewDisappearingWithRemoveEvent(key); + String finalKey = key; + postAnimation = () -> { + mLogger.disappearAnimationEnded(finalKey); + changingView.removeFromTransientContainer(); + }; + } changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR, 0 /* delay */, translationDirection, false /* isHeadsUpAppear */, - 0, changingView::removeFromTransientContainer, null); + 0, postAnimation, null); } else if (event.animationType == NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) { if (mHostLayout.isFullySwipedOut(changingView)) { @@ -431,8 +445,7 @@ public class StackStateAnimator { // this only captures HEADS_UP_APPEAR animations, but HUNs can appear with normal // ADD animations, which would not be logged here. if (loggable) { - mLogger.logHUNViewAppearing( - ((ExpandableNotificationRow) changingView).getEntry().getKey()); + mLogger.logHUNViewAppearing(key); } mTmpState.applyToView(changingView); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt index 4315265e79cc..77377af9ddfb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt @@ -24,6 +24,22 @@ class StackStateLogger @Inject constructor( }) } + fun logHUNViewDisappearingWithRemoveEvent(key: String) { + buffer.log(TAG, LogLevel.ERROR, { + str1 = key + }, { + "Heads up view disappearing $str1 for ANIMATION_TYPE_REMOVE" + }) + } + + fun logHUNViewAppearingWithAddEvent(key: String) { + buffer.log(TAG, LogLevel.ERROR, { + str1 = key + }, { + "Heads up view disappearing $str1 for ANIMATION_TYPE_ADD" + }) + } + fun disappearAnimationEnded(key: String) { buffer.log(TAG, LogLevel.INFO, { str1 = key diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index febf2b5b5f24..ebfed1a689cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -135,7 +135,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } }.setDuration(CONTENT_FADE_DURATION); - private static final int MAX_ICONS_ON_AOD = 5; + private static final int MAX_ICONS_ON_AOD = 3; public static final int MAX_ICONS_ON_LOCKSCREEN = 3; public static final int MAX_STATIC_ICONS = 4; private static final int MAX_DOTS = 1; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt new file mode 100644 index 000000000000..ff48755f750a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone + +import android.content.Context +import android.view.ViewGroup +import com.android.systemui.R +import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator +import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.LEFT +import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.RIGHT +import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate +import com.android.systemui.unfold.SysUIUnfoldScope +import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider +import javax.inject.Inject + +@SysUIUnfoldScope +class NotificationPanelUnfoldAnimationController +@Inject +constructor(private val context: Context, progressProvider: NaturalRotationUnfoldProgressProvider) { + + private val translateAnimator by lazy { + UnfoldConstantTranslateAnimator( + viewsIdToTranslate = + setOf( + ViewIdToTranslate(R.id.quick_settings_panel, LEFT), + ViewIdToTranslate(R.id.notification_stack_scroller, RIGHT), + ViewIdToTranslate(R.id.rightLayout, RIGHT), + ViewIdToTranslate(R.id.clock, LEFT), + ViewIdToTranslate(R.id.date, LEFT)), + progressProvider = progressProvider) + } + + fun setup(root: ViewGroup) { + val translationMax = + context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings).toFloat() + translateAnimator.init(root, translationMax) + } +} 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 bb0ed95cc6ca..1870588f34ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -669,6 +669,8 @@ public class NotificationPanelViewController extends PanelViewController private boolean mStatusViewCentered = true; private Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition; + private Optional<NotificationPanelUnfoldAnimationController> + mNotificationPanelUnfoldAnimationController; private final ListenerSet<Callbacks> mNotifEventSourceCallbacks = new ListenerSet<>(); @@ -929,6 +931,8 @@ public class NotificationPanelViewController extends PanelViewController mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count); mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition()); + mNotificationPanelUnfoldAnimationController = unfoldComponent.map( + SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); mCommunalSourceMonitorCallback = (source) -> { mUiExecutor.execute(() -> setCommunalSource(source)); @@ -1064,6 +1068,8 @@ public class NotificationPanelViewController extends PanelViewController mTapAgainViewController.init(); mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); + mNotificationPanelUnfoldAnimationController.ifPresent(controller -> + controller.setup(mNotificationContainerParent)); } @Override @@ -1319,6 +1325,7 @@ public class NotificationPanelViewController extends PanelViewController setKeyguardBottomAreaVisibility(mBarState, false); mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); + mNotificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView)); } private void attachSplitShadeMediaPlayerContainer(FrameLayout container) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt index 091831f36022..ea61a8b94e7e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt @@ -113,14 +113,16 @@ class ScreenOffAnimationController @Inject constructor( * the animation ends */ fun shouldDelayKeyguardShow(): Boolean = - animations.any { it.shouldPlayAnimation() } + animations.any { it.shouldDelayKeyguardShow() } /** * Return true while we want to ignore requests to show keyguard, we need to handle pending * keyguard lock requests manually + * + * @see [com.android.systemui.keyguard.KeyguardViewMediator.maybeHandlePendingLock] */ fun isKeyguardShowDelayed(): Boolean = - animations.any { it.isAnimationPlaying() } + animations.any { it.isKeyguardShowDelayed() } /** * Return true to ignore requests to hide keyguard @@ -211,6 +213,8 @@ interface ScreenOffAnimation { fun shouldAnimateInKeyguard(): Boolean = false fun animateInKeyguard(keyguardView: View, after: Runnable) = after.run() + fun shouldDelayKeyguardShow(): Boolean = false + fun isKeyguardShowDelayed(): Boolean = false fun isKeyguardHideDelayed(): Boolean = false fun shouldHideScrimOnWakeUp(): Boolean = false fun overrideNotificationsDozeAmount(): Boolean = false 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 a1445337e7d7..2f3300a53ad6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -2987,6 +2987,7 @@ public class StatusBar extends CoreStartable implements } public void showKeyguardImpl() { + Trace.beginSection("StatusBar#showKeyguard"); mIsKeyguard = true; // In case we're locking while a smartspace transition is in progress, reset it. mKeyguardUnlockAnimationController.resetSmartspaceTransition(); @@ -3001,6 +3002,7 @@ public class StatusBar extends CoreStartable implements mStatusBarStateController.setState(StatusBarState.KEYGUARD); } updatePanelExpansionForKeyguard(); + Trace.endSection(); } private void updatePanelExpansionForKeyguard() { 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 b20349667379..b833c894c43f 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.content.res.ColorStateList; import android.hardware.biometrics.BiometricSourceType; import android.os.Bundle; import android.os.SystemClock; +import android.os.Trace; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -374,6 +375,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb */ @Override public void show(Bundle options) { + Trace.beginSection("StatusBarKeyguardViewManager#show"); mShowing = true; mNotificationShadeWindowController.setKeyguardShowing(true); mKeyguardStateController.notifyKeyguardState(mShowing, @@ -381,6 +383,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb reset(true /* hideBouncerWhenShowing */); SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED, SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN); + Trace.endSection(); } /** @@ -715,6 +718,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void hide(long startTime, long fadeoutDuration) { + Trace.beginSection("StatusBarKeyguardViewManager#hide"); mShowing = false; mKeyguardStateController.notifyKeyguardState(mShowing, mKeyguardStateController.isOccluded()); @@ -814,6 +818,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED, SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__HIDDEN); + Trace.endSection(); } private boolean needsBypassFading() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt index cc65ca025139..0abe8e489638 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt @@ -136,6 +136,12 @@ class UnlockedScreenOffAnimationController @Inject constructor( globalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f) } + override fun shouldDelayKeyguardShow(): Boolean = + shouldPlayAnimation() + + override fun isKeyguardShowDelayed(): Boolean = + isAnimationPlaying() + /** * Animates in the provided keyguard view, ending in the same position that it will be in on * AOD. 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 48949f92413d..3205e097c03b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -118,6 +118,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private boolean mColorized; private int mTint; private boolean mResetting; + private boolean mWasSpinning; // TODO(b/193539698): move these to a Controller private RemoteInputController mController; @@ -439,6 +440,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEditText.requestFocus(); } } + if (mWasSpinning) { + mController.addSpinning(mEntry.getKey(), mToken); + mWasSpinning = false; + } } @Override @@ -447,6 +452,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEditText.removeTextChangedListener(mTextWatcher); mEditText.setOnEditorActionListener(null); mEditText.mRemoteInputView = null; + mWasSpinning = mController.isSpinning(mEntry.getKey(), mToken); if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) { return; } @@ -533,6 +539,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene if (isActive() && mWrapper != null) { mWrapper.setRemoteInputVisible(true); } + + mWasSpinning = false; } private void reset() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 9f20bc55ebc9..49e712d386e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -455,6 +455,13 @@ public class UserSwitcherController implements Dumpable { } } + /** + * Returns whether the current user is a system user. + */ + public boolean isSystemUser() { + return mUserTracker.getUserId() == UserHandle.USER_SYSTEM; + } + public void removeUserId(int userId) { if (userId == UserHandle.USER_SYSTEM) { Log.w(TAG, "User " + userId + " could not removed."); diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt index c481fc94c526..2e627a872c24 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt @@ -20,7 +20,6 @@ import android.os.Handler import android.os.PowerManager import android.provider.Settings import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.phone.ScreenOffAnimation @@ -28,7 +27,6 @@ import com.android.systemui.statusbar.phone.StatusBar import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus import com.android.systemui.util.settings.GlobalSettings -import dagger.Lazy import javax.inject.Inject /** @@ -40,7 +38,6 @@ class FoldAodAnimationController @Inject constructor( @Main private val handler: Handler, - private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>, private val wakefulnessLifecycle: WakefulnessLifecycle, private val globalSettings: GlobalSettings ) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer { @@ -57,7 +54,6 @@ constructor( statusBar.notificationPanelViewController.startFoldToAodAnimation { // End action isAnimationPlaying = false - keyguardViewMediatorLazy.get().maybeHandlePendingLock() } } diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt index 07f9c5487c41..7350b37e4b66 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt @@ -18,6 +18,7 @@ package com.android.systemui.unfold import com.android.keyguard.KeyguardUnfoldTransition import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.phone.NotificationPanelUnfoldAnimationController import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider @@ -85,6 +86,8 @@ interface SysUIUnfoldComponent { fun getStatusBarMoveFromCenterAnimationController(): StatusBarMoveFromCenterAnimationController + fun getNotificationPanelUnfoldAnimationController(): NotificationPanelUnfoldAnimationController + fun getFoldAodAnimationController(): FoldAodAnimationController fun getUnfoldTransitionWallpaperController(): UnfoldTransitionWallpaperController diff --git a/packages/SystemUI/src/com/android/systemui/util/service/Observer.java b/packages/SystemUI/src/com/android/systemui/util/service/Observer.java new file mode 100644 index 000000000000..768743217cc7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/service/Observer.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.service; + +/** + * The {@link Observer} interface specifies an entity which listeners + * can be informed of changes to the source, which will require updating. Note that this deals + * with changes to the source itself, not content which will be updated through the interface. + */ +public interface Observer { + /** + * Callback for receiving updates from the {@link Observer}. + */ + interface Callback { + /** + * Invoked when the source has changed. + */ + void onSourceChanged(); + } + + /** + * Adds a callback to receive future updates from the {@link Observer}. + */ + void addCallback(Callback callback); + + /** + * Removes a callback from receiving further updates. + * @param callback + */ + void removeCallback(Callback callback); +} diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java b/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java new file mode 100644 index 000000000000..2ee7b20c1f93 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/service/PackageObserver.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.service; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.PatternMatcher; +import android.util.Log; + +import com.android.systemui.communal.CommunalSource; + +import com.google.android.collect.Lists; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Iterator; + +import javax.inject.Inject; + +/** + * {@link PackageObserver} allows for monitoring the system for changes relating to a particular + * package. This can be used by {@link CommunalSource} clients to detect when a related package + * has changed and reloading is necessary. + */ +public class PackageObserver implements Observer { + private static final String TAG = "PackageObserver"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList(); + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) { + Log.d(TAG, "package added receiver - onReceive"); + } + + final Iterator<WeakReference<Callback>> iter = mCallbacks.iterator(); + while (iter.hasNext()) { + final Callback callback = iter.next().get(); + if (callback != null) { + callback.onSourceChanged(); + } else { + iter.remove(); + } + } + } + }; + + private final String mPackageName; + private final Context mContext; + + @Inject + public PackageObserver(Context context, ComponentName component) { + mContext = context; + mPackageName = component.getPackageName(); + } + + @Override + public void addCallback(Callback callback) { + if (DEBUG) { + Log.d(TAG, "addCallback:" + callback); + } + mCallbacks.add(new WeakReference<>(callback)); + + // Only register for listening to package additions on first callback. + if (mCallbacks.size() > 1) { + return; + } + + final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + filter.addDataScheme("package"); + filter.addDataSchemeSpecificPart(mPackageName, PatternMatcher.PATTERN_LITERAL); + // Note that we directly register the receiver here as data schemes are not supported by + // BroadcastDispatcher. + mContext.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED); + } + + @Override + public void removeCallback(Callback callback) { + if (DEBUG) { + Log.d(TAG, "removeCallback:" + callback); + } + final boolean removed = mCallbacks.removeIf(el -> el.get() == callback); + + if (removed && mCallbacks.isEmpty()) { + mContext.unregisterReceiver(mReceiver); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java new file mode 100644 index 000000000000..292c062369c1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.service; + +import static com.android.systemui.util.service.dagger.ObservableServiceModule.BASE_RECONNECT_DELAY_MS; +import static com.android.systemui.util.service.dagger.ObservableServiceModule.MAX_RECONNECT_ATTEMPTS; +import static com.android.systemui.util.service.dagger.ObservableServiceModule.MIN_CONNECTION_DURATION_MS; +import static com.android.systemui.util.service.dagger.ObservableServiceModule.OBSERVER; +import static com.android.systemui.util.service.dagger.ObservableServiceModule.SERVICE_CONNECTION; + +import android.util.Log; + +import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.time.SystemClock; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * The {@link PersistentConnectionManager} is responsible for maintaining a connection to a + * {@link ObservableServiceConnection}. + * @param <T> The transformed connection type handled by the service. + */ +public class PersistentConnectionManager<T> { + private static final String TAG = "PersistentConnManager"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private final SystemClock mSystemClock; + private final DelayableExecutor mMainExecutor; + private final int mBaseReconnectDelayMs; + private final int mMaxReconnectAttempts; + private final int mMinConnectionDuration; + private final Observer mObserver; + + private int mReconnectAttempts = 0; + private Runnable mCurrentReconnectCancelable; + + private final ObservableServiceConnection<T> mConnection; + + private final Runnable mConnectRunnable = new Runnable() { + @Override + public void run() { + mCurrentReconnectCancelable = null; + mConnection.bind(); + } + }; + + private final Observer.Callback mObserverCallback = () -> initiateConnectionAttempt(); + + private final ObservableServiceConnection.Callback mConnectionCallback = + new ObservableServiceConnection.Callback() { + private long mStartTime; + + @Override + public void onConnected(ObservableServiceConnection connection, Object proxy) { + mStartTime = mSystemClock.currentTimeMillis(); + } + + @Override + public void onDisconnected(ObservableServiceConnection connection, int reason) { + if (mSystemClock.currentTimeMillis() - mStartTime > mMinConnectionDuration) { + initiateConnectionAttempt(); + } else { + scheduleConnectionAttempt(); + } + } + }; + + @Inject + public PersistentConnectionManager( + SystemClock clock, + DelayableExecutor mainExecutor, + @Named(SERVICE_CONNECTION) ObservableServiceConnection<T> serviceConnection, + @Named(MAX_RECONNECT_ATTEMPTS) int maxReconnectAttempts, + @Named(BASE_RECONNECT_DELAY_MS) int baseReconnectDelayMs, + @Named(MIN_CONNECTION_DURATION_MS) int minConnectionDurationMs, + @Named(OBSERVER) Observer observer) { + mSystemClock = clock; + mMainExecutor = mainExecutor; + mConnection = serviceConnection; + mObserver = observer; + + mMaxReconnectAttempts = maxReconnectAttempts; + mBaseReconnectDelayMs = baseReconnectDelayMs; + mMinConnectionDuration = minConnectionDurationMs; + } + + /** + * Begins the {@link PersistentConnectionManager} by connecting to the associated service. + */ + public void start() { + mConnection.addCallback(mConnectionCallback); + mObserver.addCallback(mObserverCallback); + initiateConnectionAttempt(); + } + + /** + * Brings down the {@link PersistentConnectionManager}, disconnecting from the service. + */ + public void stop() { + mConnection.removeCallback(mConnectionCallback); + mObserver.removeCallback(mObserverCallback); + mConnection.unbind(); + } + + private void initiateConnectionAttempt() { + // Reset attempts + mReconnectAttempts = 0; + + // The first attempt is always a direct invocation rather than delayed. + mConnection.bind(); + } + + private void scheduleConnectionAttempt() { + // always clear cancelable if present. + if (mCurrentReconnectCancelable != null) { + mCurrentReconnectCancelable.run(); + mCurrentReconnectCancelable = null; + } + + if (mReconnectAttempts >= mMaxReconnectAttempts) { + if (DEBUG) { + Log.d(TAG, "exceeded max connection attempts."); + } + return; + } + + final long reconnectDelayMs = + (long) Math.scalb(mBaseReconnectDelayMs, mReconnectAttempts); + + if (DEBUG) { + Log.d(TAG, + "scheduling connection attempt in " + reconnectDelayMs + "milliseconds"); + } + + mCurrentReconnectCancelable = mMainExecutor.executeDelayed(mConnectRunnable, + reconnectDelayMs); + + mReconnectAttempts++; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java b/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java new file mode 100644 index 000000000000..c62c95755ce8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/service/dagger/ObservableServiceModule.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.systemui.util.service.dagger; + +import android.content.res.Resources; + +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; + +import javax.inject.Named; + +import dagger.Module; +import dagger.Provides; + +/** + * Module containing components and parameters for + * {@link com.android.systemui.util.service.ObservableServiceConnection} + * and {@link com.android.systemui.util.service.PersistentConnectionManager}. + */ +@Module(subcomponents = { + PackageObserverComponent.class, +}) +public class ObservableServiceModule { + public static final String MAX_RECONNECT_ATTEMPTS = "max_reconnect_attempts"; + public static final String BASE_RECONNECT_DELAY_MS = "base_reconnect_attempts"; + public static final String MIN_CONNECTION_DURATION_MS = "min_connection_duration_ms"; + public static final String SERVICE_CONNECTION = "service_connection"; + public static final String OBSERVER = "observer"; + + @Provides + @Named(MAX_RECONNECT_ATTEMPTS) + static int providesMaxReconnectAttempts(@Main Resources resources) { + return resources.getInteger( + R.integer.config_communalSourceMaxReconnectAttempts); + } + + @Provides + @Named(BASE_RECONNECT_DELAY_MS) + static int provideBaseReconnectDelayMs(@Main Resources resources) { + return resources.getInteger( + R.integer.config_communalSourceReconnectBaseDelay); + } + + @Provides + @Named(MIN_CONNECTION_DURATION_MS) + static int providesMinConnectionDuration(@Main Resources resources) { + return resources.getInteger( + R.integer.config_connectionMinDuration); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/service/dagger/PackageObserverComponent.java b/packages/SystemUI/src/com/android/systemui/util/service/dagger/PackageObserverComponent.java new file mode 100644 index 000000000000..8ee39b34cdf5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/service/dagger/PackageObserverComponent.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.util.service.dagger; + +import android.content.ComponentName; + +import com.android.systemui.util.service.PackageObserver; + +import dagger.BindsInstance; +import dagger.Subcomponent; + +/** + * Generates a scoped {@link PackageObserver}. + */ +@Subcomponent +public interface PackageObserverComponent { + /** + * Generates a {@link PackageObserverComponent} instance. + */ + @Subcomponent.Factory + interface Factory { + PackageObserverComponent create(@BindsInstance ComponentName component); + } + + /** + * Creates a {@link PackageObserver}. + */ + PackageObserver getPackageObserver(); +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt index 164f83dda9b7..6c1f008e9337 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt @@ -20,23 +20,21 @@ import android.testing.AndroidTestingRunner import android.view.View import android.view.ViewGroup import androidx.test.filters.SmallTest -import com.android.keyguard.KeyguardUnfoldTransition.Companion.LEFT -import com.android.keyguard.KeyguardUnfoldTransition.Companion.RIGHT import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider import com.android.systemui.util.mockito.capture -import org.junit.Assert.assertEquals +import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock -import org.mockito.MockitoAnnotations -import org.mockito.Mockito.`when` import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations /** * Translates items away/towards the hinge when the device is opened/closed. This is controlled by @@ -46,14 +44,11 @@ import org.mockito.Mockito.verify @RunWith(AndroidTestingRunner::class) class KeyguardUnfoldTransitionTest : SysuiTestCase() { - @Mock - private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider + @Mock private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider - @Captor - private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener> + @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener> - @Mock - private lateinit var parent: ViewGroup + @Mock private lateinit var parent: ViewGroup private lateinit var keyguardUnfoldTransition: KeyguardUnfoldTransition private lateinit var progressListener: TransitionProgressListener @@ -63,87 +58,35 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { fun setup() { MockitoAnnotations.initMocks(this) - xTranslationMax = context.resources.getDimensionPixelSize( - R.dimen.keyguard_unfold_translation_x).toFloat() + xTranslationMax = + context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat() - keyguardUnfoldTransition = KeyguardUnfoldTransition( - getContext(), - progressProvider - ) - - verify(progressProvider).addCallback(capture(progressListenerCaptor)) - progressListener = progressListenerCaptor.value + keyguardUnfoldTransition = KeyguardUnfoldTransition(context, progressProvider) keyguardUnfoldTransition.setup(parent) keyguardUnfoldTransition.statusViewCentered = false - } - - @Test - fun onTransition_noMatchingIds() { - // GIVEN no views matching any ids - // WHEN the transition starts - progressListener.onTransitionStarted() - progressListener.onTransitionProgress(.1f) - - // THEN nothing... no exceptions - } - @Test - fun onTransition_oneMovesLeft() { - // GIVEN one view with a matching id - val view = View(getContext()) - `when`(parent.findViewById<View>(R.id.keyguard_status_area)).thenReturn(view) - - moveAndValidate(listOf(view to LEFT)) - } - - @Test - fun onTransition_oneMovesLeftAndOneMovesRightMultipleTimes() { - // GIVEN two views with a matching id - val leftView = View(getContext()) - val rightView = View(getContext()) - `when`(parent.findViewById<View>(R.id.keyguard_status_area)).thenReturn(leftView) - `when`(parent.findViewById<View>(R.id.notification_stack_scroller)).thenReturn(rightView) - - moveAndValidate(listOf(leftView to LEFT, rightView to RIGHT)) - moveAndValidate(listOf(leftView to LEFT, rightView to RIGHT)) + verify(progressProvider).addCallback(capture(progressListenerCaptor)) + progressListener = progressListenerCaptor.value } @Test fun onTransition_centeredViewDoesNotMove() { keyguardUnfoldTransition.statusViewCentered = true - val view = View(getContext()) + val view = View(context) `when`(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view) - moveAndValidate(listOf(view to 0)) - } - - private fun moveAndValidate(list: List<Pair<View, Int>>) { - // Compare values as ints because -0f != 0f - - // WHEN the transition starts progressListener.onTransitionStarted() + assertThat(view.translationX).isZero() + progressListener.onTransitionProgress(0f) + assertThat(view.translationX).isZero() + + progressListener.onTransitionProgress(0.5f) + assertThat(view.translationX).isZero() - list.forEach { (view, direction) -> - assertEquals((-xTranslationMax * direction).toInt(), view.getTranslationX().toInt()) - } - - // WHEN the transition progresses, translation is updated - progressListener.onTransitionProgress(.5f) - list.forEach { (view, direction) -> - assertEquals( - (-xTranslationMax / 2f * direction).toInt(), - view.getTranslationX().toInt() - ) - } - - // WHEN the transition ends, translation is completed - progressListener.onTransitionProgress(1f) progressListener.onTransitionFinished() - list.forEach { (view, _) -> - assertEquals(0, view.getTranslationX().toInt()) - } + assertThat(view.translationX).isZero() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt new file mode 100644 index 000000000000..32314159f865 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt @@ -0,0 +1,117 @@ +package com.android.systemui.shared.animation + +import android.testing.AndroidTestingRunner +import android.view.View +import android.view.ViewGroup +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction +import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate +import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { + + @Mock private lateinit var progressProvider: UnfoldTransitionProgressProvider + + @Mock private lateinit var parent: ViewGroup + + @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener> + + private lateinit var animator: UnfoldConstantTranslateAnimator + private lateinit var progressListener: TransitionProgressListener + + private val viewsIdToRegister = + setOf( + ViewIdToTranslate(LEFT_VIEW_ID, Direction.LEFT), + ViewIdToTranslate(RIGHT_VIEW_ID, Direction.RIGHT)) + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + animator = + UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider) + + animator.init(parent, MAX_TRANSLATION) + + verify(progressProvider).addCallback(progressListenerCaptor.capture()) + progressListener = progressListenerCaptor.value + } + + @Test + fun onTransition_noMatchingIds() { + // GIVEN no views matching any ids + // WHEN the transition starts + progressListener.onTransitionStarted() + progressListener.onTransitionProgress(.1f) + + // THEN nothing... no exceptions + } + + @Test + fun onTransition_oneMovesLeft() { + // GIVEN one view with a matching id + val view = View(context) + whenever(parent.findViewById<View>(LEFT_VIEW_ID)).thenReturn(view) + + moveAndValidate(listOf(view to LEFT)) + } + + @Test + fun onTransition_oneMovesLeftAndOneMovesRightMultipleTimes() { + // GIVEN two views with a matching id + val leftView = View(context) + val rightView = View(context) + whenever(parent.findViewById<View>(LEFT_VIEW_ID)).thenReturn(leftView) + whenever(parent.findViewById<View>(RIGHT_VIEW_ID)).thenReturn(rightView) + + moveAndValidate(listOf(leftView to LEFT, rightView to RIGHT)) + moveAndValidate(listOf(leftView to LEFT, rightView to RIGHT)) + } + + private fun moveAndValidate(list: List<Pair<View, Int>>) { + // Compare values as ints because -0f != 0f + + // WHEN the transition starts + progressListener.onTransitionStarted() + progressListener.onTransitionProgress(0f) + + list.forEach { (view, direction) -> + assertEquals((-MAX_TRANSLATION * direction).toInt(), view.translationX.toInt()) + } + + // WHEN the transition progresses, translation is updated + progressListener.onTransitionProgress(.5f) + list.forEach { (view, direction) -> + assertEquals((-MAX_TRANSLATION / 2f * direction).toInt(), view.translationX.toInt()) + } + + // WHEN the transition ends, translation is completed + progressListener.onTransitionProgress(1f) + progressListener.onTransitionFinished() + list.forEach { (view, _) -> assertEquals(0, view.translationX.toInt()) } + } + + companion object { + private val LEFT = Direction.LEFT.multiplier.toInt() + private val RIGHT = Direction.RIGHT.multiplier.toInt() + + private const val MAX_TRANSLATION = 42f + + private const val LEFT_VIEW_ID = 1 + private const val RIGHT_VIEW_ID = 2 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index 83f1d87133c6..7fafb24317b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -35,6 +35,7 @@ import android.widget.LinearLayout; import androidx.test.filters.SmallTest; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; @@ -56,6 +57,7 @@ import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.wm.shell.bubbles.Bubbles; import com.google.android.collect.Lists; @@ -123,7 +125,9 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { mock(DynamicChildBindController.class), mock(LowPriorityInflationHelper.class), mock(AssistantFeedbackController.class), - mNotifPipelineFlags); + mNotifPipelineFlags, + mock(KeyguardUpdateMonitor.class), + mock(KeyguardStateController.class)); mViewHierarchyManager.setUpWithPresenter(mPresenter, mStackController, mListContainer); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt index 5fd4174af164..3f84c161db20 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt @@ -19,8 +19,11 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.os.UserHandle import android.service.notification.StatusBarNotification import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.notification.DynamicPrivacyController import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline @@ -28,9 +31,12 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable +import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.withArgCaptor +import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import org.junit.Test +import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever @@ -40,9 +46,13 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() { val dynamicPrivacyController: DynamicPrivacyController = mock() val lockscreenUserManager: NotificationLockscreenUserManager = mock() val pipeline: NotifPipeline = mock() + val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock() + val statusBarStateController: StatusBarStateController = mock() + val keyguardStateController: KeyguardStateController = mock() val coordinator: SensitiveContentCoordinator = SensitiveContentCoordinatorModule - .provideCoordinator(dynamicPrivacyController, lockscreenUserManager) + .provideCoordinator(dynamicPrivacyController, lockscreenUserManager, + keyguardUpdateMonitor, statusBarStateController, keyguardStateController) @Test fun onDynamicPrivacyChanged_invokeInvalidationListener() { @@ -190,6 +200,28 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() { verify(entry.representativeEntry!!).setSensitive(true, true) } + @Test + fun onBeforeRenderList_deviceDynamicallyUnlocked_deviceBiometricBypassingLockScreen() { + coordinator.attach(pipeline) + val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) + whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true) + whenever(statusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD) + whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(any())) + .thenReturn(true) + + val entry = fakeNotification(2, true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!, never()).setSensitive(any(), any()) + } + private fun fakeNotification(notifUserId: Int, needsRedaction: Boolean): ListEntry { val mockUserHandle = mock<UserHandle>().apply { whenever(identifier).thenReturn(notifUserId) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java index 15ff5551703b..ab712649a90f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection.render; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import android.content.Context; import android.testing.AndroidTestingRunner; @@ -138,7 +139,7 @@ public class ShadeViewDifferTest extends SysuiTestCase { } @Test - public void testRemovedGroupsAreKeptTogether() { + public void testRemovedGroupsAreBrokenApart() { // GIVEN a preexisting tree with a group applySpecAndCheck( node(mController1), @@ -154,10 +155,10 @@ public class ShadeViewDifferTest extends SysuiTestCase { node(mController1) ); - // THEN the group children are still attached to their parent - assertEquals(mController2.getView(), mController3.getView().getParent()); - assertEquals(mController2.getView(), mController4.getView().getParent()); - assertEquals(mController2.getView(), mController5.getView().getParent()); + // THEN the group children are no longer attached to their parent + assertNull(mController3.getView().getParent()); + assertNull(mController4.getView().getParent()); + assertNull(mController5.getView().getParent()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java index 5d7b15424fec..d002cebe5cb1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java @@ -250,15 +250,17 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase { @Notification.GroupAlertBehavior int priorityGroupAlert, @Notification.GroupAlertBehavior int siblingGroupAlert, boolean expectAlertOverride) { + long when = 10000; // Create entries in an order so that the priority entry can be deemed the newest child. NotificationEntry[] siblings = new NotificationEntry[numSiblings]; for (int i = 0; i < numSiblings; i++) { - siblings[i] = mGroupTestHelper.createChildNotification(siblingGroupAlert, i, "sibling"); + siblings[i] = mGroupTestHelper + .createChildNotification(siblingGroupAlert, i, "sibling", ++when); } NotificationEntry priorityEntry = - mGroupTestHelper.createChildNotification(priorityGroupAlert, 0, "priority"); + mGroupTestHelper.createChildNotification(priorityGroupAlert, 0, "priority", ++when); NotificationEntry summaryEntry = - mGroupTestHelper.createSummaryNotification(summaryGroupAlert, 0, "summary"); + mGroupTestHelper.createSummaryNotification(summaryGroupAlert, 0, "summary", ++when); // The priority entry is an important conversation. when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry))) @@ -322,17 +324,19 @@ public class NotificationGroupManagerLegacyTest extends SysuiTestCase { @Test public void testAlertOverrideWhenUpdatingSummaryAtEnd() { + long when = 10000; int numSiblings = 2; int groupAlert = Notification.GROUP_ALERT_SUMMARY; // Create entries in an order so that the priority entry can be deemed the newest child. NotificationEntry[] siblings = new NotificationEntry[numSiblings]; for (int i = 0; i < numSiblings; i++) { - siblings[i] = mGroupTestHelper.createChildNotification(groupAlert, i, "sibling"); + siblings[i] = + mGroupTestHelper.createChildNotification(groupAlert, i, "sibling", ++when); } NotificationEntry priorityEntry = - mGroupTestHelper.createChildNotification(groupAlert, 0, "priority"); + mGroupTestHelper.createChildNotification(groupAlert, 0, "priority", ++when); NotificationEntry summaryEntry = - mGroupTestHelper.createSummaryNotification(groupAlert, 0, "summary"); + mGroupTestHelper.createSummaryNotification(groupAlert, 0, "summary", ++when); // The priority entry is an important conversation. when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry))) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt index fa2a9066d99b..9a7e702152b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt @@ -400,4 +400,16 @@ class UserSwitcherControllerTest : SysuiTestCase() { assertEquals(fgUserName, userSwitcherController.currentUserName) } + + @Test + fun isSystemUser_currentUserIsSystemUser_shouldReturnTrue() { + `when`(userTracker.userId).thenReturn(UserHandle.USER_SYSTEM) + assertEquals(true, userSwitcherController.isSystemUser) + } + + @Test + fun isSystemUser_currentUserIsNotSystemUser_shouldReturnFalse() { + `when`(userTracker.userId).thenReturn(1) + assertEquals(false, userSwitcherController.isSystemUser) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java new file mode 100644 index 000000000000..a2fd288ef33e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.service; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.verify; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +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; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class PackageObserverTest extends SysuiTestCase { + @Mock + Context mContext; + + @Mock + Observer.Callback mCallback; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testChange() { + final PackageObserver observer = new PackageObserver(mContext, + ComponentName.unflattenFromString("com.foo.bar/baz")); + final ArgumentCaptor<BroadcastReceiver> receiverCapture = + ArgumentCaptor.forClass(BroadcastReceiver.class); + + observer.addCallback(mCallback); + + // Verify broadcast receiver registered. + verify(mContext).registerReceiver(receiverCapture.capture(), any(), anyInt()); + + // Simulate package change. + receiverCapture.getValue().onReceive(mContext, new Intent()); + + // Check that callback was informed. + verify(mCallback).onSourceChanged(); + + observer.removeCallback(mCallback); + + // Make sure receiver is unregistered on last callback removal + verify(mContext).unregisterReceiver(receiverCapture.getValue()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java new file mode 100644 index 000000000000..53d4a96b0640 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.service; + +import static org.mockito.Mockito.verify; + +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class PersistentConnectionManagerTest extends SysuiTestCase { + private static final int MAX_RETRIES = 5; + private static final int RETRY_DELAY_MS = 1000; + private static final int CONNECTION_MIN_DURATION_MS = 5000; + + private FakeSystemClock mFakeClock = new FakeSystemClock(); + private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeClock); + + @Mock + private ObservableServiceConnection<Proxy> mConnection; + + @Mock + private Observer mObserver; + + private static class Proxy { + } + + private PersistentConnectionManager<Proxy> mConnectionManager; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + mConnectionManager = new PersistentConnectionManager<>( + mFakeClock, + mFakeExecutor, + mConnection, + MAX_RETRIES, + RETRY_DELAY_MS, + CONNECTION_MIN_DURATION_MS, + mObserver); + } + + private ObservableServiceConnection.Callback<Proxy> captureCallbackAndSend( + ObservableServiceConnection<Proxy> mConnection, Proxy proxy) { + ArgumentCaptor<ObservableServiceConnection.Callback<Proxy>> connectionCallbackCaptor = + ArgumentCaptor.forClass(ObservableServiceConnection.Callback.class); + + verify(mConnection).addCallback(connectionCallbackCaptor.capture()); + verify(mConnection).bind(); + Mockito.clearInvocations(mConnection); + + final ObservableServiceConnection.Callback callback = connectionCallbackCaptor.getValue(); + if (proxy != null) { + callback.onConnected(mConnection, proxy); + } else { + callback.onDisconnected(mConnection, 0); + } + + return callback; + } + + /** + * Validates initial connection. + */ + @Test + public void testConnect() { + mConnectionManager.start(); + captureCallbackAndSend(mConnection, Mockito.mock(Proxy.class)); + } + + /** + * Ensures reconnection on disconnect. + */ + @Test + public void testRetryOnBindFailure() { + mConnectionManager.start(); + ArgumentCaptor<ObservableServiceConnection.Callback<Proxy>> connectionCallbackCaptor = + ArgumentCaptor.forClass(ObservableServiceConnection.Callback.class); + + verify(mConnection).addCallback(connectionCallbackCaptor.capture()); + + // Verify attempts happen. Note that we account for the retries plus initial attempt, which + // is not scheduled. + for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) { + verify(mConnection).bind(); + Mockito.clearInvocations(mConnection); + connectionCallbackCaptor.getValue().onDisconnected(mConnection, 0); + mFakeExecutor.advanceClockToNext(); + mFakeExecutor.runAllReady(); + } + } + + /** + * Ensures rebind on package change. + */ + @Test + public void testAttemptOnPackageChange() { + mConnectionManager.start(); + verify(mConnection).bind(); + ArgumentCaptor<Observer.Callback> callbackCaptor = + ArgumentCaptor.forClass(Observer.Callback.class); + captureCallbackAndSend(mConnection, Mockito.mock(Proxy.class)); + + verify(mObserver).addCallback(callbackCaptor.capture()); + + callbackCaptor.getValue().onSourceChanged(); + verify(mConnection).bind(); + } +} diff --git a/services/api/current.txt b/services/api/current.txt index 50f00524676c..dcf7e64479da 100644 --- a/services/api/current.txt +++ b/services/api/current.txt @@ -39,6 +39,7 @@ package com.android.server.am { public interface ActivityManagerLocal { method public boolean canStartForegroundService(int, int, @NonNull String); + method public boolean startAndBindSupplementalProcessService(@NonNull android.content.Intent, @NonNull android.content.ServiceConnection, int) throws android.os.TransactionTooLargeException; } } diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java index 0eb6b8d24768..627b0bebc905 100644 --- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java +++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java @@ -21,8 +21,6 @@ import static android.bluetooth.BluetoothAdapter.ACTION_BLE_STATE_CHANGED; import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED; import static android.bluetooth.BluetoothAdapter.EXTRA_PREVIOUS_STATE; import static android.bluetooth.BluetoothAdapter.EXTRA_STATE; -import static android.bluetooth.BluetoothAdapter.STATE_BLE_ON; -import static android.bluetooth.BluetoothAdapter.STATE_ON; import static android.bluetooth.BluetoothAdapter.nameForState; import static android.bluetooth.le.ScanCallback.SCAN_FAILED_ALREADY_STARTED; import static android.bluetooth.le.ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED; @@ -156,7 +154,7 @@ class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener { private void checkBleState() { enforceInitialized(); - final boolean bleAvailable = isBleAvailable(); + final boolean bleAvailable = mBtAdapter.isLeEnabled(); if (DEBUG) { Log.i(TAG, "checkBleState() bleAvailable=" + bleAvailable); } @@ -183,16 +181,6 @@ class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener { } } - /** - * A duplicate of {@code BluetoothAdapter.getLeAccess()} method which has the package-private - * access level, so it's not accessible from here. - */ - private boolean isBleAvailable() { - final int state = mBtAdapter.getLeState(); - if (DEBUG) Log.d(TAG, "getLeAccess() state=" + nameForBtState(state)); - return state == STATE_ON || state == STATE_BLE_ON; - } - @MainThread private void startScan() { enforceInitialized(); diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java index e5a7b4e4ee23..8a6b54fd9769 100644 --- a/services/core/java/com/android/server/DockObserver.java +++ b/services/core/java/com/android/server/DockObserver.java @@ -19,7 +19,6 @@ package com.android.server; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.media.AudioManager; import android.media.Ringtone; import android.media.RingtoneManager; @@ -32,15 +31,21 @@ import android.os.SystemClock; import android.os.UEventObserver; import android.os.UserHandle; import android.provider.Settings; -import android.util.Log; +import android.util.Pair; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; +import com.android.server.ExtconUEventObserver.ExtconInfo; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * DockObserver monitors for a docking station. @@ -48,9 +53,6 @@ import java.io.PrintWriter; final class DockObserver extends SystemService { private static final String TAG = "DockObserver"; - private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock"; - private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state"; - private static final int MSG_DOCK_STATE_CHANGED = 0; private final PowerManager mPowerManager; @@ -69,6 +71,92 @@ final class DockObserver extends SystemService { private final boolean mAllowTheaterModeWakeFromDock; + private final List<ExtconStateConfig> mExtconStateConfigs; + + static final class ExtconStateProvider { + private final Map<String, String> mState; + + ExtconStateProvider(Map<String, String> state) { + mState = state; + } + + String getValue(String key) { + return mState.get(key); + } + + + static ExtconStateProvider fromString(String stateString) { + Map<String, String> states = new HashMap<>(); + String[] lines = stateString.split("\n"); + for (String line : lines) { + String[] fields = line.split("="); + if (fields.length == 2) { + states.put(fields[0], fields[1]); + } else { + Slog.e(TAG, "Invalid line: " + line); + } + } + return new ExtconStateProvider(states); + } + + static ExtconStateProvider fromFile(String stateFilePath) { + char[] buffer = new char[1024]; + try (FileReader file = new FileReader(stateFilePath)) { + int len = file.read(buffer, 0, 1024); + String stateString = (new String(buffer, 0, len)).trim(); + return ExtconStateProvider.fromString(stateString); + } catch (FileNotFoundException e) { + Slog.w(TAG, "No state file found at: " + stateFilePath); + return new ExtconStateProvider(new HashMap<>()); + } catch (Exception e) { + Slog.e(TAG, "" , e); + return new ExtconStateProvider(new HashMap<>()); + } + } + } + + /** + * Represents a mapping from extcon state to EXTRA_DOCK_STATE value. Each + * instance corresponds to an entry in config_dockExtconStateMapping. + */ + private static final class ExtconStateConfig { + + // The EXTRA_DOCK_STATE that will be used if the extcon key-value pairs match + public final int extraStateValue; + + // A list of key-value pairs that must be present in the extcon state for a match + // to be considered. An empty list is considered a matching wildcard. + public final List<Pair<String, String>> keyValuePairs = new ArrayList<>(); + + ExtconStateConfig(int extraStateValue) { + this.extraStateValue = extraStateValue; + } + } + + private static List<ExtconStateConfig> loadExtconStateConfigs(Context context) { + String[] rows = context.getResources().getStringArray( + com.android.internal.R.array.config_dockExtconStateMapping); + try { + ArrayList<ExtconStateConfig> configs = new ArrayList<>(); + for (String row : rows) { + String[] rowFields = row.split(","); + ExtconStateConfig config = new ExtconStateConfig(Integer.parseInt(rowFields[0])); + for (int i = 1; i < rowFields.length; i++) { + String[] keyValueFields = rowFields[i].split("="); + if (keyValueFields.length != 2) { + throw new IllegalArgumentException("Invalid key-value: " + rowFields[i]); + } + config.keyValuePairs.add(Pair.create(keyValueFields[0], keyValueFields[1])); + } + configs.add(config); + } + return configs; + } catch (IllegalArgumentException | ArrayIndexOutOfBoundsException e) { + Slog.e(TAG, "Could not parse extcon state config", e); + return new ArrayList<>(); + } + } + public DockObserver(Context context) { super(context); @@ -77,9 +165,25 @@ final class DockObserver extends SystemService { mAllowTheaterModeWakeFromDock = context.getResources().getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromDock); - init(); // set initial status + mExtconStateConfigs = loadExtconStateConfigs(context); + + List<ExtconInfo> infos = ExtconInfo.getExtconInfoForTypes(new String[] { + ExtconInfo.EXTCON_DOCK + }); - mObserver.startObserving(DOCK_UEVENT_MATCH); + if (!infos.isEmpty()) { + ExtconInfo info = infos.get(0); + Slog.i(TAG, "Found extcon info devPath: " + info.getDevicePath() + + ", statePath: " + info.getStatePath()); + + // set initial status + setDockStateFromProviderLocked(ExtconStateProvider.fromFile(info.getStatePath())); + mPreviousDockState = mActualDockState; + + mExtconUEventObserver.startObserving(info); + } else { + Slog.i(TAG, "No extcon dock device found in this kernel."); + } } @Override @@ -101,26 +205,6 @@ final class DockObserver extends SystemService { } } - private void init() { - synchronized (mLock) { - try { - char[] buffer = new char[1024]; - FileReader file = new FileReader(DOCK_STATE_PATH); - try { - int len = file.read(buffer, 0, 1024); - setActualDockStateLocked(Integer.parseInt((new String(buffer, 0, len)).trim())); - mPreviousDockState = mActualDockState; - } finally { - file.close(); - } - } catch (FileNotFoundException e) { - Slog.w(TAG, "This kernel does not have dock station support"); - } catch (Exception e) { - Slog.e(TAG, "" , e); - } - } - } - private void setActualDockStateLocked(int newState) { mActualDockState = newState; if (!mUpdatesStopped) { @@ -234,19 +318,50 @@ final class DockObserver extends SystemService { } }; - private final UEventObserver mObserver = new UEventObserver() { - @Override - public void onUEvent(UEventObserver.UEvent event) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Slog.v(TAG, "Dock UEVENT: " + event.toString()); + private int getDockedStateExtraValue(ExtconStateProvider state) { + for (ExtconStateConfig config : mExtconStateConfigs) { + boolean match = true; + for (Pair<String, String> keyValue : config.keyValuePairs) { + String stateValue = state.getValue(keyValue.first); + match = match && keyValue.second.equals(stateValue); + if (!match) { + break; + } } - try { - synchronized (mLock) { - setActualDockStateLocked(Integer.parseInt(event.get("SWITCH_STATE"))); + if (match) { + return config.extraStateValue; + } + } + + return Intent.EXTRA_DOCK_STATE_DESK; + } + + @VisibleForTesting + void setDockStateFromProviderForTesting(ExtconStateProvider provider) { + synchronized (mLock) { + setDockStateFromProviderLocked(provider); + } + } + + private void setDockStateFromProviderLocked(ExtconStateProvider provider) { + int state = Intent.EXTRA_DOCK_STATE_UNDOCKED; + if ("1".equals(provider.getValue("DOCK"))) { + state = getDockedStateExtraValue(provider); + } + setActualDockStateLocked(state); + } + + private final ExtconUEventObserver mExtconUEventObserver = new ExtconUEventObserver() { + @Override + public void onUEvent(ExtconInfo extconInfo, UEventObserver.UEvent event) { + synchronized (mLock) { + String stateString = event.get("STATE"); + if (stateString != null) { + setDockStateFromProviderLocked(ExtconStateProvider.fromString(stateString)); + } else { + Slog.e(TAG, "Extcon event missing STATE: " + event); } - } catch (NumberFormatException e) { - Slog.e(TAG, "Could not parse switch state from event " + event); } } }; diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index b5c0a67be20f..9353dd832bba 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2721,7 +2721,8 @@ public final class ActiveServices { int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, final IServiceConnection connection, int flags, - String instanceName, String callingPackage, final int userId) + String instanceName, boolean isSupplementalProcessService, String callingPackage, + final int userId) throws TransactionTooLargeException { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service + " type=" + resolvedType + " conn=" + connection.asBinder() @@ -2805,10 +2806,9 @@ public final class ActiveServices { final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0; final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0; - ServiceLookupResult res = - retrieveServiceLocked(service, instanceName, resolvedType, callingPackage, - callingPid, callingUid, userId, true, - callerFg, isBindExternal, allowInstant); + ServiceLookupResult res = retrieveServiceLocked(service, instanceName, + isSupplementalProcessService, resolvedType, callingPackage, callingPid, callingUid, + userId, true, callerFg, isBindExternal, allowInstant); if (res == null) { return 0; } @@ -3228,6 +3228,20 @@ public final class ActiveServices { int callingPid, int callingUid, int userId, boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal, boolean allowInstant) { + return retrieveServiceLocked(service, instanceName, false, resolvedType, callingPackage, + callingPid, callingUid, userId, createIfNeeded, callingFromFg, isBindExternal, + allowInstant); + } + + private ServiceLookupResult retrieveServiceLocked(Intent service, + String instanceName, boolean isSupplementalProcessService, String resolvedType, + String callingPackage, int callingPid, int callingUid, int userId, + boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal, + boolean allowInstant) { + if (isSupplementalProcessService && instanceName == null) { + throw new IllegalArgumentException("No instanceName provided for supplemental process"); + } + ServiceRecord r = null; if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "retrieveServiceLocked: " + service + " type=" + resolvedType + " callingUid=" + callingUid); @@ -3249,7 +3263,6 @@ public final class ActiveServices { if (instanceName == null) { comp = service.getComponent(); } else { - // This is for isolated services final ComponentName realComp = service.getComponent(); if (realComp == null) { throw new IllegalArgumentException("Can't use custom instance name '" + instanceName @@ -3304,12 +3317,19 @@ public final class ActiveServices { return null; } if (instanceName != null - && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) { + && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0 + && !isSupplementalProcessService) { throw new IllegalArgumentException("Can't use instance name '" + instanceName - + "' with non-isolated service '" + sInfo.name + "'"); + + "' with non-isolated non-supplemental service '" + sInfo.name + "'"); } - ComponentName className = new ComponentName( - sInfo.applicationInfo.packageName, sInfo.name); + if (isSupplementalProcessService + && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) { + throw new IllegalArgumentException("Service cannot be both supplemental and " + + "isolated"); + } + + ComponentName className = new ComponentName(sInfo.applicationInfo.packageName, + sInfo.name); ComponentName name = comp != null ? comp : className; if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid, name.getPackageName(), sInfo.applicationInfo.uid)) { @@ -3392,7 +3412,8 @@ public final class ActiveServices { = new Intent.FilterComparison(service.cloneFilter()); final ServiceRestarter res = new ServiceRestarter(); r = new ServiceRecord(mAm, className, name, definingPackageName, - definingUid, filter, sInfo, callingFromFg, res); + definingUid, filter, sInfo, callingFromFg, res, + isSupplementalProcessService); res.setService(r); smap.mServicesByInstanceName.put(name, r); smap.mServicesByIntent.put(filter, r); diff --git a/services/core/java/com/android/server/am/ActivityManagerLocal.java b/services/core/java/com/android/server/am/ActivityManagerLocal.java index 9a1bfddcec07..d9ee7d974864 100644 --- a/services/core/java/com/android/server/am/ActivityManagerLocal.java +++ b/services/core/java/com/android/server/am/ActivityManagerLocal.java @@ -18,6 +18,9 @@ package com.android.server.am; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.TransactionTooLargeException; /** * Interface for in-process calls into @@ -58,4 +61,24 @@ public interface ActivityManagerLocal { * @hide */ void tempAllowWhileInUsePermissionInFgs(int uid, long durationMs); + + /** + * Starts a supplemental process service and binds to it. You can through the arguments here + * have the system bring up multiple concurrent processes hosting their own instance of that + * service. The <var>userAppUid</var> you provide here identifies the different instances - each + * unique uid is attributed to a supplemental process. + * + * @param service Identifies the supplemental process service to connect to. The Intent must + * specify an explicit component name. This value cannot be null. + * @param conn Receives information as the service is started and stopped. + * This must be a valid ServiceConnection object; it must not be null. + * @param userAppUid Uid of the app for which the supplemental process needs to be spawned. + * @return {@code true} if the system is in the process of bringing up a + * service that your client has permission to bind to; {@code false} + * if the system couldn't find the service or if your client doesn't + * have permission to bind to it. + */ + boolean startAndBindSupplementalProcessService(@NonNull Intent service, + @NonNull ServiceConnection conn, int userAppUid) throws TransactionTooLargeException; + } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 902659c27818..442b9de9911d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -218,6 +218,7 @@ import android.content.IIntentSender; import android.content.Intent; import android.content.IntentFilter; import android.content.LocusId; +import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ActivityPresentationInfo; import android.content.pm.ApplicationInfo; @@ -256,6 +257,7 @@ import android.os.BinderProxy; import android.os.BugreportParams; import android.os.Build; import android.os.Bundle; +import android.os.ConditionVariable; import android.os.Debug; import android.os.DropBoxManager; import android.os.FactoryTest; @@ -336,7 +338,7 @@ import com.android.internal.app.ProcessMap; import com.android.internal.app.SystemUserHomeActivity; import com.android.internal.app.procstats.ProcessState; import com.android.internal.app.procstats.ProcessStats; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BackgroundThread; @@ -4951,7 +4953,7 @@ public class ActivityManagerService extends IActivityManager.Stub // This line is needed to CTS test for the correct exception handling // See b/138952436#comment36 for context Slog.i(TAG, "About to commit checkpoint"); - IStorageManager storageManager = PackageHelper.getStorageManager(); + IStorageManager storageManager = InstallLocationUtils.getStorageManager(); storageManager.commitChanges(); } catch (Exception e) { PowerManager pm = (PowerManager) @@ -12314,13 +12316,25 @@ public class ActivityManagerService extends IActivityManager.Stub public int bindService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException { - return bindIsolatedService(caller, token, service, resolvedType, connection, flags, + return bindServiceInstance(caller, token, service, resolvedType, connection, flags, null, callingPackage, userId); } - public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service, + /** + * Binds to a service with a given instanceName, creating it if it does not already exist. + * If the instanceName field is not supplied, binding to the service occurs as usual. + */ + public int bindServiceInstance(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String instanceName, String callingPackage, int userId) throws TransactionTooLargeException { + return bindServiceInstance(caller, token, service, resolvedType, connection, flags, + instanceName, false, callingPackage, userId); + } + + private int bindServiceInstance(IApplicationThread caller, IBinder token, Intent service, + String resolvedType, IServiceConnection connection, int flags, String instanceName, + boolean isSupplementalProcessService, String callingPackage, int userId) + throws TransactionTooLargeException { enforceNotIsolatedCaller("bindService"); // Refuse possible leaked file descriptors @@ -12332,6 +12346,10 @@ public class ActivityManagerService extends IActivityManager.Stub throw new IllegalArgumentException("callingPackage cannot be null"); } + if (isSupplementalProcessService && instanceName == null) { + throw new IllegalArgumentException("No instance name provided for isolated process"); + } + // Ensure that instanceName, which is caller provided, does not contain // unusual characters. if (instanceName != null) { @@ -12345,8 +12363,8 @@ public class ActivityManagerService extends IActivityManager.Stub } synchronized(this) { - return mServices.bindServiceLocked(caller, token, service, - resolvedType, connection, flags, instanceName, callingPackage, userId); + return mServices.bindServiceLocked(caller, token, service, resolvedType, connection, + flags, instanceName, isSupplementalProcessService, callingPackage, userId); } } @@ -15478,6 +15496,62 @@ public class ActivityManagerService extends IActivityManager.Stub } } + /** + * Dump the resources structure for the given process + * + * @param process The process to dump resource info for + * @param fd The FileDescriptor to dump it into + * @throws RemoteException + */ + public boolean dumpResources(String process, ParcelFileDescriptor fd, RemoteCallback callback) + throws RemoteException { + synchronized (this) { + ProcessRecord proc = findProcessLOSP(process, UserHandle.USER_CURRENT, "dumpResources"); + IApplicationThread thread; + if (proc == null || (thread = proc.getThread()) == null) { + throw new IllegalArgumentException("Unknown process: " + process); + } + thread.dumpResources(fd, callback); + return true; + } + } + + /** + * Dump the resources structure for all processes + * + * @param fd The FileDescriptor to dump it into + * @throws RemoteException + */ + public void dumpAllResources(ParcelFileDescriptor fd, PrintWriter pw) throws RemoteException { + synchronized (mProcLock) { + mProcessList.forEachLruProcessesLOSP(true, app -> { + ConditionVariable lock = new ConditionVariable(); + RemoteCallback + finishCallback = new RemoteCallback(result -> lock.open(), null); + + pw.println(String.format("------ DUMP RESOURCES %s (%s) ------", + app.processName, + app.info.packageName)); + pw.flush(); + try { + app.getThread().dumpResources(fd.dup(), finishCallback); + lock.block(2000); + } catch (Exception e) { + pw.println(String.format( + "------ EXCEPTION DUMPING RESOURCES for %s (%s): %s ------", + app.processName, + app.info.packageName, + e.getMessage())); + pw.flush(); + } + pw.println(String.format("------ END DUMP RESOURCES %s (%s) ------", + app.processName, + app.info.packageName)); + pw.flush(); + }); + } + } + @Override public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize, String reportPackage) { @@ -15823,6 +15897,34 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public boolean startAndBindSupplementalProcessService(Intent service, + ServiceConnection conn, int userAppUid) throws TransactionTooLargeException { + if (service == null) { + throw new IllegalArgumentException("intent is null"); + } + if (conn == null) { + throw new IllegalArgumentException("connection is null"); + } + if (service.getComponent() == null) { + throw new IllegalArgumentException("service must specify explicit component"); + } + if (!UserHandle.isApp(userAppUid)) { + throw new IllegalArgumentException("uid is not within application range"); + } + + Handler handler = mContext.getMainThreadHandler(); + int flags = Context.BIND_AUTO_CREATE; + + final IServiceConnection sd = mContext.getServiceDispatcher(conn, handler, flags); + service.prepareToLeaveProcess(mContext); + return ActivityManagerService.this.bindServiceInstance( + mContext.getIApplicationThread(), mContext.getActivityToken(), service, + service.resolveTypeIfNeeded(mContext.getContentResolver()), sd, flags, + Integer.toString(userAppUid), /*isSupplementalProcessService*/ true, + mContext.getOpPackageName(), UserHandle.getUserId(userAppUid)) != 0; + } + + @Override public void onUserRemoved(@UserIdInt int userId) { // Clean up any ActivityTaskManager state (by telling it the user is stopped) mAtmInternal.onUserStopped(userId); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 24e7ba4a32b9..da78e2d7504e 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -570,6 +570,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN ComponentName instanceName, String definingPackageName, int definingUid, Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg, Runnable restarter) { + this(ams, name, instanceName, definingPackageName, definingUid, intent, sInfo, callerIsFg, + restarter, false); + } + + ServiceRecord(ActivityManagerService ams, ComponentName name, + ComponentName instanceName, String definingPackageName, int definingUid, + Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg, + Runnable restarter, boolean isSupplementalProcessService) { this.ams = ams; this.name = name; this.instanceName = instanceName; @@ -580,7 +588,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN serviceInfo = sInfo; appInfo = sInfo.applicationInfo; packageName = sInfo.applicationInfo.packageName; - if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) { + if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0 + || isSupplementalProcessService) { processName = sInfo.processName + ":" + instanceName.getClassName(); } else { processName = sInfo.processName; diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java index 913621913e95..af1dd335ab1e 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java @@ -98,7 +98,10 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan private final IGameServiceController mGameServiceController = new IGameServiceController.Stub() { @Override + @RequiresPermission(Manifest.permission.MANAGE_GAME_ACTIVITY) public void createGameSession(int taskId) { + mContext.enforceCallingPermission(Manifest.permission.MANAGE_GAME_ACTIVITY, + "createGameSession()"); mBackgroundExecutor.execute(() -> { GameServiceProviderInstanceImpl.this.createGameSession(taskId); }); @@ -116,9 +119,10 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan }); } - @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) + @Override + @RequiresPermission(Manifest.permission.MANAGE_GAME_ACTIVITY) public void restartGame(int taskId) { - mContext.enforceCallingPermission(Manifest.permission.FORCE_STOP_PACKAGES, + mContext.enforceCallingPermission(Manifest.permission.MANAGE_GAME_ACTIVITY, "restartGame()"); mBackgroundExecutor.execute(() -> { GameServiceProviderInstanceImpl.this.restartGame(taskId); @@ -372,12 +376,12 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan }, mBackgroundExecutor); AndroidFuture<Void> unusedPostCreateGameSessionFuture = - mGameSessionServiceConnector.post(gameService -> { + mGameSessionServiceConnector.post(gameSessionService -> { CreateGameSessionRequest createGameSessionRequest = new CreateGameSessionRequest( taskId, existingGameSessionRecord.getComponentName().getPackageName()); - gameService.create( + gameSessionService.create( mGameSessionController, createGameSessionRequest, gameSessionViewHostConfiguration, diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index 0da6a1ba3109..79705a32c264 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -53,6 +53,7 @@ import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; +import android.hardware.biometrics.common.OperationContext; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -64,6 +65,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.biometrics.log.BiometricFrameworkStatsLogger; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -681,16 +683,18 @@ public final class AuthSession implements IBinder.DeathRecipient { + ", Latency: " + latency); } - FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED, + final OperationContext operationContext = new OperationContext(); + operationContext.isCrypto = isCrypto(); + BiometricFrameworkStatsLogger.getInstance().authenticate( + operationContext, statsModality(), - mUserId, - isCrypto(), + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, - mPreAuthInfo.confirmationRequested, - FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED, - latency, mDebugEnabled, - -1 /* sensorId */, + latency, + FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED, + mPreAuthInfo.confirmationRequested, + mUserId, -1f /* ambientLightLux */); } else { final long latency = System.currentTimeMillis() - mStartTimeMs; @@ -711,17 +715,18 @@ public final class AuthSession implements IBinder.DeathRecipient { + ", Latency: " + latency); } // Auth canceled - FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED, + final OperationContext operationContext = new OperationContext(); + operationContext.isCrypto = isCrypto(); + BiometricFrameworkStatsLogger.getInstance().error( + operationContext, statsModality(), - mUserId, - isCrypto(), BiometricsProtoEnums.ACTION_AUTHENTICATE, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, - error, - 0 /* vendorCode */, mDebugEnabled, latency, - -1 /* sensorId */); + error, + 0 /* vendorCode */, + mUserId); } } diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContext.java b/services/core/java/com/android/server/biometrics/log/BiometricContext.java index 8d28298542dc..c5e266f87149 100644 --- a/services/core/java/com/android/server/biometrics/log/BiometricContext.java +++ b/services/core/java/com/android/server/biometrics/log/BiometricContext.java @@ -17,6 +17,8 @@ package com.android.server.biometrics.log; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; import android.hardware.biometrics.common.OperationContext; import java.util.function.Consumer; @@ -26,11 +28,21 @@ import java.util.function.Consumer; * logging or optimizations. */ public interface BiometricContext { - /** Gets the context source. */ - static BiometricContext getInstance() { - return BiometricContextProvider.sInstance.get(); + /** Gets the context source from the system context. */ + static BiometricContext getInstance(@NonNull Context context) { + return BiometricContextProvider.defaultProvider(context); } + /** Update the given context with the most recent values and return it. */ + OperationContext updateContext(@NonNull OperationContext operationContext, + boolean isCryptoOperation); + + /** The session id for keyguard entry, if active, or null. */ + @Nullable Integer getKeyguardEntrySessionId(); + + /** The session id for biometric prompt usage, if active, or null. */ + @Nullable Integer getBiometricPromptSessionId(); + /** If the display is in AOD. */ boolean isAoD(); diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java index 65e9e3dec60f..70acaff05e30 100644 --- a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java +++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java @@ -18,16 +18,22 @@ package com.android.server.biometrics.log; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.StatusBarManager; import android.content.Context; import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.common.OperationReason; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; -import android.util.Singleton; +import android.os.ServiceManager.ServiceNotFoundException; +import android.os.UserHandle; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.InstanceId; +import com.android.internal.statusbar.ISessionListener; import com.android.internal.statusbar.IStatusBarService; import java.util.Map; @@ -41,22 +47,41 @@ class BiometricContextProvider implements BiometricContext { private static final String TAG = "BiometricContextProvider"; - static final Singleton<BiometricContextProvider> sInstance = - new Singleton<BiometricContextProvider>() { - @Override - protected BiometricContextProvider create() { - return new BiometricContextProvider(IStatusBarService.Stub.asInterface( - ServiceManager.getService( + private static final int SESSION_TYPES = + StatusBarManager.SESSION_KEYGUARD | StatusBarManager.SESSION_BIOMETRIC_PROMPT; + + private static BiometricContextProvider sInstance; + + static BiometricContextProvider defaultProvider(@NonNull Context context) { + synchronized (BiometricContextProvider.class) { + if (sInstance == null) { + try { + sInstance = new BiometricContextProvider( + new AmbientDisplayConfiguration(context), + IStatusBarService.Stub.asInterface(ServiceManager.getServiceOrThrow( Context.STATUS_BAR_SERVICE)), null /* handler */); + } catch (ServiceNotFoundException e) { + throw new IllegalStateException("Failed to find required service", e); } - }; + } + } + return sInstance; + } @NonNull private final Map<OperationContext, Consumer<OperationContext>> mSubscribers = new ConcurrentHashMap<>(); + @Nullable + private final Map<Integer, InstanceId> mSession = new ConcurrentHashMap<>(); + + private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; + private boolean mIsDozing = false; + @VisibleForTesting - BiometricContextProvider(@NonNull IStatusBarService service, @Nullable Handler handler) { + BiometricContextProvider(@NonNull AmbientDisplayConfiguration ambientDisplayConfiguration, + @NonNull IStatusBarService service, @Nullable Handler handler) { + mAmbientDisplayConfiguration = ambientDisplayConfiguration; try { service.setBiometicContextListener(new IBiometricContextListener.Stub() { @Override @@ -73,16 +98,70 @@ class BiometricContextProvider implements BiometricContext { } } }); + service.registerSessionListener(SESSION_TYPES, new ISessionListener.Stub() { + @Override + public void onSessionStarted(int sessionType, InstanceId instance) { + mSession.put(sessionType, instance); + } + + @Override + public void onSessionEnded(int sessionType, InstanceId instance) { + final InstanceId id = mSession.remove(sessionType); + if (id != null && instance != null && id.getId() != instance.getId()) { + Slog.w(TAG, "session id mismatch"); + } + } + }); } catch (RemoteException e) { Slog.e(TAG, "Unable to register biometric context listener", e); } } - private boolean mIsDozing = false; + @Override + public OperationContext updateContext(@NonNull OperationContext operationContext, + boolean isCryptoOperation) { + operationContext.isAoD = isAoD(); + operationContext.isCrypto = isCryptoOperation; + setFirstSessionId(operationContext); + return operationContext; + } + + private void setFirstSessionId(@NonNull OperationContext operationContext) { + Integer sessionId = getKeyguardEntrySessionId(); + if (sessionId != null) { + operationContext.id = sessionId; + operationContext.reason = OperationReason.KEYGUARD; + return; + } + + sessionId = getBiometricPromptSessionId(); + if (sessionId != null) { + operationContext.id = sessionId; + operationContext.reason = OperationReason.BIOMETRIC_PROMPT; + return; + } + + operationContext.id = 0; + operationContext.reason = OperationReason.UNKNOWN; + } + + @Nullable + @Override + public Integer getKeyguardEntrySessionId() { + final InstanceId id = mSession.get(StatusBarManager.SESSION_KEYGUARD); + return id != null ? id.getId() : null; + } + + @Nullable + @Override + public Integer getBiometricPromptSessionId() { + final InstanceId id = mSession.get(StatusBarManager.SESSION_BIOMETRIC_PROMPT); + return id != null ? id.getId() : null; + } @Override public boolean isAoD() { - return mIsDozing; + return mIsDozing && mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT); } @Override @@ -98,7 +177,7 @@ class BiometricContextProvider implements BiometricContext { private void notifySubscribers() { mSubscribers.forEach((context, consumer) -> { - context.isAoD = mIsDozing; + context.isAoD = isAoD(); consumer.accept(context); }); } diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java index c3471bd1d771..8965227a1bb4 100644 --- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java +++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java @@ -17,6 +17,8 @@ package com.android.server.biometrics.log; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.common.OperationReason; import android.util.Slog; import com.android.internal.util.FrameworkStatsLog; @@ -33,42 +35,49 @@ public class BiometricFrameworkStatsLogger { private BiometricFrameworkStatsLogger() {} + /** Shared instance. */ public static BiometricFrameworkStatsLogger getInstance() { return sInstance; } /** {@see FrameworkStatsLog.BIOMETRIC_ACQUIRED}. */ - public void acquired( + public void acquired(OperationContext operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, - int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) { + int acquiredInfo, int vendorCode, int targetUserId) { FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED, statsModality, targetUserId, - isCrypto, + operationContext.isCrypto, statsAction, statsClient, acquiredInfo, vendorCode, isDebug, - -1 /* sensorId */); + -1 /* sensorId */, + operationContext.id, + sessionType(operationContext.reason), + operationContext.isAoD); } /** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */ - public void authenticate( + public void authenticate(OperationContext operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, long latency, - boolean authenticated, int authState, boolean requireConfirmation, boolean isCrypto, - int targetUserId, boolean isBiometricPrompt, float ambientLightLux) { + int authState, boolean requireConfirmation, + int targetUserId, float ambientLightLux) { FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED, statsModality, targetUserId, - isCrypto, + operationContext.isCrypto, statsClient, requireConfirmation, authState, sanitizeLatency(latency), isDebug, -1 /* sensorId */, - ambientLightLux); + ambientLightLux, + operationContext.id, + sessionType(operationContext.reason), + operationContext.isAoD); } /** {@see FrameworkStatsLog.BIOMETRIC_ENROLLED}. */ @@ -84,20 +93,23 @@ public class BiometricFrameworkStatsLogger { } /** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */ - public void error( + public void error(OperationContext operationContext, int statsModality, int statsAction, int statsClient, boolean isDebug, long latency, - int error, int vendorCode, boolean isCrypto, int targetUserId) { + int error, int vendorCode, int targetUserId) { FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED, statsModality, targetUserId, - isCrypto, + operationContext.isCrypto, statsAction, statsClient, error, vendorCode, isDebug, sanitizeLatency(latency), - -1 /* sensorId */); + -1 /* sensorId */, + operationContext.id, + sessionType(operationContext.reason), + operationContext.isAoD); } /** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */ @@ -123,4 +135,14 @@ public class BiometricFrameworkStatsLogger { } return latency; } + + private static int sessionType(@OperationReason byte reason) { + if (reason == OperationReason.BIOMETRIC_PROMPT) { + return BiometricsProtoEnums.SESSION_TYPE_BIOMETRIC_PROMPT; + } + if (reason == OperationReason.KEYGUARD) { + return BiometricsProtoEnums.SESSION_TYPE_KEYGUARD_ENTRY; + } + return BiometricsProtoEnums.SESSION_TYPE_UNKNOWN; + } } diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java index 018839079e22..2a8d9f1967eb 100644 --- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java +++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java @@ -25,6 +25,7 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.common.OperationContext; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.util.Slog; @@ -144,8 +145,8 @@ public class BiometricLogger { } /** Log an acquisition event. */ - public void logOnAcquired(Context context, - int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) { + public void logOnAcquired(Context context, OperationContext operationContext, + int acquiredInfo, int vendorCode, int targetUserId) { if (!mShouldLogMetrics) { return; } @@ -165,7 +166,7 @@ public class BiometricLogger { if (DEBUG) { Slog.v(TAG, "Acquired! Modality: " + mStatsModality + ", User: " + targetUserId - + ", IsCrypto: " + isCrypto + + ", IsCrypto: " + operationContext.isCrypto + ", Action: " + mStatsAction + ", Client: " + mStatsClient + ", AcquiredInfo: " + acquiredInfo @@ -176,14 +177,14 @@ public class BiometricLogger { return; } - mSink.acquired(mStatsModality, mStatsAction, mStatsClient, + mSink.acquired(operationContext, mStatsModality, mStatsAction, mStatsClient, Utils.isDebugEnabled(context, targetUserId), - acquiredInfo, vendorCode, isCrypto, targetUserId); + acquiredInfo, vendorCode, targetUserId); } /** Log an error during an operation. */ - public void logOnError(Context context, - int error, int vendorCode, boolean isCrypto, int targetUserId) { + public void logOnError(Context context, OperationContext operationContext, + int error, int vendorCode, int targetUserId) { if (!mShouldLogMetrics) { return; } @@ -194,7 +195,7 @@ public class BiometricLogger { if (DEBUG) { Slog.v(TAG, "Error! Modality: " + mStatsModality + ", User: " + targetUserId - + ", IsCrypto: " + isCrypto + + ", IsCrypto: " + operationContext.isCrypto + ", Action: " + mStatsAction + ", Client: " + mStatsClient + ", Error: " + error @@ -208,14 +209,14 @@ public class BiometricLogger { return; } - mSink.error(mStatsModality, mStatsAction, mStatsClient, + mSink.error(operationContext, mStatsModality, mStatsAction, mStatsClient, Utils.isDebugEnabled(context, targetUserId), latency, - error, vendorCode, isCrypto, targetUserId); + error, vendorCode, targetUserId); } /** Log authentication attempt. */ - public void logOnAuthenticated(Context context, - boolean authenticated, boolean requireConfirmation, boolean isCrypto, + public void logOnAuthenticated(Context context, OperationContext operationContext, + boolean authenticated, boolean requireConfirmation, int targetUserId, boolean isBiometricPrompt) { if (!mShouldLogMetrics) { return; @@ -241,7 +242,7 @@ public class BiometricLogger { if (DEBUG) { Slog.v(TAG, "Authenticated! Modality: " + mStatsModality + ", User: " + targetUserId - + ", IsCrypto: " + isCrypto + + ", IsCrypto: " + operationContext.isCrypto + ", Client: " + mStatsClient + ", RequireConfirmation: " + requireConfirmation + ", State: " + authState @@ -255,10 +256,9 @@ public class BiometricLogger { return; } - mSink.authenticate(mStatsModality, mStatsAction, mStatsClient, + mSink.authenticate(operationContext, mStatsModality, mStatsAction, mStatsClient, Utils.isDebugEnabled(context, targetUserId), - latency, authenticated, authState, requireConfirmation, isCrypto, - targetUserId, isBiometricPrompt, mLastAmbientLux); + latency, authState, requireConfirmation, targetUserId, mLastAmbientLux); } /** Log enrollment outcome. */ 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 e07a68c6d94d..0f0032b72b0a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -110,8 +110,8 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement // that do not handle lockout under the HAL. In these cases, ensure that the framework only // sends errors once per ClientMonitor. if (mShouldSendErrorToClient) { - getLogger().logOnError(getContext(), errorCode, vendorCode, - isCryptoOperation(), getTargetUserId()); + getLogger().logOnError(getContext(), getOperationContext(), + errorCode, vendorCode, getTargetUserId()); try { if (getListener() != null) { mShouldSendErrorToClient = false; @@ -169,8 +169,8 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement protected final void onAcquiredInternal(int acquiredInfo, int vendorCode, boolean shouldSend) { - getLogger().logOnAcquired(getContext(), acquiredInfo, vendorCode, - isCryptoOperation(), getTargetUserId()); + getLogger().logOnAcquired(getContext(), getOperationContext(), + acquiredInfo, vendorCode, getTargetUserId()); if (DEBUG) { Slog.v(TAG, "Acquired: " + acquiredInfo + " " + vendorCode + ", shouldSend: " + shouldSend); 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 949edd0e79ed..54b79e1f8e4a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -166,8 +166,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> @Override public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> hardwareAuthToken) { - getLogger().logOnAuthenticated(getContext(), authenticated, mRequireConfirmation, - isCryptoOperation(), getTargetUserId(), isBiometricPrompt()); + getLogger().logOnAuthenticated(getContext(), getOperationContext(), + authenticated, mRequireConfirmation, getTargetUserId(), isBiometricPrompt()); final ClientMonitorCallbackConverter listener = getListener(); diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java index eabafceab590..a6e89115400d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java @@ -37,7 +37,7 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor { protected final Supplier<T> mLazyDaemon; @NonNull - protected final OperationContext mOperationContext = new OperationContext(); + private final OperationContext mOperationContext = new OperationContext(); /** * @param context system_server context @@ -82,6 +82,23 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor { super.destroy(); // subclasses should do this earlier in most cases, but ensure it happens now + unsubscribeBiometricContext(); + } + + protected OperationContext getOperationContext() { + return getBiometricContext().updateContext(mOperationContext, isCryptoOperation()); + } + + protected ClientMonitorCallback getBiometricContextUnsubscriber() { + return new ClientMonitorCallback() { + @Override + public void onClientFinished(@NonNull BaseClientMonitor monitor, boolean success) { + unsubscribeBiometricContext(); + } + }; + } + + protected void unsubscribeBiometricContext() { getBiometricContext().unsubscribe(mOperationContext); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 039b08e805c1..2e820574b435 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -57,6 +57,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; @@ -645,7 +646,7 @@ public class FaceService extends SystemService { try { final SensorProps[] props = face.getSensorProps(); final FaceProvider provider = new FaceProvider(getContext(), props, instance, - mLockoutResetDispatcher); + mLockoutResetDispatcher, BiometricContext.getInstance(getContext())); mServiceProviders.add(provider); } catch (RemoteException e) { Slog.e(TAG, "Remote exception in getSensorProps: " + fqName); 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 75a1e0cf21a8..7765ab3b8e53 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 @@ -26,8 +26,6 @@ import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.common.ICancellationSignal; -import android.hardware.biometrics.common.OperationContext; -import android.hardware.biometrics.common.OperationReason; import android.hardware.biometrics.face.IFace; import android.hardware.face.FaceAuthenticationFrame; import android.hardware.face.FaceManager; @@ -157,13 +155,8 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession> final AidlSession session = getFreshDaemon(); if (session.hasContextMethods()) { - final OperationContext context = new OperationContext(); - // TODO: add reason, id - context.id = 0; - context.reason = OperationReason.UNKNOWN; - context.isAoD = getBiometricContext().isAoD(); - context.isCrypto = isCryptoOperation(); - return session.getSession().authenticateWithContext(mOperationId, context); + return session.getSession().authenticateWithContext( + mOperationId, getOperationContext()); } else { return session.getSession().authenticate(mOperationId); } @@ -282,8 +275,8 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession> mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED); // Lockout metrics are logged as an error code. final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT; - getLogger().logOnError(getContext(), error, 0 /* vendorCode */, - isCryptoOperation(), getTargetUserId()); + getLogger().logOnError(getContext(), getOperationContext(), + error, 0 /* vendorCode */, getTargetUserId()); try { getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); @@ -298,8 +291,8 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession> mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT); // Lockout metrics are logged as an error code. final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT; - getLogger().logOnError(getContext(), error, 0 /* vendorCode */, - isCryptoOperation(), getTargetUserId()); + getLogger().logOnError(getContext(), getOperationContext(), + error, 0 /* vendorCode */, getTargetUserId()); try { getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java index c79e60124c45..efedcf815228 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java @@ -22,8 +22,6 @@ import android.content.Context; import android.hardware.SensorPrivacyManager; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.common.ICancellationSignal; -import android.hardware.biometrics.common.OperationContext; -import android.hardware.biometrics.common.OperationReason; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; @@ -116,13 +114,7 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements final AidlSession session = getFreshDaemon(); if (session.hasContextMethods()) { - final OperationContext context = new OperationContext(); - // TODO: add reason, id - context.id = 0; - context.reason = OperationReason.UNKNOWN; - context.isAoD = getBiometricContext().isAoD(); - context.isCrypto = isCryptoOperation(); - return session.getSession().detectInteractionWithContext(context); + return session.getSession().detectInteractionWithContext(getOperationContext()); } else { return session.getSession().detectInteraction(); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index 6f6dadc8a041..da7853654ebf 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -21,8 +21,6 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.common.ICancellationSignal; -import android.hardware.biometrics.common.OperationContext; -import android.hardware.biometrics.common.OperationReason; import android.hardware.biometrics.face.EnrollmentType; import android.hardware.biometrics.face.Feature; import android.hardware.biometrics.face.IFace; @@ -200,14 +198,8 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> { HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken); if (session.hasContextMethods()) { - final OperationContext context = new OperationContext(); - // TODO: add reason, id - context.id = 0; - context.reason = OperationReason.UNKNOWN; - context.isAoD = getBiometricContext().isAoD(); - context.isCrypto = isCryptoOperation(); return session.getSession().enrollWithContext( - hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle, context); + hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle, getOperationContext()); } else { return session.getSession().enroll(hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle); 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 64b0892e92bb..4e03ee9a618c 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 @@ -90,7 +90,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull private final BiometricTaskStackListener mTaskStackListener; // for requests that do not use biometric prompt @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); - + @NonNull private final BiometricContext mBiometricContext; @Nullable private IFace mDaemon; private final class BiometricTaskStackListener extends TaskStackListener { @@ -128,7 +128,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { public FaceProvider(@NonNull Context context, @NonNull SensorProps[] props, @NonNull String halInstanceName, - @NonNull LockoutResetDispatcher lockoutResetDispatcher) { + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull BiometricContext biometricContext) { mContext = context; mHalInstanceName = halInstanceName; mSensors = new SparseArray<>(); @@ -137,6 +138,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mLockoutResetDispatcher = lockoutResetDispatcher; mActivityTaskManager = ActivityTaskManager.getInstance(); mTaskStackListener = new BiometricTaskStackListener(); + mBiometricContext = biometricContext; for (SensorProps prop : props) { final int sensorId = prop.commonProps.sensorId; @@ -156,7 +158,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { prop.supportsDetectInteraction, prop.halControlsPreview, false /* resetLockoutRequiresChallenge */); final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler, - internalProp, lockoutResetDispatcher); + internalProp, lockoutResetDispatcher, mBiometricContext); mSensors.put(sensorId, sensor); Slog.d(getTag(), "Added: " + internalProp); @@ -242,7 +244,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mContext.getOpPackageName(), sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), + mBiometricContext, mSensors.get(sensorId).getAuthenticatorIds()); scheduleForSensor(sensorId, client); @@ -253,7 +255,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mHandler.post(() -> { final InvalidationRequesterClient<Face> client = new InvalidationRequesterClient<>(mContext, userId, sensorId, - BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), + BiometricLogger.ofUnknown(mContext), + mBiometricContext, FaceUtils.getInstance(sensorId)); scheduleForSensor(sensorId, client); }); @@ -294,7 +297,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mSensors.get(sensorId).getLazySession(), userId, sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), + mBiometricContext, mSensors.get(sensorId).getAuthenticatorIds(), callback); scheduleForSensor(sensorId, client); }); @@ -324,7 +327,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance()); + mBiometricContext); scheduleForSensor(sensorId, client); }); } @@ -337,7 +340,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mSensors.get(sensorId).getLazySession(), token, userId, opPackageName, sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), challenge); + mBiometricContext, challenge); scheduleForSensor(sensorId, client); }); } @@ -358,7 +361,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { ENROLL_TIMEOUT_SEC, previewSurface, sensorId, createLogger(BiometricsProtoEnums.ACTION_ENROLL, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), maxTemplatesPerUser, debugConsent); + mBiometricContext, maxTemplatesPerUser, debugConsent); scheduleForSensor(sensorId, client, new ClientMonitorCallback() { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, @@ -391,7 +394,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mSensors.get(sensorId).getLazySession(), token, id, callback, userId, opPackageName, sensorId, createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), - BiometricContext.getInstance(), isStrongBiometric); + mBiometricContext, isStrongBiometric); scheduleForSensor(sensorId, client); }); @@ -416,7 +419,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { userId, operationId, restricted, opPackageName, cookie, false /* requireConfirmation */, sensorId, createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), - BiometricContext.getInstance(), isStrongBiometric, + mBiometricContext, isStrongBiometric, mUsageStats, mSensors.get(sensorId).getLockoutCache(), allowBackgroundAuthentication, isKeyguardBypassEnabled); scheduleForSensor(sensorId, client); @@ -472,7 +475,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { opPackageName, FaceUtils.getInstance(sensorId), sensorId, createLogger(BiometricsProtoEnums.ACTION_REMOVE, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), + mBiometricContext, mSensors.get(sensorId).getAuthenticatorIds()); scheduleForSensor(sensorId, client); }); @@ -486,7 +489,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mContext.getOpPackageName(), sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), hardwareAuthToken, + mBiometricContext, hardwareAuthToken, mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher); scheduleForSensor(sensorId, client); @@ -508,7 +511,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), userId, mContext.getOpPackageName(), sensorId, - BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), + BiometricLogger.ofUnknown(mContext), mBiometricContext, feature, enabled, hardwareAuthToken); scheduleForSensor(sensorId, client); }); @@ -527,7 +530,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId, mContext.getOpPackageName(), sensorId, BiometricLogger.ofUnknown(mContext), - BiometricContext.getInstance()); + mBiometricContext); scheduleForSensor(sensorId, client); }); } @@ -550,7 +553,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mContext.getOpPackageName(), sensorId, createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), enrolledList, + mBiometricContext, enrolledList, FaceUtils.getInstance(sensorId), mSensors.get(sensorId).getAuthenticatorIds()); scheduleForSensor(sensorId, client, callback); 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 fa07d120eb71..b69c7600e75a 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 @@ -476,7 +476,8 @@ public class Sensor { Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context, @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties, - @NonNull LockoutResetDispatcher lockoutResetDispatcher) { + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull BiometricContext biometricContext) { mTag = tag; mProvider = provider; mContext = context; @@ -492,7 +493,7 @@ public class Sensor { public StopUserClient<?> getStopUserClient(int userId) { return new FaceStopUserClient(mContext, mLazySession, mToken, userId, mSensorProperties.sensorId, - BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), + BiometricLogger.ofUnknown(mContext), biometricContext, () -> mCurrentSession = null); } @@ -529,7 +530,7 @@ public class Sensor { return new FaceStartUserClient(mContext, provider::getHalInstance, mToken, newUserId, mSensorProperties.sensorId, - BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), + BiometricLogger.ofUnknown(mContext), biometricContext, resultController, userStartedCallback); } }); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index be1ed7d92aeb..73c759f7738c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -119,6 +119,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull private final Map<Integer, Long> mAuthenticatorIds; @Nullable private IBiometricsFace mDaemon; @NonNull private final HalResultController mHalResultController; + @NonNull private final BiometricContext mBiometricContext; // for requests that do not use biometric prompt @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); private int mCurrentUserId = UserHandle.USER_NULL; @@ -155,6 +156,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull private final LockoutHalImpl mLockoutTracker; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; + HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler, @NonNull BiometricScheduler scheduler, @NonNull LockoutHalImpl lockoutTracker, @NonNull LockoutResetDispatcher lockoutResetDispatcher) { @@ -337,12 +339,14 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull FaceSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull Handler handler, - @NonNull BiometricScheduler scheduler) { + @NonNull BiometricScheduler scheduler, + @NonNull BiometricContext biometricContext) { mSensorProperties = sensorProps; mContext = context; mSensorId = sensorProps.sensorId; mScheduler = scheduler; mHandler = handler; + mBiometricContext = biometricContext; mUsageStats = new UsageStats(context); mAuthenticatorIds = new HashMap<>(); mLazyDaemon = Face10.this::getDaemon; @@ -367,7 +371,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final Handler handler = new Handler(Looper.getMainLooper()); return new Face10(context, sensorProps, lockoutResetDispatcher, handler, new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE, - null /* gestureAvailabilityTracker */)); + null /* gestureAvailabilityTracker */), + BiometricContext.getInstance(context)); } @Override @@ -538,7 +543,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { opPackageName, mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), sSystemClock.millis()); + mBiometricContext, sSystemClock.millis()); mGeneratedChallengeCache = client; mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override @@ -570,7 +575,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mLazyDaemon, token, userId, opPackageName, mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance()); + mBiometricContext); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, @@ -601,7 +606,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { ENROLL_TIMEOUT_SEC, previewSurface, mSensorId, createLogger(BiometricsProtoEnums.ACTION_ENROLL, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance()); + mBiometricContext); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override @@ -649,8 +654,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mLazyDaemon, token, requestId, receiver, userId, operationId, restricted, opPackageName, cookie, false /* requireConfirmation */, mSensorId, createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), - BiometricContext.getInstance(), isStrongBiometric, mLockoutTracker, mUsageStats, - allowBackgroundAuthentication, isKeyguardBypassEnabled); + mBiometricContext, isStrongBiometric, mLockoutTracker, + mUsageStats, allowBackgroundAuthentication, isKeyguardBypassEnabled); mScheduler.scheduleClientMonitor(client); }); } @@ -685,7 +690,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { FaceUtils.getLegacyInstance(mSensorId), mSensorId, createLogger(BiometricsProtoEnums.ACTION_REMOVE, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), mAuthenticatorIds); + mBiometricContext, mAuthenticatorIds); mScheduler.scheduleClientMonitor(client); }); } @@ -703,7 +708,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { FaceUtils.getLegacyInstance(mSensorId), mSensorId, createLogger(BiometricsProtoEnums.ACTION_REMOVE, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), mAuthenticatorIds); + mBiometricContext, mAuthenticatorIds); mScheduler.scheduleClientMonitor(client); }); } @@ -722,7 +727,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), hardwareAuthToken); + mBiometricContext, hardwareAuthToken); mScheduler.scheduleClientMonitor(client); }); } @@ -744,7 +749,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName, mSensorId, BiometricLogger.ofUnknown(mContext), - BiometricContext.getInstance(), feature, enabled, hardwareAuthToken, faceId); + mBiometricContext, + feature, enabled, hardwareAuthToken, faceId); mScheduler.scheduleClientMonitor(client); }); } @@ -764,7 +770,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final int faceId = faces.get(0).getBiometricId(); final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon, token, listener, userId, opPackageName, mSensorId, - BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), + BiometricLogger.ofUnknown(mContext), mBiometricContext, feature, faceId); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override @@ -793,7 +799,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), enrolledList, + mBiometricContext, enrolledList, FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, callback); }); @@ -918,7 +924,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), hasEnrolled, mAuthenticatorIds); + mBiometricContext, hasEnrolled, mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, 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 6366e19ef191..b4befd23671f 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 @@ -83,6 +83,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; @@ -815,7 +816,8 @@ public class FingerprintService extends SystemService { UserHandle.USER_CURRENT) != 0) { fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), mFingerprintStateCallback, hidlSensor, - mLockoutResetDispatcher, mGestureAvailabilityDispatcher); + mLockoutResetDispatcher, mGestureAvailabilityDispatcher, + BiometricContext.getInstance(getContext())); } else { fingerprint21 = Fingerprint21.newInstance(getContext(), mFingerprintStateCallback, hidlSensor, mHandler, @@ -843,7 +845,8 @@ public class FingerprintService extends SystemService { final FingerprintProvider provider = new FingerprintProvider(getContext(), mFingerprintStateCallback, props, instance, mLockoutResetDispatcher, - mGestureAvailabilityDispatcher); + mGestureAvailabilityDispatcher, + BiometricContext.getInstance(getContext())); mServiceProviders.add(provider); } catch (RemoteException e) { Slog.e(TAG, "Remote exception in getSensorProps: " + fqName); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index 184e1ea7d003..d26a78008529 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -24,7 +24,7 @@ import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired; import android.hardware.biometrics.common.ICancellationSignal; -import android.hardware.biometrics.common.OperationReason; +import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.fingerprint.PointerContext; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.ISidefpsController; @@ -108,7 +108,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> @NonNull @Override protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) { - return new ClientMonitorCompositeCallback(mALSProbeCallback, callback); + return new ClientMonitorCompositeCallback(mALSProbeCallback, + getBiometricContextUnsubscriber(), callback); } @Override @@ -178,12 +179,17 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> final AidlSession session = getFreshDaemon(); if (session.hasContextMethods()) { - // TODO: add reason, id - mOperationContext.id = 0; - mOperationContext.reason = OperationReason.UNKNOWN; - mOperationContext.isAoD = getBiometricContext().isAoD(); - mOperationContext.isCrypto = isCryptoOperation(); - return session.getSession().authenticateWithContext(mOperationId, mOperationContext); + final OperationContext opContext = getOperationContext(); + final ICancellationSignal cancel = session.getSession().authenticateWithContext( + mOperationId, opContext); + getBiometricContext().subscribe(opContext, ctx -> { + try { + session.getSession().onContextChanged(ctx); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to notify context changed", e); + } + }); + return cancel; } else { return session.getSession().authenticate(mOperationId); } @@ -192,6 +198,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> @Override protected void stopHalOperation() { mSensorOverlays.hide(getSensorId()); + unsubscribeBiometricContext(); + if (mCancellationSignal != null) { try { mCancellationSignal.cancel(); @@ -221,7 +229,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> context.y = y; context.minor = minor; context.major = major; - context.isAoD = false; // TODO; get value + context.isAoD = getBiometricContext().isAoD(); session.getSession().onPointerDownWithContext(context); } else { session.getSession().onPointerDown(0 /* pointerId */, x, y, minor, major); @@ -279,8 +287,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED); // Lockout metrics are logged as an error code. final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT; - getLogger().logOnError(getContext(), error, 0 /* vendorCode */, - isCryptoOperation(), getTargetUserId()); + getLogger().logOnError(getContext(), getOperationContext(), + error, 0 /* vendorCode */, getTargetUserId()); try { getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); @@ -298,8 +306,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT); // Lockout metrics are logged as an error code. final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT; - getLogger().logOnError(getContext(), error, 0 /* vendorCode */, - isCryptoOperation(), getTargetUserId()); + getLogger().logOnError(getContext(), getOperationContext(), + error, 0 /* vendorCode */, getTargetUserId()); try { getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java index 9d348e118b46..0e89814c6ad2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java @@ -21,8 +21,6 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricOverlayConstants; import android.hardware.biometrics.common.ICancellationSignal; -import android.hardware.biometrics.common.OperationContext; -import android.hardware.biometrics.common.OperationReason; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.os.RemoteException; @@ -99,13 +97,7 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements final AidlSession session = getFreshDaemon(); if (session.hasContextMethods()) { - final OperationContext context = new OperationContext(); - // TODO: add reason, id - context.id = 0; - context.reason = OperationReason.UNKNOWN; - context.isAoD = getBiometricContext().isAoD(); - context.isCrypto = isCryptoOperation(); - return session.getSession().detectInteractionWithContext(context); + return session.getSession().detectInteractionWithContext(getOperationContext()); } else { return session.getSession().detectInteraction(); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index ed16a6dd3b5c..e21d901b135d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -24,7 +24,6 @@ import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.common.OperationContext; -import android.hardware.biometrics.common.OperationReason; import android.hardware.biometrics.fingerprint.PointerContext; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; @@ -39,6 +38,8 @@ import android.util.Slog; import com.android.server.biometrics.HardwareAuthTokenUtils; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.log.CallbackWithProbe; +import com.android.server.biometrics.log.Probe; import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -58,6 +59,7 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps @NonNull private final FingerprintSensorPropertiesInternal mSensorProps; @NonNull private final SensorOverlays mSensorOverlays; + @NonNull private final CallbackWithProbe<Probe> mALSProbeCallback; private final @FingerprintManager.EnrollReason int mEnrollReason; @Nullable private ICancellationSignal mCancellationSignal; @@ -83,6 +85,8 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); mMaxTemplatesPerUser = maxTemplatesPerUser; + mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */); + mEnrollReason = enrollReason; if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) { getLogger().disableMetrics(); @@ -92,8 +96,8 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps @NonNull @Override protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) { - return new ClientMonitorCompositeCallback( - getLogger().createALSCallback(true /* startWithClient */), callback); + return new ClientMonitorCompositeCallback(mALSProbeCallback, + getBiometricContextUnsubscriber(), callback); } @Override @@ -142,22 +146,6 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps } @Override - protected void stopHalOperation() { - mSensorOverlays.hide(getSensorId()); - - if (mCancellationSignal != null) { - try { - mCancellationSignal.cancel(); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when requesting cancel", e); - onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, - 0 /* vendorCode */); - mCallback.onClientFinished(this, false /* success */); - } - } - } - - @Override protected void startHalOperation() { mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason), this); @@ -178,22 +166,44 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken); if (session.hasContextMethods()) { - final OperationContext context = new OperationContext(); - // TODO: add reason, id - context.id = 0; - context.reason = OperationReason.UNKNOWN; - context.isAoD = getBiometricContext().isAoD(); - context.isCrypto = isCryptoOperation(); - return session.getSession().enrollWithContext(hat, context); + final OperationContext opContext = getOperationContext(); + final ICancellationSignal cancel = session.getSession().enrollWithContext( + hat, opContext); + getBiometricContext().subscribe(opContext, ctx -> { + try { + session.getSession().onContextChanged(ctx); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to notify context changed", e); + } + }); + return cancel; } else { return session.getSession().enroll(hat); } } @Override + protected void stopHalOperation() { + mSensorOverlays.hide(getSensorId()); + unsubscribeBiometricContext(); + + if (mCancellationSignal != null) { + try { + mCancellationSignal.cancel(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when requesting cancel", e); + onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */); + mCallback.onClientFinished(this, false /* success */); + } + } + } + + @Override public void onPointerDown(int x, int y, float minor, float major) { try { mIsPointerDown = true; + mALSProbeCallback.getProbe().enable(); final AidlSession session = getFreshDaemon(); if (session.hasContextMethods()) { @@ -203,7 +213,7 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps context.y = y; context.minor = minor; context.major = major; - context.isAoD = false; + context.isAoD = getBiometricContext().isAoD(); session.getSession().onPointerDownWithContext(context); } else { session.getSession().onPointerDown(0 /* pointerId */, x, y, minor, major); @@ -217,6 +227,7 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps public void onPointerUp() { try { mIsPointerDown = false; + mALSProbeCallback.getProbe().disable(); final AidlSession session = getFreshDaemon(); if (session.hasContextMethods()) { 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 221ff94141e2..f810bca9707d 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 @@ -101,7 +101,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull private final BiometricTaskStackListener mTaskStackListener; // for requests that do not use biometric prompt @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); - + @NonNull private final BiometricContext mBiometricContext; @Nullable private IFingerprint mDaemon; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; @Nullable private ISidefpsController mSidefpsController; @@ -144,7 +144,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull SensorProps[] props, @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull BiometricContext biometricContext) { mContext = context; mFingerprintStateCallback = fingerprintStateCallback; mHalInstanceName = halInstanceName; @@ -153,6 +154,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mLockoutResetDispatcher = lockoutResetDispatcher; mActivityTaskManager = ActivityTaskManager.getInstance(); mTaskStackListener = new BiometricTaskStackListener(); + mBiometricContext = biometricContext; final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context); @@ -184,7 +186,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi location.sensorRadius)) .collect(Collectors.toList())); final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler, - internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher); + internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher, + mBiometricContext); mSensors.put(sensorId, sensor); Slog.d(getTag(), "Added: " + internalProp); @@ -303,7 +306,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mContext.getOpPackageName(), sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), + mBiometricContext, mSensors.get(sensorId).getAuthenticatorIds()); scheduleForSensor(sensorId, client); }); @@ -313,7 +316,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mHandler.post(() -> { final InvalidationRequesterClient<Fingerprint> client = new InvalidationRequesterClient<>(mContext, userId, sensorId, - BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), + BiometricLogger.ofUnknown(mContext), + mBiometricContext, FingerprintUtils.getInstance(sensorId)); scheduleForSensor(sensorId, client); }); @@ -327,7 +331,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mContext.getOpPackageName(), sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), hardwareAuthToken, + mBiometricContext, hardwareAuthToken, mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher); scheduleForSensor(sensorId, client); }); @@ -343,7 +347,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance()); + mBiometricContext); scheduleForSensor(sensorId, client); }); } @@ -358,7 +362,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi userId, opPackageName, sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), challenge); + mBiometricContext, challenge); scheduleForSensor(sensorId, client); }); } @@ -378,7 +382,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi opPackageName, FingerprintUtils.getInstance(sensorId), sensorId, createLogger(BiometricsProtoEnums.ACTION_ENROLL, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), + mBiometricContext, mSensors.get(sensorId).getSensorProperties(), mUdfpsOverlayController, mSidefpsController, maxTemplatesPerUser, enrollReason); scheduleForSensor(sensorId, client, new ClientMonitorCallback() { @@ -419,7 +423,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mSensors.get(sensorId).getLazySession(), token, id, callback, userId, opPackageName, sensorId, createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), - BiometricContext.getInstance(), + mBiometricContext, mUdfpsOverlayController, isStrongBiometric); scheduleForSensor(sensorId, client, mFingerprintStateCallback); }); @@ -439,7 +443,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi userId, operationId, restricted, opPackageName, cookie, false /* requireConfirmation */, sensorId, createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), - BiometricContext.getInstance(), isStrongBiometric, + mBiometricContext, isStrongBiometric, mTaskStackListener, mSensors.get(sensorId).getLockoutCache(), mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication, mSensors.get(sensorId).getSensorProperties()); @@ -503,7 +507,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi opPackageName, FingerprintUtils.getInstance(sensorId), sensorId, createLogger(BiometricsProtoEnums.ACTION_REMOVE, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), + mBiometricContext, mSensors.get(sensorId).getAuthenticatorIds()); scheduleForSensor(sensorId, client, mFingerprintStateCallback); }); @@ -520,7 +524,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mContext.getOpPackageName(), sensorId, createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), + mBiometricContext, enrolledList, FingerprintUtils.getInstance(sensorId), mSensors.get(sensorId).getAuthenticatorIds()); scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(callback, @@ -559,7 +563,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mSensors.get(sensorId).getLazySession(), userId, sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), + mBiometricContext, mSensors.get(sensorId).getAuthenticatorIds(), callback); scheduleForSensor(sensorId, client); }); 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 27226b30f548..63e345e40ad7 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 @@ -429,7 +429,8 @@ public class Sensor { Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context, @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull BiometricContext biometricContext) { mTag = tag; mProvider = provider; mContext = context; @@ -447,7 +448,7 @@ public class Sensor { public StopUserClient<?> getStopUserClient(int userId) { return new FingerprintStopUserClient(mContext, mLazySession, mToken, userId, mSensorProperties.sensorId, - BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), + BiometricLogger.ofUnknown(mContext), biometricContext, () -> mCurrentSession = null); } @@ -484,7 +485,7 @@ public class Sensor { return new FingerprintStartUserClient(mContext, provider::getHalInstance, mToken, newUserId, mSensorProperties.sensorId, - BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), + BiometricLogger.ofUnknown(mContext), biometricContext, resultController, userStartedCallback); } }); 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 6c35c8c91448..9d60859a4a21 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 @@ -120,6 +120,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull private final HalResultController mHalResultController; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; @Nullable private ISidefpsController mSidefpsController; + @NonNull private final BiometricContext mBiometricContext; // for requests that do not use biometric prompt @NonNull private final AtomicLong mRequestCounter = new AtomicLong(0); private int mCurrentUserId = UserHandle.USER_NULL; @@ -320,15 +321,18 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } } + @VisibleForTesting Fingerprint21(@NonNull Context context, @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull BiometricScheduler scheduler, @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull HalResultController controller) { + @NonNull HalResultController controller, + @NonNull BiometricContext biometricContext) { mContext = context; mFingerprintStateCallback = fingerprintStateCallback; + mBiometricContext = biometricContext; mSensorProperties = sensorProps; mSensorId = sensorProps.sensorId; @@ -370,7 +374,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final HalResultController controller = new HalResultController(sensorProps.sensorId, context, handler, scheduler); return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler, - lockoutResetDispatcher, controller); + lockoutResetDispatcher, controller, BiometricContext.getInstance(context)); } @Override @@ -497,7 +501,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mContext.getOpPackageName(), mSensorProperties.sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), + mBiometricContext, this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override @@ -544,7 +548,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider userId, mContext.getOpPackageName(), sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), mLockoutTracker); + mBiometricContext, mLockoutTracker); mScheduler.scheduleClientMonitor(client); }); } @@ -559,7 +563,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mSensorProperties.sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance()); + mBiometricContext); mScheduler.scheduleClientMonitor(client); }); } @@ -573,7 +577,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mSensorProperties.sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance()); + mBiometricContext); mScheduler.scheduleClientMonitor(client); }); } @@ -594,7 +598,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mSensorProperties.sensorId, createLogger(BiometricsProtoEnums.ACTION_ENROLL, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), + mBiometricContext, mUdfpsOverlayController, mSidefpsController, enrollReason); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @@ -636,7 +640,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mLazyDaemon, token, id, listener, userId, opPackageName, mSensorProperties.sensorId, createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), - BiometricContext.getInstance(), mUdfpsOverlayController, isStrongBiometric); + mBiometricContext, mUdfpsOverlayController, + isStrongBiometric); mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback); }); @@ -657,7 +662,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider restricted, opPackageName, cookie, false /* requireConfirmation */, mSensorProperties.sensorId, createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), - BiometricContext.getInstance(), isStrongBiometric, + mBiometricContext, isStrongBiometric, mTaskStackListener, mLockoutTracker, mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication, mSensorProperties); @@ -702,7 +707,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mSensorProperties.sensorId, createLogger(BiometricsProtoEnums.ACTION_REMOVE, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), mAuthenticatorIds); + mBiometricContext, mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback); }); } @@ -722,7 +727,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mSensorProperties.sensorId, createLogger(BiometricsProtoEnums.ACTION_REMOVE, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), mAuthenticatorIds); + mBiometricContext, mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback); }); } @@ -739,7 +744,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mSensorProperties.sensorId, createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN), - BiometricContext.getInstance(), enrolledList, + mBiometricContext, enrolledList, FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, callback); }); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index 1694bd92c73c..149526f21fdb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -37,6 +37,7 @@ import android.util.Slog; import android.util.SparseBooleanArray; import com.android.internal.R; +import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; @@ -247,7 +248,8 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull BiometricContext biometricContext) { Slog.d(TAG, "Creating Fingerprint23Mock!"); final Handler handler = new Handler(Looper.getMainLooper()); @@ -256,7 +258,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage final MockHalResultController controller = new MockHalResultController(sensorProps.sensorId, context, handler, scheduler); return new Fingerprint21UdfpsMock(context, fingerprintStateCallback, sensorProps, scheduler, - handler, lockoutResetDispatcher, controller); + handler, lockoutResetDispatcher, controller, biometricContext); } private static abstract class FakeFingerRunnable implements Runnable { @@ -385,9 +387,10 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage @NonNull TestableBiometricScheduler scheduler, @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull MockHalResultController controller) { + @NonNull MockHalResultController controller, + @NonNull BiometricContext biometricContext) { super(context, fingerprintStateCallback, sensorProps, scheduler, handler, - lockoutResetDispatcher, controller); + lockoutResetDispatcher, controller, biometricContext); mScheduler = scheduler; mScheduler.init(this); mHandler = handler; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java index f10d4e409b0e..c2929d0f15b2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java @@ -130,8 +130,9 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint> @Override public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> hardwareAuthToken) { - getLogger().logOnAuthenticated(getContext(), authenticated, false /* requireConfirmation */, - isCryptoOperation(), getTargetUserId(), false /* isBiometricPrompt */); + getLogger().logOnAuthenticated(getContext(), getOperationContext(), + authenticated, false /* requireConfirmation */, + getTargetUserId(), false /* isBiometricPrompt */); // Do not distinguish between success/failures. vibrateSuccess(); diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 1e00ea9161a8..1b0341c1ce26 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -308,7 +308,8 @@ public class CameraServiceProxy extends SystemService public void onFixedRotationFinished(int displayId) { } @Override - public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearArea) { } + public void onKeepClearAreasChanged(int displayId, List<Rect> restricted, + List<Rect> unrestricted) { } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 1f448549c59c..3494342a66fb 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1014,6 +1014,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode(); } } + if (mDisplayWhiteBalanceController != null) { + mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle); + } } private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() { diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java index 151ec8183269..fb36dc792a67 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java @@ -44,26 +44,18 @@ public class DisplayWhiteBalanceController implements AmbientSensor.AmbientBrightnessSensor.Callbacks, AmbientSensor.AmbientColorTemperatureSensor.Callbacks { - protected static final String TAG = "DisplayWhiteBalanceController"; - protected boolean mLoggingEnabled; + private static final String TAG = "DisplayWhiteBalanceController"; + private boolean mLoggingEnabled; - private boolean mEnabled; - - // To decouple the DisplayPowerController from the DisplayWhiteBalanceController, the DPC - // implements Callbacks and passes itself to the DWBC so it can call back into it without - // knowing about it. - private Callbacks mCallbacks; - - private AmbientSensor.AmbientBrightnessSensor mBrightnessSensor; + private final ColorDisplayServiceInternal mColorDisplayServiceInternal; + private final AmbientSensor.AmbientBrightnessSensor mBrightnessSensor; @VisibleForTesting AmbientFilter mBrightnessFilter; - private AmbientSensor.AmbientColorTemperatureSensor mColorTemperatureSensor; - + private final AmbientSensor.AmbientColorTemperatureSensor mColorTemperatureSensor; @VisibleForTesting AmbientFilter mColorTemperatureFilter; - private DisplayWhiteBalanceThrottler mThrottler; - + private final DisplayWhiteBalanceThrottler mThrottler; // In low brightness conditions the ALS readings are more noisy and produce // high errors. This default is introduced to provide a fixed display color // temperature when sensor readings become unreliable. @@ -74,16 +66,12 @@ public class DisplayWhiteBalanceController implements private final float mHighLightAmbientColorTemperature; private float mAmbientColorTemperature; - @VisibleForTesting float mPendingAmbientColorTemperature; private float mLastAmbientColorTemperature; - private ColorDisplayServiceInternal mColorDisplayServiceInternal; - // The most recent ambient color temperature values are kept for debugging purposes. - private static final int HISTORY_SIZE = 50; - private History mAmbientColorTemperatureHistory; + private final History mAmbientColorTemperatureHistory; // Override the ambient color temperature for debugging purposes. private float mAmbientColorTemperatureOverride; @@ -91,6 +79,10 @@ public class DisplayWhiteBalanceController implements // A piecewise linear relationship between ambient and display color temperatures. private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline; + // A piecewise linear relationship between ambient and display color temperatures, with a + // stronger change between the two sets of values. + private Spline.LinearSpline mStrongAmbientToDisplayColorTemperatureSpline; + // In very low or very high brightness conditions Display White Balance should // be to set to a default instead of using mAmbientToDisplayColorTemperatureSpline. // However, setting Display White Balance based on thresholds can cause the @@ -109,6 +101,17 @@ public class DisplayWhiteBalanceController implements private float mLatestLowLightBias; private float mLatestHighLightBias; + private boolean mEnabled; + + // Whether a higher-strength adjustment should be applied; this must be enabled in addition to + // mEnabled in order to be applied. + private boolean mStrongModeEnabled; + + // To decouple the DisplayPowerController from the DisplayWhiteBalanceController, the DPC + // implements Callbacks and passes itself to the DWBC so it can call back into it without + // knowing about it. + private Callbacks mDisplayPowerControllerCallbacks; + /** * @param brightnessSensor * The sensor used to detect changes in the ambient brightness. @@ -159,16 +162,18 @@ public class DisplayWhiteBalanceController implements @NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor, @NonNull AmbientFilter colorTemperatureFilter, @NonNull DisplayWhiteBalanceThrottler throttler, - float[] lowLightAmbientBrightnesses, float[] lowLightAmbientBiases, + float[] lowLightAmbientBrightnesses, + float[] lowLightAmbientBiases, float lowLightAmbientColorTemperature, - float[] highLightAmbientBrightnesses, float[] highLightAmbientBiases, + float[] highLightAmbientBrightnesses, + float[] highLightAmbientBiases, float highLightAmbientColorTemperature, - float[] ambientColorTemperatures, float[] displayColorTemperatures) { + float[] ambientColorTemperatures, + float[] displayColorTemperatures, + float[] strongAmbientColorTemperatures, + float[] strongDisplayColorTemperatures) { validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, throttler); - mLoggingEnabled = false; - mEnabled = false; - mCallbacks = null; mBrightnessSensor = brightnessSensor; mBrightnessFilter = brightnessFilter; mColorTemperatureSensor = colorTemperatureSensor; @@ -179,7 +184,7 @@ public class DisplayWhiteBalanceController implements mAmbientColorTemperature = -1.0f; mPendingAmbientColorTemperature = -1.0f; mLastAmbientColorTemperature = -1.0f; - mAmbientColorTemperatureHistory = new History(HISTORY_SIZE); + mAmbientColorTemperatureHistory = new History(/* size= */ 50); mAmbientColorTemperatureOverride = -1.0f; try { @@ -235,6 +240,13 @@ public class DisplayWhiteBalanceController implements mAmbientToDisplayColorTemperatureSpline = null; } + try { + mStrongAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline( + strongAmbientColorTemperatures, strongDisplayColorTemperatures); + } catch (Exception e) { + Slog.e(TAG, "Failed to create strong ambient to display color temperature spline", e); + } + mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class); } @@ -255,6 +267,19 @@ public class DisplayWhiteBalanceController implements } /** + * Enable/disable the stronger adjustment option. + * + * @param enabled whether the stronger adjustment option should be turned on + */ + public void setStrongModeEnabled(boolean enabled) { + mStrongModeEnabled = enabled; + if (mEnabled) { + updateAmbientColorTemperature(); + updateDisplayColorTemperature(); + } + } + + /** * Set an object to call back to when the display color temperature should be updated. * * @param callbacks @@ -263,10 +288,10 @@ public class DisplayWhiteBalanceController implements * @return Whether the method succeeded or not. */ public boolean setCallbacks(Callbacks callbacks) { - if (mCallbacks == callbacks) { + if (mDisplayPowerControllerCallbacks == callbacks) { return false; } - mCallbacks = callbacks; + mDisplayPowerControllerCallbacks = callbacks; return true; } @@ -321,7 +346,7 @@ public class DisplayWhiteBalanceController implements writer.println("DisplayWhiteBalanceController"); writer.println(" mLoggingEnabled=" + mLoggingEnabled); writer.println(" mEnabled=" + mEnabled); - writer.println(" mCallbacks=" + mCallbacks); + writer.println(" mDisplayPowerControllerCallbacks=" + mDisplayPowerControllerCallbacks); mBrightnessSensor.dump(writer); mBrightnessFilter.dump(writer); mColorTemperatureSensor.dump(writer); @@ -336,6 +361,8 @@ public class DisplayWhiteBalanceController implements writer.println(" mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride); writer.println(" mAmbientToDisplayColorTemperatureSpline=" + mAmbientToDisplayColorTemperatureSpline); + writer.println(" mStrongAmbientToDisplayColorTemperatureSpline=" + + mStrongAmbientToDisplayColorTemperatureSpline); writer.println(" mLowLightAmbientBrightnessToBiasSpline=" + mLowLightAmbientBrightnessToBiasSpline); writer.println(" mHighLightAmbientBrightnessToBiasSpline=" @@ -364,9 +391,20 @@ public class DisplayWhiteBalanceController implements float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time); mLatestAmbientColorTemperature = ambientColorTemperature; - if (mAmbientToDisplayColorTemperatureSpline != null && ambientColorTemperature != -1.0f) { - ambientColorTemperature = - mAmbientToDisplayColorTemperatureSpline.interpolate(ambientColorTemperature); + if (mStrongModeEnabled) { + if (mStrongAmbientToDisplayColorTemperatureSpline != null + && ambientColorTemperature != -1.0f) { + ambientColorTemperature = + mStrongAmbientToDisplayColorTemperatureSpline.interpolate( + ambientColorTemperature); + } + } else { + if (mAmbientToDisplayColorTemperatureSpline != null + && ambientColorTemperature != -1.0f) { + ambientColorTemperature = + mAmbientToDisplayColorTemperatureSpline.interpolate( + ambientColorTemperature); + } } float ambientBrightness = mBrightnessFilter.getEstimate(time); @@ -409,8 +447,8 @@ public class DisplayWhiteBalanceController implements Slog.d(TAG, "pending ambient color temperature: " + ambientColorTemperature); } mPendingAmbientColorTemperature = ambientColorTemperature; - if (mCallbacks != null) { - mCallbacks.updateWhiteBalance(); + if (mDisplayPowerControllerCallbacks != null) { + mDisplayPowerControllerCallbacks.updateWhiteBalance(); } } diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java index a72b1ed8f3ec..07821b0a984e 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java @@ -87,15 +87,22 @@ public class DisplayWhiteBalanceFactory { .config_displayWhiteBalanceHighLightAmbientColorTemperature); final float[] ambientColorTemperatures = getFloatArray(resources, com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures); - final float[] displayColorTempeartures = getFloatArray(resources, + final float[] displayColorTemperatures = getFloatArray(resources, com.android.internal.R.array.config_displayWhiteBalanceDisplayColorTemperatures); + final float[] strongAmbientColorTemperatures = getFloatArray(resources, + com.android.internal.R.array + .config_displayWhiteBalanceStrongAmbientColorTemperatures); + final float[] strongDisplayColorTemperatures = getFloatArray(resources, + com.android.internal.R.array + .config_displayWhiteBalanceStrongDisplayColorTemperatures); final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController( brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, throttler, displayWhiteBalanceLowLightAmbientBrightnesses, displayWhiteBalanceLowLightAmbientBiases, lowLightAmbientColorTemperature, displayWhiteBalanceHighLightAmbientBrightnesses, displayWhiteBalanceHighLightAmbientBiases, highLightAmbientColorTemperature, - ambientColorTemperatures, displayColorTempeartures); + ambientColorTemperatures, displayColorTemperatures, strongAmbientColorTemperatures, + strongDisplayColorTemperatures); brightnessSensor.setCallbacks(controller); colorTemperatureSensor.setCallbacks(controller); return controller; diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 611b28850efe..f0a6af3c8834 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -416,13 +416,14 @@ public final class DreamManagerService extends SystemService { mCurrentDreamCanDoze = canDoze; mCurrentDreamUserId = userId; + if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) { + mUiEventLogger.log(DreamManagerEvent.DREAM_START); + } + PowerManager.WakeLock wakeLock = mPowerManager .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream"); mHandler.post(wakeLock.wrap(() -> { mAtmInternal.notifyDreamStateChanged(true); - if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) { - mUiEventLogger.log(DreamManagerEvent.DREAM_START); - } mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock, mDreamOverlayServiceName); })); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 8f051303bf03..2d2edfa51896 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE; import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.READ_CONTACTS; import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS; +import static android.Manifest.permission.SET_INITIAL_LOCK; import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_DETAIL; import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_MESSAGE; import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_TITLE; @@ -1650,9 +1651,13 @@ public class LockSettingsService extends ILockSettings.Stub { "This operation requires secure lock screen feature"); } if (!hasPermission(PERMISSION) && !hasPermission(SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS)) { - throw new SecurityException( - "setLockCredential requires SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS or " - + PERMISSION); + if (hasPermission(SET_INITIAL_LOCK) && savedCredential.isNone()) { + // SET_INITIAL_LOCK can only be used if credential is not set. + } else { + throw new SecurityException( + "setLockCredential requires SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS or " + + PERMISSION); + } } final long identity = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 303ab4669855..7f997df3b222 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -677,9 +677,9 @@ class MediaRouter2ServiceImpl { UserRecord userRecord = routerRecord.mUserRecord; userRecord.mRouterRecords.remove(routerRecord); routerRecord.mUserRecord.mHandler.sendMessage( - obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers, + obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers, routerRecord.mUserRecord.mHandler, - routerRecord.mPackageName, /* preferredFeatures=*/ null)); + routerRecord.mPackageName, null)); userRecord.mHandler.sendMessage( obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler, userRecord.mHandler)); @@ -694,10 +694,10 @@ class MediaRouter2ServiceImpl { } routerRecord.mDiscoveryPreference = discoveryRequest; routerRecord.mUserRecord.mHandler.sendMessage( - obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers, + obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers, routerRecord.mUserRecord.mHandler, routerRecord.mPackageName, - routerRecord.mDiscoveryPreference.getPreferredFeatures())); + routerRecord.mDiscoveryPreference)); routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler, routerRecord.mUserRecord.mHandler)); @@ -921,7 +921,7 @@ class MediaRouter2ServiceImpl { // TODO: UserRecord <-> routerRecord, why do they reference each other? // How about removing mUserRecord from routerRecord? routerRecord.mUserRecord.mHandler.sendMessage( - obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManager, + obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManager, routerRecord.mUserRecord.mHandler, routerRecord, manager)); } @@ -2118,19 +2118,19 @@ class MediaRouter2ServiceImpl { } } - private void notifyPreferredFeaturesChangedToManager(@NonNull RouterRecord routerRecord, + private void notifyDiscoveryPreferenceChangedToManager(@NonNull RouterRecord routerRecord, @NonNull IMediaRouter2Manager manager) { try { - manager.notifyPreferredFeaturesChanged(routerRecord.mPackageName, - routerRecord.mDiscoveryPreference.getPreferredFeatures()); + manager.notifyDiscoveryPreferenceChanged(routerRecord.mPackageName, + routerRecord.mDiscoveryPreference); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify preferred features changed." + " Manager probably died.", ex); } } - private void notifyPreferredFeaturesChangedToManagers(@NonNull String routerPackageName, - @Nullable List<String> preferredFeatures) { + private void notifyDiscoveryPreferenceChangedToManagers(@NonNull String routerPackageName, + @Nullable RouteDiscoveryPreference discoveryPreference) { MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { return; @@ -2143,7 +2143,8 @@ class MediaRouter2ServiceImpl { } for (IMediaRouter2Manager manager : managers) { try { - manager.notifyPreferredFeaturesChanged(routerPackageName, preferredFeatures); + manager.notifyDiscoveryPreferenceChanged(routerPackageName, + discoveryPreference); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify preferred features changed." + " Manager probably died.", ex); diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java index b186f610d498..29aad63a1f4b 100644 --- a/services/core/java/com/android/server/notification/ZenModeFiltering.java +++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java @@ -24,11 +24,14 @@ import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; import android.media.AudioAttributes; +import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings.Global; import android.service.notification.ZenModeConfig; import android.telecom.TelecomManager; +import android.telephony.PhoneNumberUtils; +import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.Slog; @@ -36,6 +39,8 @@ import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.util.NotificationMessagingUtil; import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; import java.util.Date; public class ZenModeFiltering { @@ -64,13 +69,22 @@ public class ZenModeFiltering { pw.print(prefix); pw.print("RepeatCallers.mThresholdMinutes="); pw.println(REPEAT_CALLERS.mThresholdMinutes); synchronized (REPEAT_CALLERS) { - if (!REPEAT_CALLERS.mCalls.isEmpty()) { - pw.print(prefix); pw.println("RepeatCallers.mCalls="); - for (int i = 0; i < REPEAT_CALLERS.mCalls.size(); i++) { + if (!REPEAT_CALLERS.mTelCalls.isEmpty()) { + pw.print(prefix); pw.println("RepeatCallers.mTelCalls="); + for (int i = 0; i < REPEAT_CALLERS.mTelCalls.size(); i++) { pw.print(prefix); pw.print(" "); - pw.print(REPEAT_CALLERS.mCalls.keyAt(i)); + pw.print(REPEAT_CALLERS.mTelCalls.keyAt(i)); pw.print(" at "); - pw.println(ts(REPEAT_CALLERS.mCalls.valueAt(i))); + pw.println(ts(REPEAT_CALLERS.mTelCalls.valueAt(i))); + } + } + if (!REPEAT_CALLERS.mOtherCalls.isEmpty()) { + pw.print(prefix); pw.println("RepeatCallers.mOtherCalls="); + for (int i = 0; i < REPEAT_CALLERS.mOtherCalls.size(); i++) { + pw.print(prefix); pw.print(" "); + pw.print(REPEAT_CALLERS.mOtherCalls.keyAt(i)); + pw.print(" at "); + pw.println(ts(REPEAT_CALLERS.mOtherCalls.valueAt(i))); } } } @@ -330,34 +344,39 @@ public class ZenModeFiltering { } private static class RepeatCallers { - // Person : time - private final ArrayMap<String, Long> mCalls = new ArrayMap<>(); + // We keep a separate map per uri scheme to do more generous number-matching + // handling on telephone numbers specifically. For other inputs, we + // simply match directly on the string. + private final ArrayMap<String, Long> mTelCalls = new ArrayMap<>(); + private final ArrayMap<String, Long> mOtherCalls = new ArrayMap<>(); private int mThresholdMinutes; private synchronized void recordCall(Context context, Bundle extras) { setThresholdMinutes(context); if (mThresholdMinutes <= 0 || extras == null) return; - final String peopleString = peopleString(extras); - if (peopleString == null) return; + final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras); + if (extraPeople == null || extraPeople.length == 0) return; final long now = System.currentTimeMillis(); - cleanUp(mCalls, now); - mCalls.put(peopleString, now); + cleanUp(mTelCalls, now); + cleanUp(mOtherCalls, now); + recordCallers(extraPeople, now); } private synchronized boolean isRepeat(Context context, Bundle extras) { setThresholdMinutes(context); if (mThresholdMinutes <= 0 || extras == null) return false; - final String peopleString = peopleString(extras); - if (peopleString == null) return false; + final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras); + if (extraPeople == null || extraPeople.length == 0) return false; final long now = System.currentTimeMillis(); - cleanUp(mCalls, now); - return mCalls.containsKey(peopleString); + cleanUp(mTelCalls, now); + cleanUp(mOtherCalls, now); + return checkCallers(context, extraPeople); } private synchronized void cleanUp(ArrayMap<String, Long> calls, long now) { final int N = calls.size(); for (int i = N - 1; i >= 0; i--) { - final long time = mCalls.valueAt(i); + final long time = calls.valueAt(i); if (time > now || (now - time) > mThresholdMinutes * 1000 * 60) { calls.removeAt(i); } @@ -367,10 +386,16 @@ public class ZenModeFiltering { // Clean up all calls that occurred after the given time. // Used only for tests, to clean up after testing. private synchronized void cleanUpCallsAfter(long timeThreshold) { - for (int i = mCalls.size() - 1; i >= 0; i--) { - final long time = mCalls.valueAt(i); + for (int i = mTelCalls.size() - 1; i >= 0; i--) { + final long time = mTelCalls.valueAt(i); if (time > timeThreshold) { - mCalls.removeAt(i); + mTelCalls.removeAt(i); + } + } + for (int j = mOtherCalls.size() - 1; j >= 0; j--) { + final long time = mOtherCalls.valueAt(j); + if (time > timeThreshold) { + mOtherCalls.removeAt(j); } } } @@ -382,21 +407,65 @@ public class ZenModeFiltering { } } - private static String peopleString(Bundle extras) { - final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras); - if (extraPeople == null || extraPeople.length == 0) return null; - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < extraPeople.length; i++) { - String extraPerson = extraPeople[i]; - if (extraPerson == null) continue; - extraPerson = extraPerson.trim(); - if (extraPerson.isEmpty()) continue; - if (sb.length() > 0) { - sb.append('|'); + private synchronized void recordCallers(String[] people, long now) { + for (int i = 0; i < people.length; i++) { + String person = people[i]; + if (person == null) continue; + final Uri uri = Uri.parse(person); + if ("tel".equals(uri.getScheme())) { + String tel = uri.getSchemeSpecificPart(); + // while ideally we should not need to do this, sometimes we have seen tel + // numbers given in a url-encoded format + try { + tel = URLDecoder.decode(tel, "UTF-8"); + } catch (UnsupportedEncodingException e) { + // ignore, keep the original tel string + Slog.w(TAG, "unsupported encoding in tel: uri input"); + } + mTelCalls.put(tel, now); + } else { + // for non-tel calls, store the entire string, uri-component and all + mOtherCalls.put(person, now); } - sb.append(extraPerson); } - return sb.length() == 0 ? null : sb.toString(); + } + + private synchronized boolean checkCallers(Context context, String[] people) { + // get the default country code for checking telephone numbers + final String defaultCountryCode = + context.getSystemService(TelephonyManager.class).getNetworkCountryIso(); + for (int i = 0; i < people.length; i++) { + String person = people[i]; + if (person == null) continue; + final Uri uri = Uri.parse(person); + if ("tel".equals(uri.getScheme())) { + String number = uri.getSchemeSpecificPart(); + if (mTelCalls.containsKey(number)) { + // check directly via map first + return true; + } else { + // see if a number that matches via areSameNumber exists + String numberToCheck = number; + try { + numberToCheck = URLDecoder.decode(number, "UTF-8"); + } catch (UnsupportedEncodingException e) { + // ignore, continue to use the original string + Slog.w(TAG, "unsupported encoding in tel: uri input"); + } + for (String prev : mTelCalls.keySet()) { + if (PhoneNumberUtils.areSamePhoneNumber( + numberToCheck, prev, defaultCountryCode)) { + return true; + } + } + } + } else { + if (mOtherCalls.containsKey(person)) { + return true; + } + } + } + return false; } } diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java index 06405ae32fcf..2fda02db63e5 100644 --- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java +++ b/services/core/java/com/android/server/pm/InitAppsHelper.java @@ -32,6 +32,7 @@ import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS; import static com.android.server.pm.PackageManagerService.TAG; import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_FRAMEWORK_RES_SPLITS; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.parsing.ApkLiteParseUtils; import android.os.Environment; @@ -61,14 +62,25 @@ import java.util.concurrent.ExecutorService; * further cleanup and eventually all the installation/scanning related logic will go to another * class. */ -final class InitAndSystemPackageHelper { +final class InitAppsHelper { private final PackageManagerService mPm; - private final List<ScanPartition> mDirsToScanAsSystem; private final int mScanFlags; private final int mSystemParseFlags; private final int mSystemScanFlags; private final InstallPackageHelper mInstallPackageHelper; + private final ApexManager mApexManager; + private final PackageParser2 mPackageParser; + private final ExecutorService mExecutorService; + /* Tracks how long system scan took */ + private long mSystemScanTime; + /* Track of the number of cached system apps */ + private int mCachedSystemApps; + /* Track of the number of system apps */ + private int mSystemPackagesCount; + private final boolean mIsDeviceUpgrading; + private final boolean mIsOnlyCoreApps; + private final List<ScanPartition> mSystemPartitions; /** * Tracks new system packages [received in an OTA] that we expect to @@ -76,21 +88,34 @@ final class InitAndSystemPackageHelper { * are package location. */ private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>(); + /* Tracks of any system packages that no longer exist that needs to be pruned. */ + private final List<String> mPossiblyDeletedUpdatedSystemApps = new ArrayList<>(); + // Tracks of stub packages that must either be replaced with full versions in the /data + // partition or be disabled. + private final List<String> mStubSystemApps = new ArrayList<>(); // TODO(b/198166813): remove PMS dependency - InitAndSystemPackageHelper(PackageManagerService pm) { + InitAppsHelper(PackageManagerService pm, ApexManager apexManager, + InstallPackageHelper installPackageHelper, PackageParser2 packageParser, + List<ScanPartition> systemPartitions) { mPm = pm; - mInstallPackageHelper = new InstallPackageHelper(pm); + mApexManager = apexManager; + mInstallPackageHelper = installPackageHelper; + mPackageParser = packageParser; + mSystemPartitions = systemPartitions; mDirsToScanAsSystem = getSystemScanPartitions(); + mIsDeviceUpgrading = mPm.isDeviceUpgrading(); + mIsOnlyCoreApps = mPm.isOnlyCoreApps(); // Set flag to monitor and not change apk file paths when scanning install directories. int scanFlags = SCAN_BOOTING | SCAN_INITIAL; - if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()) { + if (mIsDeviceUpgrading || mPm.isFirstBoot()) { mScanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE; } else { mScanFlags = scanFlags; } mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR; mSystemScanFlags = scanFlags | SCAN_AS_SYSTEM; + mExecutorService = ParallelPackageParser.makeExecutorService(); } private List<File> getFrameworkResApkSplitFiles() { @@ -118,7 +143,7 @@ final class InitAndSystemPackageHelper { private List<ScanPartition> getSystemScanPartitions() { final List<ScanPartition> scanPartitions = new ArrayList<>(); - scanPartitions.addAll(mPm.mInjector.getSystemPartitions()); + scanPartitions.addAll(mSystemPartitions); scanPartitions.addAll(getApexScanPartitions()); Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions); return scanPartitions; @@ -126,8 +151,7 @@ final class InitAndSystemPackageHelper { private List<ScanPartition> getApexScanPartitions() { final List<ScanPartition> scanPartitions = new ArrayList<>(); - final List<ApexManager.ActiveApexInfo> activeApexInfos = - mPm.mApexManager.getActiveApexInfos(); + final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos(); for (int i = 0; i < activeApexInfos.size(); i++) { final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i)); if (scanPartition != null) { @@ -144,117 +168,133 @@ final class InitAndSystemPackageHelper { if (apexInfo.preInstalledApexPath.getAbsolutePath().equals( sp.getFolder().getAbsolutePath()) || apexInfo.preInstalledApexPath.getAbsolutePath().startsWith( - sp.getFolder().getAbsolutePath() + File.separator)) { + sp.getFolder().getAbsolutePath() + File.separator)) { return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX); } } return null; } - public OverlayConfig initPackages( - WatchedArrayMap<String, PackageSetting> packageSettings, int[] userIds, - long startTime) { - PackageParser2 packageParser = mPm.mInjector.getScanningCachingPackageParser(); - - ExecutorService executorService = ParallelPackageParser.makeExecutorService(); + /** + * Install apps from system dirs. + */ + @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) + public OverlayConfig initSystemApps(WatchedArrayMap<String, PackageSetting> packageSettings, + int[] userIds, long startTime) { // Prepare apex package info before scanning APKs, this information is needed when // scanning apk in apex. - mPm.mApexManager.scanApexPackagesTraced(packageParser, executorService); + mApexManager.scanApexPackagesTraced(mPackageParser, mExecutorService); - scanSystemDirs(packageParser, executorService); + scanSystemDirs(mPackageParser, mExecutorService); // Parse overlay configuration files to set default enable state, mutability, and // priority of system overlays. final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>(); - for (ApexManager.ActiveApexInfo apexInfo : mPm.mApexManager.getActiveApexInfos()) { - for (String packageName : mPm.mApexManager.getApksInApex(apexInfo.apexModuleName)) { + for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) { + for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) { apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath); } } - OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance( + final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance( consumer -> mPm.forEachPackage( pkg -> consumer.accept(pkg, pkg.isSystem(), - apkInApexPreInstalledPaths.get(pkg.getPackageName())))); - // Prune any system packages that no longer exist. - final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>(); - // Stub packages must either be replaced with full versions in the /data - // partition or be disabled. - final List<String> stubSystemApps = new ArrayList<>(); - - if (!mPm.isOnlyCoreApps()) { + apkInApexPreInstalledPaths.get(pkg.getPackageName())))); + + if (!mIsOnlyCoreApps) { // do this first before mucking with mPackages for the "expecting better" case - updateStubSystemAppsList(stubSystemApps); + updateStubSystemAppsList(mStubSystemApps); mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings, - possiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds); + mPossiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds); } - final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get(); + logSystemAppsScanningTime(startTime); + return overlayConfig; + } + + @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) + private void logSystemAppsScanningTime(long startTime) { + mCachedSystemApps = PackageCacher.sCachedPackageReadCount.get(); // Remove any shared userIDs that have no associated packages mPm.mSettings.pruneSharedUsersLPw(); - final long systemScanTime = SystemClock.uptimeMillis() - startTime; - final int systemPackagesCount = mPm.mPackages.size(); - Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime - + " ms, packageCount: " + systemPackagesCount + mSystemScanTime = SystemClock.uptimeMillis() - startTime; + mSystemPackagesCount = mPm.mPackages.size(); + Slog.i(TAG, "Finished scanning system apps. Time: " + mSystemScanTime + + " ms, packageCount: " + mSystemPackagesCount + " , timePerPackage: " - + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount) - + " , cached: " + cachedSystemApps); - if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) { + + (mSystemPackagesCount == 0 ? 0 : mSystemScanTime / mSystemPackagesCount) + + " , cached: " + mCachedSystemApps); + if (mIsDeviceUpgrading && mSystemPackagesCount > 0) { //CHECKSTYLE:OFF IndentationCheck FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME, - systemScanTime / systemPackagesCount); + mSystemScanTime / mSystemPackagesCount); //CHECKSTYLE:ON IndentationCheck } + } - if (!mPm.isOnlyCoreApps()) { + /** + * Install apps/updates from data dir and fix system apps that are affected. + */ + @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) + public void initNonSystemApps(@NonNull int[] userIds, long startTime) { + if (!mIsOnlyCoreApps) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis()); scanDirTracedLI(mPm.getAppInstallDir(), /* frameworkSplits= */ null, 0, - mScanFlags | SCAN_REQUIRE_KNOWN, 0, - packageParser, executorService); - + mScanFlags | SCAN_REQUIRE_KNOWN, + mPackageParser, mExecutorService); } - List<Runnable> unfinishedTasks = executorService.shutdownNow(); + List<Runnable> unfinishedTasks = mExecutorService.shutdownNow(); if (!unfinishedTasks.isEmpty()) { throw new IllegalStateException("Not all tasks finished before calling close: " + unfinishedTasks); } - - if (!mPm.isOnlyCoreApps()) { - mInstallPackageHelper.cleanupDisabledPackageSettings(possiblyDeletedUpdatedSystemApps, - userIds, mScanFlags); - mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter, - stubSystemApps, mSystemScanFlags, mSystemParseFlags); - - // Uncompress and install any stubbed system applications. - // This must be done last to ensure all stubs are replaced or disabled. - mInstallPackageHelper.installSystemStubPackages(stubSystemApps, mScanFlags); - - final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get() - - cachedSystemApps; - - final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime; - final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount; - Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime - + " ms, packageCount: " + dataPackagesCount - + " , timePerPackage: " - + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount) - + " , cached: " + cachedNonSystemApps); - if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) { - //CHECKSTYLE:OFF IndentationCheck - FrameworkStatsLog.write( - FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, - BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME, - dataScanTime / dataPackagesCount); - //CHECKSTYLE:OFF IndentationCheck - } + if (!mIsOnlyCoreApps) { + fixSystemPackages(userIds); + logNonSystemAppScanningTime(startTime); } mExpectingBetter.clear(); - mPm.mSettings.pruneRenamedPackagesLPw(); - packageParser.close(); - return overlayConfig; + mPackageParser.close(); + } + + /** + * Clean up system packages now that some system package updates have been installed from + * the data dir. Also install system stub packages as the last step. + */ + @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) + private void fixSystemPackages(@NonNull int[] userIds) { + mInstallPackageHelper.cleanupDisabledPackageSettings(mPossiblyDeletedUpdatedSystemApps, + userIds, mScanFlags); + mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter, + mStubSystemApps, mSystemScanFlags, mSystemParseFlags); + + // Uncompress and install any stubbed system applications. + // This must be done last to ensure all stubs are replaced or disabled. + mInstallPackageHelper.installSystemStubPackages(mStubSystemApps, mScanFlags); + } + + @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) + private void logNonSystemAppScanningTime(long startTime) { + final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get() + - mCachedSystemApps; + + final long dataScanTime = SystemClock.uptimeMillis() - mSystemScanTime - startTime; + final int dataPackagesCount = mPm.mPackages.size() - mSystemPackagesCount; + Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime + + " ms, packageCount: " + dataPackagesCount + + " , timePerPackage: " + + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount) + + " , cached: " + cachedNonSystemApps); + if (mIsDeviceUpgrading && dataPackagesCount > 0) { + //CHECKSTYLE:OFF IndentationCheck + FrameworkStatsLog.write( + FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, + BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME, + dataScanTime / dataPackagesCount); + //CHECKSTYLE:OFF IndentationCheck + } } /** @@ -274,14 +314,14 @@ final class InitAndSystemPackageHelper { continue; } scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null, - mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0, + mSystemParseFlags, mSystemScanFlags | partition.scanFlag, packageParser, executorService); } List<File> frameworkSplits = getFrameworkResApkSplitFiles(); scanDirTracedLI(frameworkDir, frameworkSplits, mSystemParseFlags | PARSE_FRAMEWORK_RES_SPLITS, - mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0, + mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, packageParser, executorService); if (!mPm.mPackages.containsKey("android")) { throw new IllegalStateException( @@ -293,11 +333,11 @@ final class InitAndSystemPackageHelper { if (partition.getPrivAppFolder() != null) { scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null, mSystemParseFlags, - mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0, + mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, packageParser, executorService); } scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null, - mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0, + mSystemParseFlags, mSystemScanFlags | partition.scanFlag, packageParser, executorService); } } @@ -316,11 +356,11 @@ final class InitAndSystemPackageHelper { @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) private void scanDirTracedLI(File scanDir, List<File> frameworkSplits, final int parseFlags, int scanFlags, - long currentTime, PackageParser2 packageParser, ExecutorService executorService) { + PackageParser2 packageParser, ExecutorService executorService) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]"); try { mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags, - scanFlags, currentTime, packageParser, executorService); + scanFlags, packageParser, executorService); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 50c26f4439a0..83c84728cb5a 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -141,7 +141,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.F2fsUtils; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import com.android.internal.security.VerityUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; @@ -2358,26 +2358,26 @@ final class InstallPackageHelper { if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { // Check for updated system application. if (installedPkg.isSystem()) { - return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL; } else { // If current upgrade specifies particular preference if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { // Application explicitly specified internal. - return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL; } else if ( installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { // App explicitly prefers external. Let policy decide } else { // Prefer previous location if (installedPkg.isExternalStorage()) { - return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; + return InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL; } - return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + return InstallLocationUtils.RECOMMEND_INSTALL_INTERNAL; } } } else { // Invalid install. Return error code - return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS; + return InstallLocationUtils.RECOMMEND_FAILED_ALREADY_EXISTS; } } } @@ -3030,7 +3030,7 @@ final class InstallPackageHelper { final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm); removePackageHelper.removePackageLI(stubPkg, true /*chatty*/); try { - return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null); + return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, null); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(), e); @@ -3163,7 +3163,7 @@ final class InstallPackageHelper { | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR; @PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath); final AndroidPackage pkg = scanSystemPackageTracedLI( - codePath, parseFlags, scanFlags, 0 /*currentTime*/, null); + codePath, parseFlags, scanFlags, null); PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName()); @@ -3338,7 +3338,7 @@ final class InstallPackageHelper { mRemovePackageHelper.removePackageLI(pkg, true); try { final File codePath = new File(pkg.getPath()); - scanSystemPackageTracedLI(codePath, 0, scanFlags, 0, null); + scanSystemPackageTracedLI(codePath, 0, scanFlags, null); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse updated, ex-system package: " + e.getMessage()); @@ -3359,7 +3359,7 @@ final class InstallPackageHelper { @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags, - int scanFlags, long currentTime, PackageParser2 packageParser, + int scanFlags, PackageParser2 packageParser, ExecutorService executorService) { final File[] files = scanDir.listFiles(); if (ArrayUtils.isEmpty(files)) { @@ -3402,7 +3402,7 @@ final class InstallPackageHelper { parseResult.parsedPackage); } try { - addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, currentTime, + addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, null); } catch (PackageManagerException e) { errorCode = e.error; @@ -3465,7 +3465,7 @@ final class InstallPackageHelper { try { final AndroidPackage newPkg = scanSystemPackageTracedLI( - scanFile, reparseFlags, rescanFlags, 0, null); + scanFile, reparseFlags, rescanFlags, null); // We rescanned a stub, add it to the list of stubbed system packages if (newPkg.isStub()) { stubSystemApps.add(packageName); @@ -3479,14 +3479,14 @@ final class InstallPackageHelper { /** * Traces a package scan. - * @see #scanSystemPackageLI(File, int, int, long, UserHandle) + * @see #scanSystemPackageLI(File, int, int, UserHandle) */ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) public AndroidPackage scanSystemPackageTracedLI(File scanFile, final int parseFlags, - int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { + int scanFlags, UserHandle user) throws PackageManagerException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]"); try { - return scanSystemPackageLI(scanFile, parseFlags, scanFlags, currentTime, user); + return scanSystemPackageLI(scanFile, parseFlags, scanFlags, user); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -3498,7 +3498,7 @@ final class InstallPackageHelper { */ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags, - long currentTime, UserHandle user) throws PackageManagerException { + UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); @@ -3514,7 +3514,7 @@ final class InstallPackageHelper { PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage); } - return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user); + return addForInitLI(parsedPackage, parseFlags, scanFlags, user); } /** @@ -3533,11 +3533,11 @@ final class InstallPackageHelper { @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) private AndroidPackage addForInitLI(ParsedPackage parsedPackage, @ParsingPackageUtils.ParseFlags int parseFlags, - @PackageManagerService.ScanFlags int scanFlags, long currentTime, + @PackageManagerService.ScanFlags int scanFlags, @Nullable UserHandle user) throws PackageManagerException { final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI( - parsedPackage, parseFlags, scanFlags, currentTime, user); + parsedPackage, parseFlags, scanFlags, user); final ScanResult scanResult = scanResultPair.first; boolean shouldHideSystemApp = scanResultPair.second; if (scanResult.mSuccess) { @@ -3715,7 +3715,7 @@ final class InstallPackageHelper { private Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage, @ParsingPackageUtils.ParseFlags int parseFlags, - @PackageManagerService.ScanFlags int scanFlags, long currentTime, + @PackageManagerService.ScanFlags int scanFlags, @Nullable UserHandle user) throws PackageManagerException { final boolean scanSystemPartition = (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0; @@ -3902,7 +3902,7 @@ final class InstallPackageHelper { } final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, - scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null); + scanFlags | SCAN_UPDATE_SIGNATURE, 0 /* currentTime */, user, null); return new Pair<>(scanResult, shouldHideSystemApp); } diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java index d996fe46a4f6..7e845c74617b 100644 --- a/services/core/java/com/android/server/pm/InstallParams.java +++ b/services/core/java/com/android/server/pm/InstallParams.java @@ -35,18 +35,17 @@ import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.SigningDetails; import android.content.pm.parsing.PackageLite; -import android.os.Environment; import android.os.Message; import android.os.Trace; import android.os.UserHandle; -import android.os.storage.StorageManager; import android.util.ArrayMap; import android.util.Pair; import android.util.Slog; import com.android.internal.content.F2fsUtils; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import com.android.internal.util.Preconditions; +import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.File; import java.util.ArrayList; @@ -142,12 +141,8 @@ final class InstallParams extends HandlerParams { * Only {@link PackageManager#INSTALL_INTERNAL} flag may mutate in * {@link #mInstallFlags} */ - private int overrideInstallLocation(PackageInfoLite pkgLite) { - final boolean ephemeral = (mInstallFlags & PackageManager.INSTALL_INSTANT_APP) != 0; - if (DEBUG_INSTANT && ephemeral) { - Slog.v(TAG, "pkgLite for install: " + pkgLite); - } - + private int overrideInstallLocation(String packageName, int recommendedInstallLocation, + int installLocation) { if (mOriginInfo.mStaged) { // If we're already staged, we've firmly committed to an install location if (mOriginInfo.mFile != null) { @@ -155,77 +150,35 @@ final class InstallParams extends HandlerParams { } else { throw new IllegalStateException("Invalid stage location"); } - } else if (pkgLite.recommendedInstallLocation - == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { - /* - * If we are not staged and have too little free space, try to free cache - * before giving up. - */ - // TODO: focus freeing disk space on the target device - final StorageManager storage = StorageManager.from(mPm.mContext); - final long lowThreshold = storage.getStorageLowBytes( - Environment.getDataDirectory()); - - final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize( - mOriginInfo.mResolvedPath, mPackageAbiOverride); - if (sizeBytes >= 0) { - synchronized (mPm.mInstallLock) { - try { - mPm.mInstaller.freeCache(null, sizeBytes + lowThreshold, 0); - pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext, - mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, - mPackageAbiOverride); - } catch (Installer.InstallerException e) { - Slog.w(TAG, "Failed to free cache", e); - } - } - } - - /* - * The cache free must have deleted the file we downloaded to install. - * - * TODO: fix the "freeCache" call to not delete the file we care about. - */ - if (pkgLite.recommendedInstallLocation - == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { - pkgLite.recommendedInstallLocation = - PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; + } + if (recommendedInstallLocation < 0) { + return InstallLocationUtils.getInstallationErrorCode(recommendedInstallLocation); + } + // Override with defaults if needed. + synchronized (mPm.mLock) { + // reader + AndroidPackage installedPkg = mPm.mPackages.get(packageName); + if (installedPkg != null) { + // Currently installed package which the new package is attempting to replace + recommendedInstallLocation = InstallLocationUtils.installLocationPolicy( + installLocation, recommendedInstallLocation, mInstallFlags, + installedPkg.isSystem(), installedPkg.isExternalStorage()); } } - int ret = INSTALL_SUCCEEDED; - int loc = pkgLite.recommendedInstallLocation; - if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) { - ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; - } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) { - ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; - } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { - ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) { - ret = PackageManager.INSTALL_FAILED_INVALID_APK; - } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { - ret = PackageManager.INSTALL_FAILED_INVALID_URI; - } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) { - ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; - } else { - // Override with defaults if needed. - loc = mInstallPackageHelper.installLocationPolicy(pkgLite, mInstallFlags); - - final boolean onInt = (mInstallFlags & PackageManager.INSTALL_INTERNAL) != 0; - - if (!onInt) { - // Override install location with flags - if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { - // Set the flag to install on external media. - mInstallFlags &= ~PackageManager.INSTALL_INTERNAL; - } else { - // Make sure the flag for installing on external - // media is unset - mInstallFlags |= PackageManager.INSTALL_INTERNAL; - } + final boolean onInt = (mInstallFlags & PackageManager.INSTALL_INTERNAL) != 0; + + if (!onInt) { + // Override install location with flags + if (recommendedInstallLocation == InstallLocationUtils.RECOMMEND_INSTALL_EXTERNAL) { + // Set the flag to install on external media. + mInstallFlags &= ~PackageManager.INSTALL_INTERNAL; + } else { + // Make sure the flag for installing on external media is unset + mInstallFlags |= PackageManager.INSTALL_INTERNAL; } } - return ret; + return INSTALL_SUCCEEDED; } /* @@ -254,7 +207,21 @@ final class InstallParams extends HandlerParams { } } - mRet = overrideInstallLocation(pkgLite); + final boolean ephemeral = (mInstallFlags & PackageManager.INSTALL_INSTANT_APP) != 0; + if (DEBUG_INSTANT && ephemeral) { + Slog.v(TAG, "pkgLite for install: " + pkgLite); + } + + if (!mOriginInfo.mStaged && pkgLite.recommendedInstallLocation + == InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { + // If we are not staged and have too little free space, try to free cache + // before giving up. + pkgLite.recommendedInstallLocation = mPm.freeCacheForInstallation( + pkgLite.recommendedInstallLocation, mPackageLite, + mOriginInfo.mResolvedPath, mPackageAbiOverride, mInstallFlags); + } + mRet = overrideInstallLocation(pkgLite.packageName, pkgLite.recommendedInstallLocation, + pkgLite.installLocation); } @Override diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index ccc375ff85f2..8465248a6e46 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -84,7 +84,7 @@ import android.util.Xml; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.ImageUtils; @@ -782,7 +782,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements // If caller requested explicit location, validity check it, otherwise // resolve the best internal or adopted location. if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) { - if (!PackageHelper.fitsOnInternal(mContext, params)) { + if (!InstallLocationUtils.fitsOnInternal(mContext, params)) { throw new IOException("No suitable internal storage available"); } } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) { @@ -796,7 +796,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements // requested install flags, delta size, and manifest settings. final long ident = Binder.clearCallingIdentity(); try { - params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params); + params.volumeUuid = InstallLocationUtils.resolveInstallVolume(mContext, params); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 390dd3fb4fc8..7152783e3d64 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -143,8 +143,8 @@ import android.util.apk.ApkSignatureVerifier; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.content.InstallLocationUtils; import com.android.internal.content.NativeLibraryHelper; -import com.android.internal.content.PackageHelper; import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.os.SomeArgs; import com.android.internal.security.VerityUtils; @@ -1537,7 +1537,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (stageDir != null && lengthBytes > 0) { mContext.getSystemService(StorageManager.class).allocateBytes( targetPfd.getFileDescriptor(), lengthBytes, - PackageHelper.translateAllocateFlags(params.installFlags)); + InstallLocationUtils.translateAllocateFlags(params.installFlags)); } if (offsetBytes > 0) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 79cfa06859d0..9d95ead3a311 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -101,6 +101,7 @@ import android.content.pm.KeySet; import android.content.pm.ModuleInfo; import android.content.pm.PackageChangeEvent; import android.content.pm.PackageInfo; +import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageManager.ComponentEnabledSetting; @@ -129,6 +130,7 @@ import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VersionedPackage; import android.content.pm.dex.IArtManager; import android.content.pm.overlay.OverlayPaths; +import android.content.pm.parsing.PackageLite; import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Bitmap; @@ -189,7 +191,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; import com.android.internal.content.F2fsUtils; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import com.android.internal.content.om.OverlayConfig; import com.android.internal.telephony.CarrierAppUtils; import com.android.internal.util.ArrayUtils; @@ -942,7 +944,7 @@ public class PackageManagerService extends IPackageManager.Stub private final BroadcastHelper mBroadcastHelper; private final RemovePackageHelper mRemovePackageHelper; private final DeletePackageHelper mDeletePackageHelper; - private final InitAndSystemPackageHelper mInitAndSystemPackageHelper; + private final InitAppsHelper mInitAppsHelper; private final AppDataHelper mAppDataHelper; private final InstallPackageHelper mInstallPackageHelper; private final PreferredActivityHelper mPreferredActivityHelper; @@ -1687,7 +1689,7 @@ public class PackageManagerService extends IPackageManager.Stub mAppDataHelper = testParams.appDataHelper; mInstallPackageHelper = testParams.installPackageHelper; mRemovePackageHelper = testParams.removePackageHelper; - mInitAndSystemPackageHelper = testParams.initAndSystemPackageHelper; + mInitAppsHelper = testParams.initAndSystemPackageHelper; mDeletePackageHelper = testParams.deletePackageHelper; mPreferredActivityHelper = testParams.preferredActivityHelper; mResolveIntentHelper = testParams.resolveIntentHelper; @@ -1834,7 +1836,8 @@ public class PackageManagerService extends IPackageManager.Stub mAppDataHelper = new AppDataHelper(this); mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper); mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper); - mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this); + mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper, + mInjector.getScanningCachingPackageParser(), mInjector.getSystemPartitions()); mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper, mAppDataHelper); mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper); @@ -1964,8 +1967,8 @@ public class PackageManagerService extends IPackageManager.Stub mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion); final int[] userIds = mUserManager.getUserIds(); - mOverlayConfig = mInitAndSystemPackageHelper.initPackages(packageSettings, - userIds, startTime); + mOverlayConfig = mInitAppsHelper.initSystemApps(packageSettings, userIds, startTime); + mInitAppsHelper.initNonSystemApps(userIds, startTime); // Resolve the storage manager. mStorageManagerPackage = getStorageManagerPackageName(); @@ -2901,6 +2904,36 @@ public class PackageManagerService extends IPackageManager.Stub throw new IOException("Failed to free " + bytes + " on storage device at " + file); } + int freeCacheForInstallation(int recommendedInstallLocation, PackageLite pkgLite, + String resolvedPath, String mPackageAbiOverride, int installFlags) { + // TODO: focus freeing disk space on the target device + final StorageManager storage = StorageManager.from(mContext); + final long lowThreshold = storage.getStorageLowBytes(Environment.getDataDirectory()); + + final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(resolvedPath, + mPackageAbiOverride); + if (sizeBytes >= 0) { + synchronized (mInstallLock) { + try { + mInstaller.freeCache(null, sizeBytes + lowThreshold, 0); + PackageInfoLite pkgInfoLite = PackageManagerServiceUtils.getMinimalPackageInfo( + mContext, pkgLite, resolvedPath, installFlags, + mPackageAbiOverride); + // The cache free must have deleted the file we downloaded to install. + if (pkgInfoLite.recommendedInstallLocation + == InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI) { + pkgInfoLite.recommendedInstallLocation = + InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; + } + return pkgInfoLite.recommendedInstallLocation; + } catch (Installer.InstallerException e) { + Slog.w(TAG, "Failed to free cache", e); + } + } + } + return recommendedInstallLocation; + } + /** * Update given flags when being used to request {@link PackageInfo}. */ @@ -3676,7 +3709,7 @@ public class PackageManagerService extends IPackageManager.Stub // Before everything else, see whether we need to fstrim. try { - IStorageManager sm = PackageHelper.getStorageManager(); + IStorageManager sm = InstallLocationUtils.getStorageManager(); if (sm != null) { boolean doTrim = false; final long interval = android.provider.Settings.Global.getLong( @@ -6595,8 +6628,9 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstallLocation() == loc) { return true; } - if (loc == PackageHelper.APP_INSTALL_AUTO || loc == PackageHelper.APP_INSTALL_INTERNAL - || loc == PackageHelper.APP_INSTALL_EXTERNAL) { + if (loc == InstallLocationUtils.APP_INSTALL_AUTO + || loc == InstallLocationUtils.APP_INSTALL_INTERNAL + || loc == InstallLocationUtils.APP_INSTALL_EXTERNAL) { android.provider.Settings.Global.putInt(mContext.getContentResolver(), android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION, loc); return true; @@ -6609,7 +6643,7 @@ public class PackageManagerService extends IPackageManager.Stub // allow instant app access return android.provider.Settings.Global.getInt(mContext.getContentResolver(), android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION, - PackageHelper.APP_INSTALL_AUTO); + InstallLocationUtils.APP_INSTALL_AUTO); } /** Called by UserManagerService */ @@ -8643,7 +8677,7 @@ public class PackageManagerService extends IPackageManager.Stub } boolean isExpectingBetter(String packageName) { - return mInitAndSystemPackageHelper.isExpectingBetter(packageName); + return mInitAppsHelper.isExpectingBetter(packageName); } int getDefParseFlags() { @@ -8746,13 +8780,12 @@ public class PackageManagerService extends IPackageManager.Stub } boolean isOverlayMutable(String packageName) { - return (mOverlayConfig != null ? mOverlayConfig - : OverlayConfig.getSystemInstance()).isMutable(packageName); + return mOverlayConfig.isMutable(packageName); } @ScanFlags int getSystemPackageScanFlags(File codePath) { List<ScanPartition> dirsToScanAsSystem = - mInitAndSystemPackageHelper.getDirsToScanAsSystem(); + mInitAppsHelper.getDirsToScanAsSystem(); @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM; for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) { ScanPartition partition = dirsToScanAsSystem.get(i); @@ -8770,7 +8803,7 @@ public class PackageManagerService extends IPackageManager.Stub Pair<Integer, Integer> getSystemPackageRescanFlagsAndReparseFlags(File scanFile, int systemScanFlags, int systemParseFlags) { List<ScanPartition> dirsToScanAsSystem = - mInitAndSystemPackageHelper.getDirsToScanAsSystem(); + mInitAppsHelper.getDirsToScanAsSystem(); @ParsingPackageUtils.ParseFlags int reparseFlags = 0; @PackageManagerService.ScanFlags int rescanFlags = 0; for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java index 1caa76d2435a..00ca4ae8b734 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java @@ -108,7 +108,7 @@ public final class PackageManagerServiceTestParams { public AppDataHelper appDataHelper; public InstallPackageHelper installPackageHelper; public RemovePackageHelper removePackageHelper; - public InitAndSystemPackageHelper initAndSystemPackageHelper; + public InitAppsHelper initAndSystemPackageHelper; public DeletePackageHelper deletePackageHelper; public PreferredActivityHelper preferredActivityHelper; public ResolveIntentHelper resolveIntentHelper; diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 19c31e0b1625..d6340b5bc811 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -43,6 +43,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.PackageInfoLite; +import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackagePartitions; import android.content.pm.ResolveInfo; @@ -79,8 +80,8 @@ import android.util.Printer; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.content.InstallLocationUtils; import com.android.internal.content.NativeLibraryHelper; -import com.android.internal.content.PackageHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.HexDump; @@ -805,27 +806,37 @@ public class PackageManagerServiceUtils { final PackageInfoLite ret = new PackageInfoLite(); if (packagePath == null || pkg == null) { Slog.i(TAG, "Invalid package file " + packagePath); - ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; + ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK; return ret; } final File packageFile = new File(packagePath); final long sizeBytes; try { - sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride); + sizeBytes = InstallLocationUtils.calculateInstalledSize(pkg, abiOverride); } catch (IOException e) { if (!packageFile.exists()) { - ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI; + ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI; } else { - ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; + ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK; } return ret; } - final int recommendedInstallLocation = PackageHelper.resolveInstallLocation(context, - pkg.getPackageName(), pkg.getInstallLocation(), sizeBytes, flags); - + final PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams( + PackageInstaller.SessionParams.MODE_INVALID); + sessionParams.appPackageName = pkg.getPackageName(); + sessionParams.installLocation = pkg.getInstallLocation(); + sessionParams.sizeBytes = sizeBytes; + sessionParams.installFlags = flags; + final int recommendedInstallLocation; + try { + recommendedInstallLocation = InstallLocationUtils.resolveInstallLocation(context, + sessionParams); + } catch (IOException e) { + throw new IllegalStateException(e); + } ret.packageName = pkg.getPackageName(); ret.splitNames = pkg.getSplitNames(); ret.versionCode = pkg.getVersionCode(); @@ -837,7 +848,6 @@ public class PackageManagerServiceUtils { ret.recommendedInstallLocation = recommendedInstallLocation; ret.multiArch = pkg.isMultiArch(); ret.debuggable = pkg.isDebuggable(); - return ret; } @@ -857,7 +867,7 @@ public class PackageManagerServiceUtils { throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(), result.getException()); } - return PackageHelper.calculateInstalledSize(result.getResult(), abiOverride); + return InstallLocationUtils.calculateInstalledSize(result.getResult(), abiOverride); } catch (PackageManagerException | IOException e) { Slog.w(TAG, "Failed to calculate installed size: " + e); return -1; diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 4b592062c888..d4fcd06a6548 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -98,7 +98,7 @@ import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -593,7 +593,7 @@ class PackageManagerShellCommand extends ShellCommand { null /* splitApkPaths */, null /* splitRevisionCodes */, apkLite.getTargetSdkVersion(), null /* requiredSplitTypes */, null /* splitTypes */); - sessionSize += PackageHelper.calculateInstalledSize(pkgLite, + sessionSize += InstallLocationUtils.calculateInstalledSize(pkgLite, params.sessionParams.abiOverride, fd.getFileDescriptor()); } catch (IOException e) { getErrPrintWriter().println("Error: Failed to parse APK file: " + inPath); @@ -1649,11 +1649,11 @@ class PackageManagerShellCommand extends ShellCommand { private int runGetInstallLocation() throws RemoteException { int loc = mInterface.getInstallLocation(); String locStr = "invalid"; - if (loc == PackageHelper.APP_INSTALL_AUTO) { + if (loc == InstallLocationUtils.APP_INSTALL_AUTO) { locStr = "auto"; - } else if (loc == PackageHelper.APP_INSTALL_INTERNAL) { + } else if (loc == InstallLocationUtils.APP_INSTALL_INTERNAL) { locStr = "internal"; - } else if (loc == PackageHelper.APP_INSTALL_EXTERNAL) { + } else if (loc == InstallLocationUtils.APP_INSTALL_EXTERNAL) { locStr = "external"; } getOutPrintWriter().println(loc + "[" + locStr + "]"); diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java index 9bfb7d19eee1..9db215e9093c 100644 --- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java +++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java @@ -28,7 +28,6 @@ import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.SigningDetails; -import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; import android.content.rollback.RollbackInfo; @@ -43,11 +42,12 @@ import android.util.Slog; import android.util.apk.ApkSignatureVerifier; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.pkg.ParsedPackage; +import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils; import com.android.server.rollback.RollbackManagerInternal; import java.io.File; @@ -291,8 +291,8 @@ final class PackageSessionVerifier { throws PackageManagerException { // Before marking the session as ready, start checkpoint service if available try { - if (PackageHelper.getStorageManager().supportsCheckpoint()) { - PackageHelper.getStorageManager().startCheckpoint(2); + if (InstallLocationUtils.getStorageManager().supportsCheckpoint()) { + InstallLocationUtils.getStorageManager().startCheckpoint(2); } } catch (Exception e) { // Failed to get hold of StorageManager @@ -544,7 +544,7 @@ final class PackageSessionVerifier { */ private void checkActiveSessions() throws PackageManagerException { try { - checkActiveSessions(PackageHelper.getStorageManager().supportsCheckpoint()); + checkActiveSessions(InstallLocationUtils.getStorageManager().supportsCheckpoint()); } catch (RemoteException e) { throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED, "Can't query fs-checkpoint status : " + e); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 29de5551cb27..f63f8f4289ed 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -53,7 +53,7 @@ import android.util.TimingsTraceLog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import com.android.internal.os.BackgroundThread; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; @@ -237,7 +237,7 @@ public class StagingManager { mApexManager.revertActiveSessions(); } - PackageHelper.getStorageManager().abortChanges( + InstallLocationUtils.getStorageManager().abortChanges( "abort-staged-install", false /*retry*/); } } catch (Exception e) { @@ -674,8 +674,8 @@ public class StagingManager { boolean needsCheckpoint = false; boolean supportsCheckpoint = false; try { - supportsCheckpoint = PackageHelper.getStorageManager().supportsCheckpoint(); - needsCheckpoint = PackageHelper.getStorageManager().needsCheckpoint(); + supportsCheckpoint = InstallLocationUtils.getStorageManager().supportsCheckpoint(); + needsCheckpoint = InstallLocationUtils.getStorageManager().needsCheckpoint(); } catch (RemoteException e) { // This means that vold has crashed, and device is in a bad state. throw new IllegalStateException("Failed to get checkpoint status", e); diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java index bb7e55a4bf40..137209850736 100644 --- a/services/core/java/com/android/server/pm/StorageEventHelper.java +++ b/services/core/java/com/android/server/pm/StorageEventHelper.java @@ -32,7 +32,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackagePartitions; import android.content.pm.UserInfo; import android.content.pm.VersionedPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import android.os.Environment; import android.os.FileUtils; import android.os.UserHandle; @@ -48,6 +47,7 @@ import android.util.Slog; import com.android.internal.policy.AttributeCache; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import java.io.File; import java.util.ArrayList; @@ -150,7 +150,7 @@ public final class StorageEventHelper extends StorageEventListener { final AndroidPackage pkg; try { pkg = installPackageHelper.scanSystemPackageTracedLI( - ps.getPath(), parseFlags, SCAN_INITIAL, 0, null); + ps.getPath(), parseFlags, SCAN_INITIAL, null); loaded.add(pkg); } catch (PackageManagerException e) { diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 73ec2cd66ac1..77d63100032c 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -33,6 +33,7 @@ import android.metrics.LogMaker; import android.net.Uri; import android.os.BatteryStats; import android.os.Handler; +import android.os.IWakeLockCallback; import android.os.Looper; import android.os.Message; import android.os.PowerManager; @@ -215,14 +216,15 @@ public class Notifier { * Called when a wake lock is acquired. */ public void onWakeLockAcquired(int flags, String tag, String packageName, - int ownerUid, int ownerPid, WorkSource workSource, String historyTag) { + int ownerUid, int ownerPid, WorkSource workSource, String historyTag, + IWakeLockCallback callback) { if (DEBUG) { Slog.d(TAG, "onWakeLockAcquired: flags=" + flags + ", tag=\"" + tag + "\", packageName=" + packageName + ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid + ", workSource=" + workSource); } - + notifyWakeLockListener(callback, true); final int monitorType = getBatteryStatsWakeLockMonitorType(flags); if (monitorType >= 0) { try { @@ -300,8 +302,9 @@ public class Notifier { */ public void onWakeLockChanging(int flags, String tag, String packageName, int ownerUid, int ownerPid, WorkSource workSource, String historyTag, - int newFlags, String newTag, String newPackageName, int newOwnerUid, - int newOwnerPid, WorkSource newWorkSource, String newHistoryTag) { + IWakeLockCallback callback, int newFlags, String newTag, String newPackageName, + int newOwnerUid, int newOwnerPid, WorkSource newWorkSource, String newHistoryTag, + IWakeLockCallback newCallback) { final int monitorType = getBatteryStatsWakeLockMonitorType(flags); final int newMonitorType = getBatteryStatsWakeLockMonitorType(newFlags); @@ -323,10 +326,16 @@ public class Notifier { } catch (RemoteException ex) { // Ignore } + } else if (!PowerManagerService.isSameCallback(callback, newCallback)) { + onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag, + null /* Do not notify the old callback */); + onWakeLockAcquired(newFlags, newTag, newPackageName, newOwnerUid, newOwnerPid, + newWorkSource, newHistoryTag, newCallback /* notify the new callback */); } else { - onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag); + onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag, + callback); onWakeLockAcquired(newFlags, newTag, newPackageName, newOwnerUid, newOwnerPid, - newWorkSource, newHistoryTag); + newWorkSource, newHistoryTag, newCallback); } } @@ -334,14 +343,15 @@ public class Notifier { * Called when a wake lock is released. */ public void onWakeLockReleased(int flags, String tag, String packageName, - int ownerUid, int ownerPid, WorkSource workSource, String historyTag) { + int ownerUid, int ownerPid, WorkSource workSource, String historyTag, + IWakeLockCallback callback) { if (DEBUG) { Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag + "\", packageName=" + packageName + ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid + ", workSource=" + workSource); } - + notifyWakeLockListener(callback, false); final int monitorType = getBatteryStatsWakeLockMonitorType(flags); if (monitorType >= 0) { try { @@ -859,6 +869,18 @@ public class Notifier { return enabled && dndOff; } + private void notifyWakeLockListener(IWakeLockCallback callback, boolean isEnabled) { + if (callback != null) { + mHandler.post(() -> { + try { + callback.onStateChanged(isEnabled); + } catch (RemoteException e) { + throw new IllegalArgumentException("Wakelock.mCallback is already dead.", e); + } + }); + } + } + private final class NotifierHandler extends Handler { public NotifierHandler(Looper looper) { diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index efcfbdd16e4e..38570727742d 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -66,6 +66,7 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; import android.os.IPowerManager; +import android.os.IWakeLockCallback; import android.os.Looper; import android.os.Message; import android.os.ParcelDuration; @@ -1436,7 +1437,8 @@ public final class PowerManagerService extends SystemService } private void acquireWakeLockInternal(IBinder lock, int displayId, int flags, String tag, - String packageName, WorkSource ws, String historyTag, int uid, int pid) { + String packageName, WorkSource ws, String historyTag, int uid, int pid, + @Nullable IWakeLockCallback callback) { synchronized (mLock) { if (displayId != Display.INVALID_DISPLAY) { final DisplayInfo displayInfo = @@ -1460,11 +1462,12 @@ public final class PowerManagerService extends SystemService boolean notifyAcquire; if (index >= 0) { wakeLock = mWakeLocks.get(index); - if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) { + if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid, callback)) { // Update existing wake lock. This shouldn't happen but is harmless. notifyWakeLockChangingLocked(wakeLock, flags, tag, packageName, - uid, pid, ws, historyTag); - wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid); + uid, pid, ws, historyTag, callback); + wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid, + callback); } notifyAcquire = false; } else { @@ -1476,12 +1479,7 @@ public final class PowerManagerService extends SystemService } state.mNumWakeLocks++; wakeLock = new WakeLock(lock, displayId, flags, tag, packageName, ws, historyTag, - uid, pid, state); - try { - lock.linkToDeath(wakeLock, 0); - } catch (RemoteException ex) { - throw new IllegalArgumentException("Wake lock is already dead."); - } + uid, pid, state, callback); mWakeLocks.add(wakeLock); setWakeLockDisabledStateLocked(wakeLock); notifyAcquire = true; @@ -1576,11 +1574,8 @@ public final class PowerManagerService extends SystemService mRequestWaitForNegativeProximity = true; } - try { - wakeLock.mLock.unlinkToDeath(wakeLock, 0); - } catch (NoSuchElementException e) { - Slog.wtf(TAG, "Failed to unlink wakelock", e); - } + wakeLock.unlinkToDeath(); + wakeLock.setDisabled(true); removeWakeLockLocked(wakeLock, index); } } @@ -1650,13 +1645,41 @@ public final class PowerManagerService extends SystemService if (!wakeLock.hasSameWorkSource(ws)) { notifyWakeLockChangingLocked(wakeLock, wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid, - ws, historyTag); + ws, historyTag, null); wakeLock.mHistoryTag = historyTag; wakeLock.updateWorkSource(ws); } } } + private void updateWakeLockCallbackInternal(IBinder lock, IWakeLockCallback callback, + int callingUid) { + synchronized (mLock) { + int index = findWakeLockIndexLocked(lock); + if (index < 0) { + if (DEBUG_SPEW) { + Slog.d(TAG, "updateWakeLockCallbackInternal: lock=" + Objects.hashCode(lock) + + " [not found]"); + } + throw new IllegalArgumentException("Wake lock not active: " + lock + + " from uid " + callingUid); + } + + WakeLock wakeLock = mWakeLocks.get(index); + if (DEBUG_SPEW) { + Slog.d(TAG, "updateWakeLockCallbackInternal: lock=" + Objects.hashCode(lock) + + " [" + wakeLock.mTag + "]"); + } + + if (!isSameCallback(callback, wakeLock.mCallback)) { + notifyWakeLockChangingLocked(wakeLock, wakeLock.mFlags, wakeLock.mTag, + wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid, + wakeLock.mWorkSource, wakeLock.mHistoryTag, callback); + wakeLock.mCallback = callback; + } + } + } + @GuardedBy("mLock") private int findWakeLockIndexLocked(IBinder lock) { final int count = mWakeLocks.size(); @@ -1684,7 +1707,7 @@ public final class PowerManagerService extends SystemService wakeLock.mNotifiedAcquired = true; mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource, - wakeLock.mHistoryTag); + wakeLock.mHistoryTag, wakeLock.mCallback); restartNofifyLongTimerLocked(wakeLock); } } @@ -1726,11 +1749,13 @@ public final class PowerManagerService extends SystemService @GuardedBy("mLock") private void notifyWakeLockChangingLocked(WakeLock wakeLock, int flags, String tag, - String packageName, int uid, int pid, WorkSource ws, String historyTag) { + String packageName, int uid, int pid, WorkSource ws, String historyTag, + IWakeLockCallback callback) { if (mSystemReady && wakeLock.mNotifiedAcquired) { mNotifier.onWakeLockChanging(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource, - wakeLock.mHistoryTag, flags, tag, packageName, uid, pid, ws, historyTag); + wakeLock.mHistoryTag, wakeLock.mCallback, flags, tag, packageName, uid, pid, ws, + historyTag, callback); notifyWakeLockLongFinishedLocked(wakeLock); // Changing the wake lock will count as releasing the old wake lock(s) and // acquiring the new ones... we do this because otherwise once a wakelock @@ -1747,7 +1772,7 @@ public final class PowerManagerService extends SystemService wakeLock.mAcquireTime = 0; mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid, - wakeLock.mWorkSource, wakeLock.mHistoryTag); + wakeLock.mWorkSource, wakeLock.mHistoryTag, wakeLock.mCallback); notifyWakeLockLongFinishedLocked(wakeLock); } } @@ -4045,10 +4070,7 @@ public final class PowerManagerService extends SystemService } } } - if (wakeLock.mDisabled != disabled) { - wakeLock.mDisabled = disabled; - return true; - } + return wakeLock.setDisabled(disabled); } return false; } @@ -5041,10 +5063,11 @@ public final class PowerManagerService extends SystemService public boolean mNotifiedAcquired; public boolean mNotifiedLong; public boolean mDisabled; + public IWakeLockCallback mCallback; public WakeLock(IBinder lock, int displayId, int flags, String tag, String packageName, WorkSource workSource, String historyTag, int ownerUid, int ownerPid, - UidState uidState) { + UidState uidState, @Nullable IWakeLockCallback callback) { mLock = lock; mDisplayId = displayId; mFlags = flags; @@ -5055,15 +5078,43 @@ public final class PowerManagerService extends SystemService mOwnerUid = ownerUid; mOwnerPid = ownerPid; mUidState = uidState; + mCallback = callback; + linkToDeath(); } @Override public void binderDied() { + unlinkToDeath(); PowerManagerService.this.handleWakeLockDeath(this); } + private void linkToDeath() { + try { + mLock.linkToDeath(this, 0); + } catch (RemoteException e) { + throw new IllegalArgumentException("Wakelock.mLock is already dead."); + } + } + + @GuardedBy("mLock") + void unlinkToDeath() { + try { + mLock.unlinkToDeath(this, 0); + } catch (NoSuchElementException e) { + Slog.wtf(TAG, "Failed to unlink Wakelock.mLock", e); + } + } + + public boolean setDisabled(boolean disabled) { + if (mDisabled != disabled) { + mDisabled = disabled; + return true; + } else { + return false; + } + } public boolean hasSameProperties(int flags, String tag, WorkSource workSource, - int ownerUid, int ownerPid) { + int ownerUid, int ownerPid, IWakeLockCallback callback) { return mFlags == flags && mTag.equals(tag) && hasSameWorkSource(workSource) @@ -5072,7 +5123,8 @@ public final class PowerManagerService extends SystemService } public void updateProperties(int flags, String tag, String packageName, - WorkSource workSource, String historyTag, int ownerUid, int ownerPid) { + WorkSource workSource, String historyTag, int ownerUid, int ownerPid, + IWakeLockCallback callback) { if (!mPackageName.equals(packageName)) { throw new IllegalStateException("Existing wake lock package name changed: " + mPackageName + " to " + packageName); @@ -5089,6 +5141,7 @@ public final class PowerManagerService extends SystemService mTag = tag; updateWorkSource(workSource); mHistoryTag = historyTag; + mCallback = callback; } public boolean hasSameWorkSource(WorkSource workSource) { @@ -5307,11 +5360,12 @@ public final class PowerManagerService extends SystemService @Override // Binder call public void acquireWakeLockWithUid(IBinder lock, int flags, String tag, - String packageName, int uid, int displayId) { + String packageName, int uid, int displayId, IWakeLockCallback callback) { if (uid < 0) { uid = Binder.getCallingUid(); } - acquireWakeLock(lock, flags, tag, packageName, new WorkSource(uid), null, displayId); + acquireWakeLock(lock, flags, tag, packageName, new WorkSource(uid), null, + displayId, callback); } @Override // Binder call @@ -5346,7 +5400,8 @@ public final class PowerManagerService extends SystemService @Override // Binder call public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, - WorkSource ws, String historyTag, int displayId) { + WorkSource ws, String historyTag, int displayId, + @Nullable IWakeLockCallback callback) { if (lock == null) { throw new IllegalArgumentException("lock must not be null"); } @@ -5386,7 +5441,7 @@ public final class PowerManagerService extends SystemService final long ident = Binder.clearCallingIdentity(); try { acquireWakeLockInternal(lock, displayId, flags, tag, packageName, ws, historyTag, - uid, pid); + uid, pid, callback); } finally { Binder.restoreCallingIdentity(ident); } @@ -5395,7 +5450,8 @@ public final class PowerManagerService extends SystemService @Override // Binder call public void acquireWakeLockAsync(IBinder lock, int flags, String tag, String packageName, WorkSource ws, String historyTag) { - acquireWakeLock(lock, flags, tag, packageName, ws, historyTag, Display.INVALID_DISPLAY); + acquireWakeLock(lock, flags, tag, packageName, ws, historyTag, Display.INVALID_DISPLAY, + null); } @Override // Binder call @@ -5463,6 +5519,23 @@ public final class PowerManagerService extends SystemService } @Override // Binder call + public void updateWakeLockCallback(IBinder lock, IWakeLockCallback callback) { + if (lock == null) { + throw new IllegalArgumentException("lock must not be null"); + } + + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); + + final int callingUid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + try { + updateWakeLockCallbackInternal(lock, callback, callingUid); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call public boolean isWakeLockLevelSupported(int level) { final long ident = Binder.clearCallingIdentity(); try { @@ -6510,4 +6583,15 @@ public final class PowerManagerService extends SystemService } }; + static boolean isSameCallback(IWakeLockCallback callback1, + IWakeLockCallback callback2) { + if (callback1 == callback2) { + return true; + } + if (callback1 != null && callback2 != null + && callback1.asBinder() == callback2.asBinder()) { + return true; + } + return false; + } } diff --git a/services/core/java/com/android/server/resources/ResourcesManagerService.java b/services/core/java/com/android/server/resources/ResourcesManagerService.java new file mode 100644 index 000000000000..cc275466a5ff --- /dev/null +++ b/services/core/java/com/android/server/resources/ResourcesManagerService.java @@ -0,0 +1,102 @@ +/* + * 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.resources; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.IResourcesManager; +import android.os.Binder; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.RemoteCallback; +import android.os.RemoteException; + +import com.android.server.SystemService; +import com.android.server.am.ActivityManagerService; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * A service for managing information about ResourcesManagers + */ +public class ResourcesManagerService extends SystemService { + private ActivityManagerService mActivityManagerService; + + /** + * Initializes the system service. + * <p> + * Subclasses must define a single argument constructor that accepts the context + * and passes it to super. + * </p> + * + * @param context The system server context. + */ + public ResourcesManagerService(@NonNull Context context) { + super(context); + publishBinderService(Context.RESOURCES_SERVICE, mService); + } + + @Override + public void onStart() { + // Intentionally left empty. + } + + private final IBinder mService = new IResourcesManager.Stub() { + @Override + public boolean dumpResources(String process, ParcelFileDescriptor fd, + RemoteCallback callback) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) { + callback.sendResult(null); + throw new SecurityException("dump should only be called by shell"); + } + return mActivityManagerService.dumpResources(process, fd, callback); + } + + @Override + protected void dump(@NonNull FileDescriptor fd, + @NonNull PrintWriter pw, @Nullable String[] args) { + try { + mActivityManagerService.dumpAllResources(ParcelFileDescriptor.dup(fd), pw); + } catch (Exception e) { + pw.println("Exception while trying to dump all resources: " + e.getMessage()); + e.printStackTrace(pw); + } + } + + @Override + public int handleShellCommand(@NonNull ParcelFileDescriptor in, + @NonNull ParcelFileDescriptor out, + @NonNull ParcelFileDescriptor err, + @NonNull String[] args) { + return (new ResourcesManagerShellCommand(this)).exec( + this, + in.getFileDescriptor(), + out.getFileDescriptor(), + err.getFileDescriptor(), + args); + } + }; + + public void setActivityManagerService( + ActivityManagerService activityManagerService) { + mActivityManagerService = activityManagerService; + } +} diff --git a/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java new file mode 100644 index 000000000000..7d8336a0d3e9 --- /dev/null +++ b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java @@ -0,0 +1,94 @@ +/* + * 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.resources; + +import android.content.res.IResourcesManager; +import android.os.ConditionVariable; +import android.os.ParcelFileDescriptor; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.ShellCommand; +import android.util.Slog; + +import java.io.IOException; +import java.io.PrintWriter; + +/** + * Shell command handler for resources related commands + */ +public class ResourcesManagerShellCommand extends ShellCommand { + private static final String TAG = "ResourcesManagerShellCommand"; + + private final IResourcesManager mInterface; + + public ResourcesManagerShellCommand(IResourcesManager anInterface) { + mInterface = anInterface; + } + + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + final PrintWriter err = getErrPrintWriter(); + try { + switch (cmd) { + case "dump": + return dumpResources(); + default: + return handleDefaultCommands(cmd); + } + } catch (IllegalArgumentException e) { + err.println("Error: " + e.getMessage()); + } catch (RemoteException e) { + err.println("Remote exception: " + e); + } + return -1; + } + + private int dumpResources() throws RemoteException { + String processId = getNextArgRequired(); + try { + ConditionVariable lock = new ConditionVariable(); + RemoteCallback + finishCallback = new RemoteCallback(result -> lock.open(), null); + + if (!mInterface.dumpResources(processId, + ParcelFileDescriptor.dup(getOutFileDescriptor()), finishCallback)) { + getErrPrintWriter().println("RESOURCES DUMP FAILED on process " + processId); + return -1; + } + lock.block(5000); + return 0; + } catch (IOException e) { + Slog.e(TAG, "Exception while dumping resources", e); + getErrPrintWriter().println("Exception while dumping resources: " + e.getMessage()); + } + return -1; + } + + @Override + public void onHelp() { + final PrintWriter out = getOutPrintWriter(); + out.println("Resources manager commands:"); + out.println(" help"); + out.println(" Print this help text."); + out.println(" dump <PROCESS>"); + out.println(" Dump the Resources objects in use as well as the history of Resources"); + + } +} diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 344edbbef9c7..8a87c96fcaaa 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -158,6 +158,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D private final SparseArray<UiState> mDisplayUiState = new SparseArray<>(); @GuardedBy("mLock") private IUdfpsHbmListener mUdfpsHbmListener; + @GuardedBy("mLock") + private IBiometricContextListener mBiometricContextListener; @GuardedBy("mCurrentRequestAddTilePackages") private final ArrayMap<String, Long> mCurrentRequestAddTilePackages = new ArrayMap<>(); @@ -897,6 +899,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D @Override public void setBiometicContextListener(IBiometricContextListener listener) { enforceStatusBarService(); + synchronized (mLock) { + mBiometricContextListener = listener; + } if (mBar != null) { try { mBar.setBiometicContextListener(listener); @@ -1327,6 +1332,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D mHandler.post(() -> { synchronized (mLock) { setUdfpsHbmListener(mUdfpsHbmListener); + setBiometicContextListener(mBiometricContextListener); } }); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index f8d7b784f08b..5f04b7e2621a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -46,6 +46,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.activityTypeToString; import static android.app.WindowConfiguration.isSplitScreenWindowingMode; +import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION; +import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE; +import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON; import static android.content.Intent.ACTION_MAIN; import static android.content.Intent.CATEGORY_HOME; import static android.content.Intent.CATEGORY_LAUNCHER; @@ -241,6 +244,7 @@ import android.app.TaskInfo; import android.app.TaskInfo.CameraCompatControlState; import android.app.WaitResult; import android.app.WindowConfiguration; +import android.app.admin.DevicePolicyManager; import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ActivityRelaunchItem; @@ -272,6 +276,7 @@ import android.graphics.Bitmap; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.hardware.HardwareBuffer; import android.net.Uri; import android.os.Binder; @@ -538,6 +543,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Tracking splash screen status from previous activity boolean mSplashScreenStyleEmpty = false; + Drawable mEnterpriseThumbnailDrawable; + + private void updateEnterpriseThumbnailDrawable(Context context) { + DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); + mEnterpriseThumbnailDrawable = dpm.getDrawable( + WORK_PROFILE_ICON, OUTLINE, PROFILE_SWITCH_ANIMATION, + () -> context.getDrawable(R.drawable.ic_corp_badge)); + } + static final int LAUNCH_SOURCE_TYPE_SYSTEM = 1; static final int LAUNCH_SOURCE_TYPE_HOME = 2; static final int LAUNCH_SOURCE_TYPE_SYSTEMUI = 3; @@ -716,6 +730,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Nullable private Rect mLetterboxBoundsForFixedOrientationAndAspectRatio; + // Whether the activity is eligible to be letterboxed for fixed orientation with respect to its + // requested orientation, even when it's letterbox for another reason (e.g., size compat mode) + // and therefore #isLetterboxedForFixedOrientationAndAspectRatio returns false. + private boolean mIsEligibleForFixedOrientationLetterbox; + // State of the Camera app compat control which is used to correct stretched viewfinder // in apps that don't handle all possible configurations and changes between them correctly. @CameraCompatControlState @@ -1925,6 +1944,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAtmService.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, packageName); mActivityRecordInputSink = new ActivityRecordInputSink(this); + + updateEnterpriseThumbnailDrawable(mAtmService.mUiContext); } /** @@ -6988,12 +7009,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } final Rect frame = win.getRelativeFrame(); - final int thumbnailDrawableRes = task.mUserId == mWmService.mCurrentUserId - ? R.drawable.ic_account_circle - : R.drawable.ic_corp_badge; - final HardwareBuffer thumbnail = - getDisplayContent().mAppTransition - .createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame); + final Drawable thumbnailDrawable = task.mUserId == mWmService.mCurrentUserId + ? mAtmService.mUiContext.getDrawable(R.drawable.ic_account_circle) + : mEnterpriseThumbnailDrawable; + final HardwareBuffer thumbnail = getDisplayContent().mAppTransition + .createCrossProfileAppsThumbnail(thumbnailDrawable, frame); if (thumbnail == null) { return; } @@ -7627,6 +7647,24 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** + * Whether this activity is eligible for letterbox eduction. + * + * <p>Conditions that need to be met: + * + * <ul> + * <li>{@link LetterboxConfiguration#getIsEducationEnabled} is true. + * <li>The activity is eligible for fixed orientation letterbox. + * <li>The activity is in fullscreen. + * </ul> + */ + // TODO(b/215316431): Add tests + boolean isEligibleForLetterboxEducation() { + return mWmService.mLetterboxConfiguration.getIsEducationEnabled() + && mIsEligibleForFixedOrientationLetterbox + && getWindowingMode() == WINDOWING_MODE_FULLSCREEN; + } + + /** * In some cases, applying insets to bounds changes the orientation. For example, if a * close-to-square display rotates to portrait to respect a portrait orientation activity, after * insets such as the status and nav bars are applied, the activity may actually have a @@ -7688,6 +7726,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig, int windowingMode) { mLetterboxBoundsForFixedOrientationAndAspectRatio = null; + mIsEligibleForFixedOrientationLetterbox = false; final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); final Rect stableBounds = new Rect(); // If orientation is respected when insets are applied, then stableBounds will be empty. @@ -7727,8 +7766,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // make it fit the available bounds by scaling down its bounds. final int forcedOrientation = getRequestedConfigurationOrientation(); - if (forcedOrientation == ORIENTATION_UNDEFINED - || (forcedOrientation == parentOrientation && orientationRespectedWithInsets)) { + mIsEligibleForFixedOrientationLetterbox = forcedOrientation != ORIENTATION_UNDEFINED + && forcedOrientation != parentOrientation; + + if (!mIsEligibleForFixedOrientationLetterbox && (forcedOrientation == ORIENTATION_UNDEFINED + || orientationRespectedWithInsets)) { return; } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index d5abe4f8ed02..56adcfd76403 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -93,7 +93,6 @@ import static com.android.server.wm.WindowManagerInternal.KeyguardExitAnimationS import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM; import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE; -import android.annotation.DrawableRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; @@ -101,6 +100,7 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.hardware.HardwareBuffer; import android.os.Binder; import android.os.Debug; @@ -539,8 +539,8 @@ public class AppTransition implements Dump { * animation. */ HardwareBuffer createCrossProfileAppsThumbnail( - @DrawableRes int thumbnailDrawableRes, Rect frame) { - return mTransitionAnimation.createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame); + Drawable thumbnailDrawable, Rect frame) { + return mTransitionAnimation.createCrossProfileAppsThumbnail(thumbnailDrawable, frame); } Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4009220d1008..c87027dde3ad 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5486,26 +5486,46 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } void updateKeepClearAreas() { + final List<Rect> restrictedKeepClearAreas = new ArrayList(); + final List<Rect> unrestrictedKeepClearAreas = new ArrayList(); + getKeepClearAreas(restrictedKeepClearAreas, unrestrictedKeepClearAreas); mWmService.mDisplayNotificationController.dispatchKeepClearAreasChanged( - this, getKeepClearAreas()); + this, restrictedKeepClearAreas, unrestrictedKeepClearAreas); } /** - * Returns all keep-clear areas from visible windows on this display. + * Fills {@param outRestricted} with all keep-clear areas from visible, relevant windows + * on this display, which set restricted keep-clear areas. + * Fills {@param outUnrestricted} with keep-clear areas from visible, relevant windows on this + * display, which set unrestricted keep-clear areas. + * + * For context on restricted vs unrestricted keep-clear areas, see + * {@link android.Manifest.permission.USE_UNRESTRICTED_KEEP_CLEAR_AREAS}. */ - ArrayList<Rect> getKeepClearAreas() { - final ArrayList<Rect> keepClearAreas = new ArrayList<Rect>(); + void getKeepClearAreas(List<Rect> outRestricted, List<Rect> outUnrestricted) { final Matrix tmpMatrix = new Matrix(); final float[] tmpFloat9 = new float[9]; forAllWindows(w -> { if (w.isVisible() && !w.inPinnedWindowingMode()) { - keepClearAreas.addAll(w.getKeepClearAreas(tmpMatrix, tmpFloat9)); + if (w.mSession.mSetsUnrestrictedKeepClearAreas) { + outUnrestricted.addAll(w.getKeepClearAreas(tmpMatrix, tmpFloat9)); + } else { + outRestricted.addAll(w.getKeepClearAreas(tmpMatrix, tmpFloat9)); + } } // We stop traversing when we reach the base of a fullscreen app. return w.getWindowType() == TYPE_BASE_APPLICATION && w.getWindowingMode() == WINDOWING_MODE_FULLSCREEN; }, true); + } + + /** + * Returns all keep-clear areas from visible, relevant windows on this display. + */ + ArrayList<Rect> getKeepClearAreas() { + final ArrayList<Rect> keepClearAreas = new ArrayList<Rect>(); + getKeepClearAreas(keepClearAreas, keepClearAreas); return keepClearAreas; } diff --git a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java index 276dbe9c844a..e18d5396831a 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java +++ b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java @@ -120,12 +120,13 @@ class DisplayWindowListenerController { mDisplayListeners.finishBroadcast(); } - void dispatchKeepClearAreasChanged(DisplayContent display, List<Rect> keepClearAreas) { + void dispatchKeepClearAreasChanged(DisplayContent display, List<Rect> restricted, + List<Rect> unrestricted) { int count = mDisplayListeners.beginBroadcast(); for (int i = 0; i < count; ++i) { try { mDisplayListeners.getBroadcastItem(i).onKeepClearAreasChanged( - display.mDisplayId, keepClearAreas); + display.mDisplayId, restricted, unrestricted); } catch (RemoteException e) { } } diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index 1955e30aab30..ad2767c41e82 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -126,6 +126,9 @@ final class LetterboxConfiguration { @LetterboxReachabilityPosition private volatile int mLetterboxPositionForReachability; + // Whether education is allowed for letterboxed fullscreen apps. + private boolean mIsEducationEnabled; + LetterboxConfiguration(Context systemUiContext) { mContext = systemUiContext; mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( @@ -143,6 +146,8 @@ final class LetterboxConfiguration { R.bool.config_letterboxIsReachabilityEnabled); mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext); mLetterboxPositionForReachability = mDefaultPositionForReachability; + mIsEducationEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsEducationEnabled); } /** @@ -501,4 +506,27 @@ final class LetterboxConfiguration { mLetterboxPositionForReachability = Math.max(mLetterboxPositionForReachability - 1, 0); } + /** + * Whether education is allowed for letterboxed fullscreen apps. + */ + boolean getIsEducationEnabled() { + return mIsEducationEnabled; + } + + /** + * Overrides whether education is allowed for letterboxed fullscreen apps. + */ + void setIsEducationEnabled(boolean enabled) { + mIsEducationEnabled = enabled; + } + + /** + * Resets whether education is allowed for letterboxed fullscreen apps to + * {@link R.bool.config_letterboxIsEducationEnabled}. + */ + void resetIsEducationEnabled() { + mIsEducationEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsEducationEnabled); + } + } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 98acc4607d18..2ae2b4370522 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.Manifest.permission.HIDE_OVERLAY_WINDOWS; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; +import static android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY; import static android.app.ActivityTaskManager.INVALID_TASK_ID; @@ -114,6 +115,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { private String mRelayoutTag; private final InsetsVisibilities mDummyRequestedVisibilities = new InsetsVisibilities(); private final InsetsSourceControl[] mDummyControls = new InsetsSourceControl[0]; + final boolean mSetsUnrestrictedKeepClearAreas; public Session(WindowManagerService service, IWindowSessionCallback callback) { mService = service; @@ -132,6 +134,9 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { == PERMISSION_GRANTED; mCanStartTasksFromRecents = service.mContext.checkCallingOrSelfPermission( START_TASKS_FROM_RECENTS) == PERMISSION_GRANTED; + mSetsUnrestrictedKeepClearAreas = + service.mContext.checkCallingOrSelfPermission(SET_UNRESTRICTED_KEEP_CLEAR_AREAS) + == PERMISSION_GRANTED; mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications; mDragDropController = mService.mDragDropController; StringBuilder sb = new StringBuilder(); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 7d06526e18b5..97735a29b027 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3434,6 +3434,9 @@ class Task extends TaskFragment { // Whether the direct top activity is in size compat mode on foreground. info.topActivityInSizeCompat = isTopActivityResumed && mReuseActivitiesReport.top.inSizeCompatMode(); + // Whether the direct top activity is eligible for letterbox education. + info.topActivityEligibleForLetterboxEducation = isTopActivityResumed + && mReuseActivitiesReport.top.isEligibleForLetterboxEducation(); // Whether the direct top activity requested showing camera compat control. info.cameraCompatControlState = isTopActivityResumed ? mReuseActivitiesReport.top.getCameraCompatControlState() diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 0f8587c99958..1cf4c1b0fbb2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -822,6 +822,29 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } + private int runSetLetterboxIsEducationEnabled(PrintWriter pw) throws RemoteException { + String arg = getNextArg(); + final boolean enabled; + switch (arg) { + case "true": + case "1": + enabled = true; + break; + case "false": + case "0": + enabled = false; + break; + default: + getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg); + return -1; + } + + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setIsEducationEnabled(enabled); + } + return 0; + } + private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException { if (peekNextArg() == null) { getErrPrintWriter().println("Error: No arguments provided."); @@ -859,6 +882,9 @@ public class WindowManagerShellCommand extends ShellCommand { case "--defaultPositionForReachability": runSetLetterboxDefaultPositionForReachability(pw); break; + case "--isEducationEnabled": + runSetLetterboxIsEducationEnabled(pw); + break; default: getErrPrintWriter().println( "Error: Unrecognized letterbox style option: " + arg); @@ -903,6 +929,9 @@ public class WindowManagerShellCommand extends ShellCommand { case "defaultPositionForReachability": mLetterboxConfiguration.getDefaultPositionForReachability(); break; + case "isEducationEnabled": + mLetterboxConfiguration.getIsEducationEnabled(); + break; default: getErrPrintWriter().println( "Error: Unrecognized letterbox style option: " + arg); @@ -998,6 +1027,7 @@ public class WindowManagerShellCommand extends ShellCommand { mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); mLetterboxConfiguration.resetIsReachabilityEnabled(); mLetterboxConfiguration.resetDefaultPositionForReachability(); + mLetterboxConfiguration.resetIsEducationEnabled(); } } @@ -1014,6 +1044,8 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println("Default position for reachability: " + LetterboxConfiguration.letterboxReachabilityPositionToString( mLetterboxConfiguration.getDefaultPositionForReachability())); + pw.println("Is education enabled: " + + mLetterboxConfiguration.getIsEducationEnabled()); pw.println("Background type: " + LetterboxConfiguration.letterboxBackgroundTypeToString( @@ -1154,10 +1186,12 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" --defaultPositionForReachability [left|center|right]"); pw.println(" Default horizontal position of app window when reachability is."); pw.println(" enabled."); + pw.println(" --isEducationEnabled [true|1|false|0]"); + pw.println(" Whether education is allowed for letterboxed fullscreen apps."); pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType"); pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha"); pw.println(" |horizontalPositionMultiplier|isReachabilityEnabled"); - pw.println(" |defaultPositionMultiplierForReachability]"); + pw.println(" isEducationEnabled||defaultPositionMultiplierForReachability]"); pw.println(" Resets overrides to default values for specified properties separated"); pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'."); pw.println(" If no arguments provided, all values will be reset."); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 95ef5f76b765..99abf440910e 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -103,6 +103,7 @@ cc_defaults { "libappfuse", "libbinder_ndk", "libbinder", + "libchrome", "libcutils", "libcrypto", "liblog", diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index df5fb2827a16..8cb27e179c19 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -457,6 +457,9 @@ void NativeInputManager::dump(std::string& dump) { mInputManager->getReader().dump(dump); dump += "\n"; + mInputManager->getUnwantedInteractionBlocker().dump(dump); + dump += "\n"; + mInputManager->getClassifier().dump(dump); dump += "\n"; @@ -704,6 +707,7 @@ void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) { void NativeInputManager::notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) { ATRACE_CALL(); + mInputManager->getUnwantedInteractionBlocker().notifyInputDevicesChanged(inputDevices); JNIEnv* env = jniEnv(); size_t count = inputDevices.size(); diff --git a/services/core/jni/gnss/GnssAntennaInfoCallback.cpp b/services/core/jni/gnss/GnssAntennaInfoCallback.cpp index fbc000b25d26..99d06eb062da 100644 --- a/services/core/jni/gnss/GnssAntennaInfoCallback.cpp +++ b/services/core/jni/gnss/GnssAntennaInfoCallback.cpp @@ -226,17 +226,18 @@ jobject GnssAntennaInfoCallback::translateSingleGnssAntennaInfo( env->NewObject(class_gnssAntennaInfoBuilder, method_gnssAntennaInfoBuilderCtor); // Set fields - env->CallObjectMethod(gnssAntennaInfoBuilderObject, - method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz, - gnssAntennaInfo.carrierFrequencyMHz); - env->CallObjectMethod(gnssAntennaInfoBuilderObject, - method_gnssAntennaInfoBuilderSetPhaseCenterOffset, phaseCenterOffset); - env->CallObjectMethod(gnssAntennaInfoBuilderObject, - method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections, - phaseCenterVariationCorrections); - env->CallObjectMethod(gnssAntennaInfoBuilderObject, - method_gnssAntennaInfoBuilderSetSignalGainCorrections, - signalGainCorrections); + callObjectMethodIgnoringResult(env, gnssAntennaInfoBuilderObject, + method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz, + gnssAntennaInfo.carrierFrequencyMHz); + callObjectMethodIgnoringResult(env, gnssAntennaInfoBuilderObject, + method_gnssAntennaInfoBuilderSetPhaseCenterOffset, + phaseCenterOffset); + callObjectMethodIgnoringResult(env, gnssAntennaInfoBuilderObject, + method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections, + phaseCenterVariationCorrections); + callObjectMethodIgnoringResult(env, gnssAntennaInfoBuilderObject, + method_gnssAntennaInfoBuilderSetSignalGainCorrections, + signalGainCorrections); // build jobject gnssAntennaInfoObject = diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp index 6c0d5d984980..34ca559806c6 100644 --- a/services/core/jni/gnss/GnssMeasurementCallback.cpp +++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp @@ -212,13 +212,14 @@ void setMeasurementData(JNIEnv* env, jobject& callbacksObj, jobject clock, jobject gnssMeasurementsEventBuilderObject = env->NewObject(class_gnssMeasurementsEventBuilder, method_gnssMeasurementsEventBuilderCtor); - env->CallObjectMethod(gnssMeasurementsEventBuilderObject, - method_gnssMeasurementsEventBuilderSetClock, clock); - env->CallObjectMethod(gnssMeasurementsEventBuilderObject, - method_gnssMeasurementsEventBuilderSetMeasurements, measurementArray); - env->CallObjectMethod(gnssMeasurementsEventBuilderObject, - method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls, - gnssAgcArray); + callObjectMethodIgnoringResult(env, gnssMeasurementsEventBuilderObject, + method_gnssMeasurementsEventBuilderSetClock, clock); + callObjectMethodIgnoringResult(env, gnssMeasurementsEventBuilderObject, + method_gnssMeasurementsEventBuilderSetMeasurements, + measurementArray); + callObjectMethodIgnoringResult(env, gnssMeasurementsEventBuilderObject, + method_gnssMeasurementsEventBuilderSetGnssAutomaticGainControls, + gnssAgcArray); jobject gnssMeasurementsEventObject = env->CallObjectMethod(gnssMeasurementsEventBuilderObject, method_gnssMeasurementsEventBuilderBuild); @@ -408,24 +409,24 @@ void GnssMeasurementCallbackAidl::translateSingleGnssMeasurement(JNIEnv* env, satellitePvt.satClockInfo.satHardwareCodeBiasMeters, satellitePvt.satClockInfo.satTimeCorrectionMeters, satellitePvt.satClockInfo.satClkDriftMps); - env->CallObjectMethod(satellitePvtBuilderObject, - method_satellitePvtBuilderSetPositionEcef, positionEcef); - env->CallObjectMethod(satellitePvtBuilderObject, - method_satellitePvtBuilderSetVelocityEcef, velocityEcef); - env->CallObjectMethod(satellitePvtBuilderObject, method_satellitePvtBuilderSetClockInfo, - clockInfo); + callObjectMethodIgnoringResult(env, satellitePvtBuilderObject, + method_satellitePvtBuilderSetPositionEcef, positionEcef); + callObjectMethodIgnoringResult(env, satellitePvtBuilderObject, + method_satellitePvtBuilderSetVelocityEcef, velocityEcef); + callObjectMethodIgnoringResult(env, satellitePvtBuilderObject, + method_satellitePvtBuilderSetClockInfo, clockInfo); } if (satFlags & SatellitePvt::HAS_IONO) { - env->CallObjectMethod(satellitePvtBuilderObject, - method_satellitePvtBuilderSetIonoDelayMeters, - satellitePvt.ionoDelayMeters); + callObjectMethodIgnoringResult(env, satellitePvtBuilderObject, + method_satellitePvtBuilderSetIonoDelayMeters, + satellitePvt.ionoDelayMeters); } if (satFlags & SatellitePvt::HAS_TROPO) { - env->CallObjectMethod(satellitePvtBuilderObject, - method_satellitePvtBuilderSetTropoDelayMeters, - satellitePvt.tropoDelayMeters); + callObjectMethodIgnoringResult(env, satellitePvtBuilderObject, + method_satellitePvtBuilderSetTropoDelayMeters, + satellitePvt.tropoDelayMeters); } jobject satellitePvtObject = @@ -453,17 +454,19 @@ void GnssMeasurementCallbackAidl::translateSingleGnssMeasurement(JNIEnv* env, jobject correlationVectorBuilderObject = env->NewObject(class_correlationVectorBuilder, method_correlationVectorBuilderCtor); - env->CallObjectMethod(correlationVectorBuilderObject, - method_correlationVectorBuilderSetMagnitude, magnitudeArray); - env->CallObjectMethod(correlationVectorBuilderObject, - method_correlationVectorBuilderSetFrequencyOffsetMetersPerSecond, - correlationVector.frequencyOffsetMps); - env->CallObjectMethod(correlationVectorBuilderObject, - method_correlationVectorBuilderSetSamplingStartMeters, - correlationVector.samplingStartM); - env->CallObjectMethod(correlationVectorBuilderObject, - method_correlationVectorBuilderSetSamplingWidthMeters, - correlationVector.samplingWidthM); + callObjectMethodIgnoringResult(env, correlationVectorBuilderObject, + method_correlationVectorBuilderSetMagnitude, + magnitudeArray); + callObjectMethodIgnoringResult( + env, correlationVectorBuilderObject, + method_correlationVectorBuilderSetFrequencyOffsetMetersPerSecond, + correlationVector.frequencyOffsetMps); + callObjectMethodIgnoringResult(env, correlationVectorBuilderObject, + method_correlationVectorBuilderSetSamplingStartMeters, + correlationVector.samplingStartM); + callObjectMethodIgnoringResult(env, correlationVectorBuilderObject, + method_correlationVectorBuilderSetSamplingWidthMeters, + correlationVector.samplingWidthM); jobject correlationVectorObject = env->CallObjectMethod(correlationVectorBuilderObject, method_correlationVectorBuilderBuild); @@ -519,12 +522,14 @@ jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs(JNIEnv* env, const GnssAgc& gnssAgc = agcs[i]; jobject agcBuilderObject = env->NewObject(class_gnssAgcBuilder, method_gnssAgcBuilderCtor); - env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetLevelDb, - gnssAgc.agcLevelDb); - env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetConstellationType, - (int)gnssAgc.constellation); - env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetCarrierFrequencyHz, - gnssAgc.carrierFrequencyHz); + callObjectMethodIgnoringResult(env, agcBuilderObject, method_gnssAgcBuilderSetLevelDb, + gnssAgc.agcLevelDb); + callObjectMethodIgnoringResult(env, agcBuilderObject, + method_gnssAgcBuilderSetConstellationType, + (int)gnssAgc.constellation); + callObjectMethodIgnoringResult(env, agcBuilderObject, + method_gnssAgcBuilderSetCarrierFrequencyHz, + gnssAgc.carrierFrequencyHz); jobject agcObject = env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderBuild); env->SetObjectArrayElement(gnssAgcArray, i, agcObject); diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp index 40a94ce62b05..8f32c47fcb5a 100644 --- a/services/core/jni/gnss/Utils.cpp +++ b/services/core/jni/gnss/Utils.cpp @@ -111,6 +111,13 @@ void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { } } +void callObjectMethodIgnoringResult(JNIEnv* env, jobject obj, jmethodID mid, ...) { + va_list args; + va_start(args, mid); + env->DeleteLocalRef(env->CallObjectMethodV(obj, mid, args)); + va_end(args); +} + JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor) : env_(env), clazz_(clazz) { object_ = env_->NewObject(clazz_, defaultCtor); diff --git a/services/core/jni/gnss/Utils.h b/services/core/jni/gnss/Utils.h index 2640a7774c26..c8ee661bc10a 100644 --- a/services/core/jni/gnss/Utils.h +++ b/services/core/jni/gnss/Utils.h @@ -56,6 +56,8 @@ jboolean checkAidlStatus(const android::binder::Status& status, const char* erro void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); +void callObjectMethodIgnoringResult(JNIEnv* env, jobject obj, jmethodID mid, ...); + template <class T> void logHidlError(hardware::Return<T>& result, const char* errorMessage) { ALOGE("%s HIDL transport error: %s", errorMessage, result.description().c_str()); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 3bda7bf049c4..ee64a73471d0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -17680,6 +17680,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ? Collections.emptySet() : mOverlayPackagesProvider.getNonRequiredApps( admin, caller.getUserId(), ACTION_PROVISION_MANAGED_PROFILE); + if (nonRequiredApps.isEmpty()) { + Slogf.i(LOG_TAG, "No disallowed packages for the managed profile."); + } else { + for (String packageName : nonRequiredApps) { + Slogf.i(LOG_TAG, "Disallowed package [" + packageName + "]"); + } + } userInfo = mUserManager.createProfileForUserEvenWhenDisallowed( provisioningParams.getProfileName(), UserManager.USER_TYPE_PROFILE_MANAGED, @@ -17698,6 +17705,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { startTime, callerPackage); + onCreateAndProvisionManagedProfileStarted(provisioningParams); + installExistingAdminPackage(userInfo.id, admin.getPackageName()); if (!enableAdminAndSetProfileOwner( userInfo.id, caller.getUserId(), admin, provisioningParams.getOwnerName())) { @@ -17718,6 +17727,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + onCreateAndProvisionManagedProfileCompleted(provisioningParams); + sendProvisioningCompletedBroadcast( userInfo.id, ACTION_PROVISION_MANAGED_PROFILE, @@ -17739,6 +17750,29 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + /** + * Callback called at the beginning of {@link #createAndProvisionManagedProfile( + * ManagedProfileProvisioningParams, String)} after the relevant prechecks have passed. + * + * <p>The logic in this method blocks provisioning. + * + * <p>This method is meant to be overridden by OEMs. + */ + private void onCreateAndProvisionManagedProfileStarted( + ManagedProfileProvisioningParams provisioningParams) {} + + /** + * Callback called at the end of {@link #createAndProvisionManagedProfile( + * ManagedProfileProvisioningParams, String)} after all the other provisioning tasks + * have completed successfully. + * + * <p>The logic in this method blocks provisioning. + * + * <p>This method is meant to be overridden by OEMs. + */ + private void onCreateAndProvisionManagedProfileCompleted( + ManagedProfileProvisioningParams provisioningParams) {} + private void resetInteractAcrossProfilesAppOps() { mInjector.getCrossProfileApps().clearInteractAcrossProfilesAppOps(); pregrantDefaultInteractAcrossProfilesAppOps(); @@ -17978,6 +18012,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ERROR_PRE_CONDITION_FAILED, "Provisioning preconditions failed with result: " + result); } + onProvisionFullyManagedDeviceStarted(provisioningParams); setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime()); setLocale(provisioningParams.getLocale()); @@ -18003,6 +18038,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { disallowAddUser(); setAdminCanGrantSensorsPermissionForUserUnchecked(deviceOwnerUserId, provisioningParams.canDeviceOwnerGrantSensorsPermissions()); + onProvisionFullyManagedDeviceCompleted(provisioningParams); sendProvisioningCompletedBroadcast( deviceOwnerUserId, ACTION_PROVISION_MANAGED_DEVICE, @@ -18018,6 +18054,29 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + /** + * Callback called at the beginning of {@link #provisionFullyManagedDevice( + * FullyManagedDeviceProvisioningParams, String)} after the relevant prechecks have passed. + * + * <p>The logic in this method blocks provisioning. + * + * <p>This method is meant to be overridden by OEMs. + */ + private void onProvisionFullyManagedDeviceStarted( + FullyManagedDeviceProvisioningParams provisioningParams) {} + + /** + * Callback called at the end of {@link #provisionFullyManagedDevice( + * FullyManagedDeviceProvisioningParams, String)} after all the other provisioning tasks + * have completed successfully. + * + * <p>The logic in this method blocks provisioning. + * + * <p>This method is meant to be overridden by OEMs. + */ + private void onProvisionFullyManagedDeviceCompleted( + FullyManagedDeviceProvisioningParams provisioningParams) {} + private void setTimeAndTimezone(String timeZone, long localTime) { try { final AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index aad1bf8ab7c3..c9aeabd17191 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -176,6 +176,7 @@ import com.android.server.power.hint.HintManagerService; import com.android.server.powerstats.PowerStatsService; import com.android.server.profcollect.ProfcollectForwardingService; import com.android.server.recoverysystem.RecoverySystemService; +import com.android.server.resources.ResourcesManagerService; import com.android.server.restrictions.RestrictionsManagerService; import com.android.server.role.RoleServicePlatformHelper; import com.android.server.rotationresolver.RotationResolverManagerService; @@ -1285,6 +1286,13 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(new OverlayManagerService(mSystemContext)); t.traceEnd(); + // Manages Resources packages + t.traceBegin("StartResourcesManagerService"); + ResourcesManagerService resourcesService = new ResourcesManagerService(mSystemContext); + resourcesService.setActivityManagerService(mActivityManagerService); + mSystemServiceManager.startService(resourcesService); + t.traceEnd(); + t.traceBegin("StartSensorPrivacyService"); mSystemServiceManager.startService(new SensorPrivacyService(mSystemContext)); t.traceEnd(); diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java index d5e4710095f2..317a51b3fe2d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java @@ -26,6 +26,7 @@ import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -33,6 +34,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; +import android.Manifest; import android.annotation.Nullable; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; @@ -83,6 +85,7 @@ import org.mockito.quality.Strictness; import java.util.ArrayList; import java.util.HashMap; +import java.util.Objects; /** @@ -121,7 +124,7 @@ public final class GameServiceProviderInstanceImplTest { private WindowManagerInternal mMockWindowManagerInternal; @Mock private IActivityManager mMockActivityManager; - private FakeContext mFakeContext; + private MockContext mMockContext; private FakeGameClassifier mFakeGameClassifier; private FakeGameService mFakeGameService; private FakeServiceConnector<IGameService> mFakeGameServiceConnector; @@ -140,7 +143,7 @@ public final class GameServiceProviderInstanceImplTest { .strictness(Strictness.LENIENT) .startMocking(); - mFakeContext = new FakeContext(InstrumentationRegistry.getInstrumentation().getContext()); + mMockContext = new MockContext(InstrumentationRegistry.getInstrumentation().getContext()); mFakeGameClassifier = new FakeGameClassifier(); mFakeGameClassifier.recordGamePackage(GAME_A_PACKAGE); @@ -169,7 +172,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance = new GameServiceProviderInstanceImpl( new UserHandle(USER_ID), ConcurrentUtils.DIRECT_EXECUTOR, - mFakeContext, + mMockContext, mFakeGameClassifier, mMockActivityManager, mMockActivityTaskManager, @@ -301,6 +304,7 @@ public final class GameServiceProviderInstanceImplTest { throws Exception { mGameServiceProviderInstance.start(); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty(); @@ -322,6 +326,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSessionService.CapturedCreateInvocation capturedCreateInvocation = @@ -336,6 +341,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty(); @@ -345,6 +351,7 @@ public final class GameServiceProviderInstanceImplTest { public void gameTaskStartedAndSessionRequested_createsGameSession() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -362,7 +369,9 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); CreateGameSessionRequest expectedCreateGameSessionRequest = new CreateGameSessionRequest(10, @@ -376,6 +385,7 @@ public final class GameServiceProviderInstanceImplTest { public void gameSessionSuccessfullyCreated_createsTaskOverlay() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -391,6 +401,7 @@ public final class GameServiceProviderInstanceImplTest { public void gameTaskFocused_propagatedToGameSession() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -416,6 +427,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -432,6 +444,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); dispatchTaskRemoved(10); @@ -449,6 +462,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -466,6 +480,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -486,6 +501,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -513,6 +529,7 @@ public final class GameServiceProviderInstanceImplTest { startTask(10, GAME_A_MAIN_ACTIVITY); startTask(11, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -530,6 +547,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -557,6 +575,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -586,6 +605,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -619,10 +639,19 @@ public final class GameServiceProviderInstanceImplTest { } @Test + public void createGameSession_failurePermissionDenied() throws Exception { + mGameServiceProviderInstance.start(); + startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionDenied(Manifest.permission.MANAGE_GAME_ACTIVITY); + assertThrows(SecurityException.class, () -> mFakeGameService.requestCreateGameSession(10)); + } + + @Test public void stop_severalActiveGameSessions_destroysGameSessionsAndUnbinds() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -650,6 +679,7 @@ public final class GameServiceProviderInstanceImplTest { public void takeScreenshot_failureNoBitmapCaptured() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); IGameSessionController gameSessionController = getOnlyElement( @@ -669,6 +699,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); IGameSessionController gameSessionController = getOnlyElement( @@ -683,6 +714,7 @@ public final class GameServiceProviderInstanceImplTest { @Test public void restartGame_taskIdAssociatedWithGame_restartsTargetGame() throws Exception { + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); Intent launchIntent = new Intent("com.test.ACTION_LAUNCH_GAME_PACKAGE") .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); when(mMockPackageManager.getLaunchIntentForPackage(GAME_A_PACKAGE)) @@ -691,6 +723,7 @@ public final class GameServiceProviderInstanceImplTest { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -710,14 +743,16 @@ public final class GameServiceProviderInstanceImplTest { .mGameSessionController.restartGame(10); verify(mMockActivityManager).forceStopPackage(GAME_A_PACKAGE, UserHandle.USER_CURRENT); - assertThat(mFakeContext.getLastStartedIntent()).isEqualTo(launchIntent); + assertThat(mMockContext.getLastStartedIntent()).isEqualTo(launchIntent); } @Test public void restartGame_taskIdNotAssociatedWithGame_noOp() throws Exception { + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); @@ -730,7 +765,20 @@ public final class GameServiceProviderInstanceImplTest { .mGameSessionController.restartGame(11); verifyZeroInteractions(mMockActivityManager); - assertThat(mFakeContext.getLastStartedIntent()).isNull(); + assertThat(mMockContext.getLastStartedIntent()).isNull(); + } + + @Test + public void restartGame_failurePermissionDenied() throws Exception { + mGameServiceProviderInstance.start(); + startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); + mFakeGameService.requestCreateGameSession(10); + IGameSessionController gameSessionController = Objects.requireNonNull(getOnlyElement( + mFakeGameSessionService.getCapturedCreateInvocations())).mGameSessionController; + mockPermissionDenied(Manifest.permission.MANAGE_GAME_ACTIVITY); + assertThrows(SecurityException.class, + () -> gameSessionController.restartGame(10)); } private void startTask(int taskId, ComponentName componentName) { @@ -774,6 +822,14 @@ public final class GameServiceProviderInstanceImplTest { } } + private void mockPermissionGranted(String permission) { + mMockContext.setPermission(permission, PackageManager.PERMISSION_GRANTED); + } + + private void mockPermissionDenied(String permission) { + mMockContext.setPermission(permission, PackageManager.PERMISSION_DENIED); + } + static final class FakeGameService extends IGameService.Stub { private IGameServiceController mGameServiceController; @@ -900,13 +956,28 @@ public final class GameServiceProviderInstanceImplTest { } } - private final class FakeContext extends ContextWrapper { + private final class MockContext extends ContextWrapper { private Intent mLastStartedIntent; + // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant + private final HashMap<String, Integer> mMockedPermissions = new HashMap<>(); - FakeContext(Context base) { + MockContext(Context base) { super(base); } + /** + * Mock checks for the specified permission, and have them behave as per {@code granted}. + * + * <p>Passing null reverts to default behavior, which does a real permission check on the + * test package. + * + * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or + * {@link PackageManager#PERMISSION_DENIED}. + */ + public void setPermission(String permission, Integer granted) { + mMockedPermissions.put(permission, granted); + } + @Override public PackageManager getPackageManager() { return mMockPackageManager; @@ -919,7 +990,15 @@ public final class GameServiceProviderInstanceImplTest { @Override public void enforceCallingPermission(String permission, @Nullable String message) { - // Do nothing. + final Integer granted = mMockedPermissions.get(permission); + if (granted == null) { + super.enforceCallingOrSelfPermission(permission, message); + return; + } + + if (!granted.equals(PackageManager.PERMISSION_GRANTED)) { + throw new SecurityException("[Test] permission denied: " + permission); + } } Intent getLastStartedIntent() { diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java index bdfdf7723c02..64657a91224a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java @@ -50,7 +50,7 @@ import android.util.IntArray; import android.util.SparseArray; import com.android.dx.mockito.inline.extended.ExtendedMockito; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import com.android.internal.os.BackgroundThread; import com.android.internal.util.Preconditions; @@ -101,12 +101,12 @@ public class StagingManagerTest { mMockitoSession = ExtendedMockito.mockitoSession() .strictness(Strictness.LENIENT) .mockStatic(SystemProperties.class) - .mockStatic(PackageHelper.class) + .mockStatic(InstallLocationUtils.class) .startMocking(); when(mStorageManager.supportsCheckpoint()).thenReturn(true); when(mStorageManager.needsCheckpoint()).thenReturn(true); - when(PackageHelper.getStorageManager()).thenReturn(mStorageManager); + when(InstallLocationUtils.getStorageManager()).thenReturn(mStorageManager); when(SystemProperties.get(eq("ro.apex.updatable"))).thenReturn("true"); when(SystemProperties.get(eq("ro.apex.updatable"), anyString())).thenReturn("true"); diff --git a/services/tests/servicestests/src/com/android/server/DockObserverTest.java b/services/tests/servicestests/src/com/android/server/DockObserverTest.java new file mode 100644 index 000000000000..c325778a5683 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/DockObserverTest.java @@ -0,0 +1,134 @@ +/* + * 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; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Intent; +import android.os.Looper; +import android.testing.AndroidTestingRunner; +import android.testing.TestableContext; +import android.testing.TestableLooper; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.internal.R; +import com.android.internal.util.test.BroadcastInterceptingContext; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.ExecutionException; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class DockObserverTest { + + @Rule + public TestableContext mContext = + new TestableContext(ApplicationProvider.getApplicationContext(), null); + + private final BroadcastInterceptingContext mInterceptingContext = + new BroadcastInterceptingContext(mContext); + + BroadcastInterceptingContext.FutureIntent updateExtconDockState(DockObserver observer, + String extconDockState) { + BroadcastInterceptingContext.FutureIntent futureIntent = + mInterceptingContext.nextBroadcastIntent(Intent.ACTION_DOCK_EVENT); + observer.setDockStateFromProviderForTesting( + DockObserver.ExtconStateProvider.fromString(extconDockState)); + TestableLooper.get(this).processAllMessages(); + return futureIntent; + } + + DockObserver observerWithMappingConfig(String[] configEntries) { + mContext.getOrCreateTestableResources().addOverride( + R.array.config_dockExtconStateMapping, + configEntries); + return new DockObserver(mInterceptingContext); + } + + void assertDockEventIntentWithExtraThenUndock(DockObserver observer, String extconDockState, + int expectedExtra) throws ExecutionException, InterruptedException { + assertThat(updateExtconDockState(observer, extconDockState) + .get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1)) + .isEqualTo(expectedExtra); + assertThat(updateExtconDockState(observer, "DOCK=0") + .get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1)) + .isEqualTo(Intent.EXTRA_DOCK_STATE_UNDOCKED); + } + + @Before + public void setUp() { + if (Looper.myLooper() == null) { + Looper.prepare(); + } + } + + @Test + public void testDockIntentBroadcast_onlyAfterBootReady() + throws ExecutionException, InterruptedException { + DockObserver observer = new DockObserver(mInterceptingContext); + BroadcastInterceptingContext.FutureIntent futureIntent = + updateExtconDockState(observer, "DOCK=1"); + updateExtconDockState(observer, "DOCK=1").assertNotReceived(); + // Last boot phase reached + observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); + TestableLooper.get(this).processAllMessages(); + assertThat(futureIntent.get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1)) + .isEqualTo(Intent.EXTRA_DOCK_STATE_DESK); + } + + @Test + public void testDockIntentBroadcast_customConfigResource() + throws ExecutionException, InterruptedException { + DockObserver observer = observerWithMappingConfig( + new String[] {"2,KEY1=1,KEY2=2", "3,KEY3=3"}); + observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); + + // Mapping should not match + assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1", + Intent.EXTRA_DOCK_STATE_DESK); + assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY1=1", + Intent.EXTRA_DOCK_STATE_DESK); + assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY2=2", + Intent.EXTRA_DOCK_STATE_DESK); + + // 1st mapping now matches + assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY2=2\nKEY1=1", + Intent.EXTRA_DOCK_STATE_CAR); + + // 2nd mapping now matches + assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY3=3", + Intent.EXTRA_DOCK_STATE_LE_DESK); + } + + @Test + public void testDockIntentBroadcast_customConfigResourceWithWildcard() + throws ExecutionException, InterruptedException { + DockObserver observer = observerWithMappingConfig(new String[] { + "2,KEY2=2", + "3,KEY3=3", + "4" + }); + observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); + assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY5=5", + Intent.EXTRA_DOCK_STATE_HE_DESK); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java index ec884f59014f..5746f6f2d446 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java @@ -19,18 +19,25 @@ package com.android.server.biometrics.log; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.app.StatusBarManager; import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.common.OperationReason; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.logging.InstanceId; +import com.android.internal.statusbar.ISessionListener; import com.android.internal.statusbar.IStatusBarService; import com.google.common.collect.ImmutableList; @@ -56,6 +63,10 @@ public class BiometricContextProviderTest { @Mock private IStatusBarService mStatusBarService; + @Mock + private ISessionListener mSessionListener; + @Mock + private AmbientDisplayConfiguration mAmbientDisplayConfiguration; private OperationContext mOpContext = new OperationContext(); private IBiometricContextListener mListener; @@ -63,11 +74,17 @@ public class BiometricContextProviderTest { @Before public void setup() throws RemoteException { - mProvider = new BiometricContextProvider(mStatusBarService, null /* handler */); + when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true); + mProvider = new BiometricContextProvider(mAmbientDisplayConfiguration, mStatusBarService, + null /* handler */); ArgumentCaptor<IBiometricContextListener> captor = ArgumentCaptor.forClass(IBiometricContextListener.class); verify(mStatusBarService).setBiometicContextListener(captor.capture()); mListener = captor.getValue(); + ArgumentCaptor<ISessionListener> sessionCaptor = + ArgumentCaptor.forClass(ISessionListener.class); + verify(mStatusBarService).registerSessionListener(anyInt(), sessionCaptor.capture()); + mSessionListener = sessionCaptor.getValue(); } @Test @@ -76,6 +93,12 @@ public class BiometricContextProviderTest { assertThat(mProvider.isAoD()).isTrue(); mListener.onDozeChanged(false); assertThat(mProvider.isAoD()).isFalse(); + + when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(false); + mListener.onDozeChanged(true); + assertThat(mProvider.isAoD()).isFalse(); + mListener.onDozeChanged(false); + assertThat(mProvider.isAoD()).isFalse(); } @Test @@ -112,4 +135,84 @@ public class BiometricContextProviderTest { verify(emptyConsumer, never()).accept(any()); verify(nonEmptyConsumer).accept(same(mOpContext)); } + + @Test + public void testSessionId() throws RemoteException { + final int keyguardSessionId = 10; + final int bpSessionId = 20; + + assertThat(mProvider.getBiometricPromptSessionId()).isNull(); + assertThat(mProvider.getKeyguardEntrySessionId()).isNull(); + + mSessionListener.onSessionStarted(StatusBarManager.SESSION_KEYGUARD, + InstanceId.fakeInstanceId(keyguardSessionId)); + + assertThat(mProvider.getBiometricPromptSessionId()).isNull(); + assertThat(mProvider.getKeyguardEntrySessionId()).isEqualTo(keyguardSessionId); + + mSessionListener.onSessionStarted(StatusBarManager.SESSION_BIOMETRIC_PROMPT, + InstanceId.fakeInstanceId(bpSessionId)); + + assertThat(mProvider.getBiometricPromptSessionId()).isEqualTo(bpSessionId); + assertThat(mProvider.getKeyguardEntrySessionId()).isEqualTo(keyguardSessionId); + + mSessionListener.onSessionEnded(StatusBarManager.SESSION_KEYGUARD, + InstanceId.fakeInstanceId(keyguardSessionId)); + + assertThat(mProvider.getBiometricPromptSessionId()).isEqualTo(bpSessionId); + assertThat(mProvider.getKeyguardEntrySessionId()).isNull(); + + mSessionListener.onSessionEnded(StatusBarManager.SESSION_BIOMETRIC_PROMPT, + InstanceId.fakeInstanceId(bpSessionId)); + + assertThat(mProvider.getBiometricPromptSessionId()).isNull(); + assertThat(mProvider.getKeyguardEntrySessionId()).isNull(); + } + + @Test + public void testUpdate() throws RemoteException { + mListener.onDozeChanged(false); + OperationContext context = mProvider.updateContext(mOpContext, false /* crypto */); + + // default state when nothing has been set + assertThat(context).isSameInstanceAs(mOpContext); + assertThat(mOpContext.id).isEqualTo(0); + assertThat(mOpContext.reason).isEqualTo(OperationReason.UNKNOWN); + assertThat(mOpContext.isAoD).isEqualTo(false); + assertThat(mOpContext.isCrypto).isEqualTo(false); + + for (int type : List.of(StatusBarManager.SESSION_BIOMETRIC_PROMPT, + StatusBarManager.SESSION_KEYGUARD)) { + final int id = 40 + type; + final boolean aod = (type & 1) == 0; + + mListener.onDozeChanged(aod); + mSessionListener.onSessionStarted(type, InstanceId.fakeInstanceId(id)); + context = mProvider.updateContext(mOpContext, false /* crypto */); + assertThat(context).isSameInstanceAs(mOpContext); + assertThat(mOpContext.id).isEqualTo(id); + assertThat(mOpContext.reason).isEqualTo(reason(type)); + assertThat(mOpContext.isAoD).isEqualTo(aod); + assertThat(mOpContext.isCrypto).isEqualTo(false); + + mSessionListener.onSessionEnded(type, InstanceId.fakeInstanceId(id)); + } + + context = mProvider.updateContext(mOpContext, false /* crypto */); + assertThat(context).isSameInstanceAs(mOpContext); + assertThat(mOpContext.id).isEqualTo(0); + assertThat(mOpContext.reason).isEqualTo(OperationReason.UNKNOWN); + assertThat(mOpContext.isAoD).isEqualTo(false); + assertThat(mOpContext.isCrypto).isEqualTo(false); + } + + private static byte reason(int type) { + if (type == StatusBarManager.SESSION_BIOMETRIC_PROMPT) { + return OperationReason.BIOMETRIC_PROMPT; + } + if (type == StatusBarManager.SESSION_KEYGUARD) { + return OperationReason.KEYGUARD; + } + return OperationReason.UNKNOWN; + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java index b0eb810b5da9..fe023374ba88 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java @@ -31,6 +31,7 @@ import android.hardware.Sensor; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.common.OperationContext; import android.hardware.input.InputSensorInfo; import android.platform.test.annotations.Presubmit; import android.testing.TestableContext; @@ -68,10 +69,12 @@ public class BiometricLoggerTest { @Mock private BaseClientMonitor mClient; + private OperationContext mOpContext; private BiometricLogger mLogger; @Before public void setUp() { + mOpContext = new OperationContext(); mContext.addMockSystemService(SensorManager.class, mSensorManager); when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn( new Sensor(new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0, @@ -93,14 +96,13 @@ public class BiometricLoggerTest { final int acquiredInfo = 2; final int vendorCode = 3; - final boolean isCrypto = true; final int targetUserId = 9; - mLogger.logOnAcquired(mContext, acquiredInfo, vendorCode, isCrypto, targetUserId); + mLogger.logOnAcquired(mContext, mOpContext, acquiredInfo, vendorCode, targetUserId); - verify(mSink).acquired( + verify(mSink).acquired(eq(mOpContext), eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(), - eq(acquiredInfo), eq(vendorCode), eq(isCrypto), eq(targetUserId)); + eq(acquiredInfo), eq(vendorCode), eq(targetUserId)); } @Test @@ -109,17 +111,16 @@ public class BiometricLoggerTest { final boolean authenticated = true; final boolean requireConfirmation = false; - final boolean isCrypto = false; final int targetUserId = 11; final boolean isBiometricPrompt = true; - mLogger.logOnAuthenticated(mContext, - authenticated, requireConfirmation, isCrypto, targetUserId, isBiometricPrompt); + mLogger.logOnAuthenticated(mContext, mOpContext, + authenticated, requireConfirmation, targetUserId, isBiometricPrompt); - verify(mSink).authenticate( + verify(mSink).authenticate(eq(mOpContext), eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(), - anyLong(), eq(authenticated), anyInt(), eq(requireConfirmation), eq(isCrypto), - eq(targetUserId), eq(isBiometricPrompt), anyFloat()); + anyLong(), anyInt(), eq(requireConfirmation), + eq(targetUserId), anyFloat()); } @Test @@ -143,14 +144,13 @@ public class BiometricLoggerTest { final int error = 7; final int vendorCode = 11; - final boolean isCrypto = false; final int targetUserId = 9; - mLogger.logOnError(mContext, error, vendorCode, isCrypto, targetUserId); + mLogger.logOnError(mContext, mOpContext, error, vendorCode, targetUserId); - verify(mSink).error( + verify(mSink).error(eq(mOpContext), eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(), - anyLong(), eq(error), eq(vendorCode), eq(isCrypto), eq(targetUserId)); + anyLong(), eq(error), eq(vendorCode), eq(targetUserId)); } @Test @@ -175,38 +175,34 @@ public class BiometricLoggerTest { private void testDisabledMetrics(boolean isBadConfig) { mLogger.disableMetrics(); - mLogger.logOnAcquired(mContext, + mLogger.logOnAcquired(mContext, mOpContext, 0 /* acquiredInfo */, 1 /* vendorCode */, - true /* isCrypto */, 8 /* targetUserId */); - mLogger.logOnAuthenticated(mContext, + mLogger.logOnAuthenticated(mContext, mOpContext, true /* authenticated */, true /* requireConfirmation */, - false /* isCrypto */, 4 /* targetUserId */, true/* isBiometricPrompt */); mLogger.logOnEnrolled(2 /* targetUserId */, 10 /* latency */, true /* enrollSuccessful */); - mLogger.logOnError(mContext, + mLogger.logOnError(mContext, mOpContext, 4 /* error */, 0 /* vendorCode */, - false /* isCrypto */, 6 /* targetUserId */); - verify(mSink, never()).acquired( + verify(mSink, never()).acquired(eq(mOpContext), anyInt(), anyInt(), anyInt(), anyBoolean(), - anyInt(), anyInt(), anyBoolean(), anyInt()); - verify(mSink, never()).authenticate( + anyInt(), anyInt(), anyInt()); + verify(mSink, never()).authenticate(eq(mOpContext), anyInt(), anyInt(), anyInt(), anyBoolean(), - anyLong(), anyBoolean(), anyInt(), anyBoolean(), - anyBoolean(), anyInt(), anyBoolean(), anyFloat()); + anyLong(), anyInt(), anyBoolean(), anyInt(), anyFloat()); verify(mSink, never()).enroll( anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyBoolean(), anyFloat()); - verify(mSink, never()).error( + verify(mSink, never()).error(eq(mOpContext), anyInt(), anyInt(), anyInt(), anyBoolean(), - anyLong(), anyInt(), anyInt(), anyBoolean(), anyInt()); + anyLong(), anyInt(), anyInt(), anyInt()); mLogger.logUnknownEnrollmentInFramework(); mLogger.logUnknownEnrollmentInHal(); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java index 25585dd0cfc3..aba93b058669 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java @@ -16,12 +16,13 @@ package com.android.server.biometrics.sensors.face.aidl; -import static com.google.common.truth.Truth.assertThat; - import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -47,6 +48,7 @@ import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -57,7 +59,6 @@ public class FaceAuthenticationClientTest { private static final int USER_ID = 12; private static final long OP_ID = 32; - private static final boolean HAS_AOD = true; @Rule public final TestableContext mContext = new TestableContext( @@ -89,7 +90,8 @@ public class FaceAuthenticationClientTest { @Before public void setup() { - when(mBiometricContext.isAoD()).thenReturn(HAS_AOD); + when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer( + i -> i.getArgument(0)); } @Test @@ -106,11 +108,12 @@ public class FaceAuthenticationClientTest { final FaceAuthenticationClient client = createClient(2); client.start(mCallback); - verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture()); + InOrder order = inOrder(mHal, mBiometricContext); + order.verify(mBiometricContext).updateContext( + mOperationContextCaptor.capture(), anyBoolean()); + order.verify(mHal).authenticateWithContext( + eq(OP_ID), same(mOperationContextCaptor.getValue())); verify(mHal, never()).authenticate(anyLong()); - - final OperationContext opContext = mOperationContextCaptor.getValue(); - assertThat(opContext.isAoD).isEqualTo(HAS_AOD); } private FaceAuthenticationClient createClient(int version) throws RemoteException { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java index 6c72ebfd3674..25135c6670db 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java @@ -16,10 +16,11 @@ package com.android.server.biometrics.sensors.face.aidl; -import static com.google.common.truth.Truth.assertThat; - import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -43,6 +44,7 @@ import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -52,7 +54,6 @@ import org.mockito.junit.MockitoRule; public class FaceDetectClientTest { private static final int USER_ID = 12; - private static final boolean HAS_AOD = true; @Rule public final TestableContext mContext = new TestableContext( @@ -80,7 +81,8 @@ public class FaceDetectClientTest { @Before public void setup() { - when(mBiometricContext.isAoD()).thenReturn(HAS_AOD); + when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer( + i -> i.getArgument(0)); } @Test @@ -97,11 +99,11 @@ public class FaceDetectClientTest { final FaceDetectClient client = createClient(2); client.start(mCallback); - verify(mHal).detectInteractionWithContext(mOperationContextCaptor.capture()); + InOrder order = inOrder(mHal, mBiometricContext); + order.verify(mBiometricContext).updateContext( + mOperationContextCaptor.capture(), anyBoolean()); + order.verify(mHal).detectInteractionWithContext(same(mOperationContextCaptor.getValue())); verify(mHal, never()).detectInteraction(); - - final OperationContext opContext = mOperationContextCaptor.getValue(); - assertThat(opContext.isAoD).isEqualTo(HAS_AOD); } private FaceDetectClient createClient(int version) throws RemoteException { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java index 22070e9579ee..38e048bc1ba7 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java @@ -17,11 +17,15 @@ package com.android.server.biometrics.sensors.face.aidl; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyByte; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.face.ISession; import android.hardware.face.Face; import android.os.IBinder; @@ -38,8 +42,12 @@ import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -71,10 +79,18 @@ public class FaceEnrollClientTest { private ClientMonitorCallback mCallback; @Mock private Sensor.HalSessionCallback mHalSessionCallback; + @Captor + private ArgumentCaptor<OperationContext> mOperationContextCaptor; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + @Before + public void setup() { + when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer( + i -> i.getArgument(0)); + } + @Test public void enrollNoContext_v1() throws RemoteException { final FaceEnrollClient client = createClient(1); @@ -89,7 +105,11 @@ public class FaceEnrollClientTest { final FaceEnrollClient client = createClient(2); client.start(mCallback); - verify(mHal).enrollWithContext(any(), anyByte(), any(), any(), any()); + InOrder order = inOrder(mHal, mBiometricContext); + order.verify(mBiometricContext).updateContext( + mOperationContextCaptor.capture(), anyBoolean()); + order.verify(mHal).enrollWithContext(any(), anyByte(), any(), any(), + same(mOperationContextCaptor.getValue())); verify(mHal, never()).enroll(any(), anyByte(), any(), any()); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java index 0ac00aafbf6c..12b8264fc20c 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java @@ -37,6 +37,7 @@ import androidx.annotation.NonNull; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.LockoutResetDispatcher; @@ -60,6 +61,8 @@ public class FaceProviderTest { private UserManager mUserManager; @Mock private IFace mDaemon; + @Mock + private BiometricContext mBiometricContext; private SensorProps[] mSensorProps; private LockoutResetDispatcher mLockoutResetDispatcher; @@ -89,7 +92,7 @@ public class FaceProviderTest { mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); mFaceProvider = new TestableFaceProvider(mDaemon, mContext, mSensorProps, TAG, - mLockoutResetDispatcher); + mLockoutResetDispatcher, mBiometricContext); } @SuppressWarnings("rawtypes") @@ -139,8 +142,9 @@ public class FaceProviderTest { @NonNull Context context, @NonNull SensorProps[] props, @NonNull String halInstanceName, - @NonNull LockoutResetDispatcher lockoutResetDispatcher) { - super(context, props, halInstanceName, lockoutResetDispatcher); + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull BiometricContext biometricContext) { + super(context, props, halInstanceName, lockoutResetDispatcher, biometricContext); mDaemon = daemon; } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java index 21a7a8ae65b9..116d2d5a66a0 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java @@ -41,6 +41,7 @@ import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.LockoutResetDispatcher; @@ -70,6 +71,8 @@ public class Face10Test { private UserManager mUserManager; @Mock private BiometricScheduler mScheduler; + @Mock + private BiometricContext mBiometricContext; private final Handler mHandler = new Handler(Looper.getMainLooper()); private LockoutResetDispatcher mLockoutResetDispatcher; @@ -100,7 +103,8 @@ public class Face10Test { resetLockoutRequiresChallenge); Face10.sSystemClock = Clock.fixed(Instant.ofEpochMilli(100), ZoneId.of("PST")); - mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mHandler, mScheduler); + mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mHandler, mScheduler, + mBiometricContext); mBinder = new Binder(); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java index 5c360e9147c1..de0f038e8ec5 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java @@ -24,7 +24,9 @@ import static org.mockito.Mockito.anyFloat; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.same; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -56,6 +58,7 @@ import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -68,7 +71,6 @@ public class FingerprintAuthenticationClientTest { private static final int USER_ID = 8; private static final long OP_ID = 7; - private static final boolean HAS_AOD = true; private static final int POINTER_ID = 0; private static final int TOUCH_X = 8; private static final int TOUCH_Y = 20; @@ -107,6 +109,8 @@ public class FingerprintAuthenticationClientTest { private ArgumentCaptor<OperationContext> mOperationContextCaptor; @Captor private ArgumentCaptor<PointerContext> mPointerContextCaptor; + @Captor + private ArgumentCaptor<Consumer<OperationContext>> mContextInjector; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -115,7 +119,8 @@ public class FingerprintAuthenticationClientTest { public void setup() { when(mBiometricLogger.createALSCallback(anyBoolean())).thenAnswer(i -> new CallbackWithProbe<>(mLuxProbe, i.getArgument(0))); - when(mBiometricContext.isAoD()).thenReturn(HAS_AOD); + when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer( + i -> i.getArgument(0)); } @Test @@ -132,11 +137,12 @@ public class FingerprintAuthenticationClientTest { final FingerprintAuthenticationClient client = createClient(2); client.start(mCallback); - verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture()); + InOrder order = inOrder(mHal, mBiometricContext); + order.verify(mBiometricContext).updateContext( + mOperationContextCaptor.capture(), anyBoolean()); + order.verify(mHal).authenticateWithContext( + eq(OP_ID), same(mOperationContextCaptor.getValue())); verify(mHal, never()).authenticate(anyLong()); - - final OperationContext opContext = mOperationContextCaptor.getValue(); - assertThat(opContext.isAoD).isEqualTo(HAS_AOD); } @Test @@ -205,6 +211,23 @@ public class FingerprintAuthenticationClientTest { } @Test + public void notifyHalWhenContextChanges() throws RemoteException { + final FingerprintAuthenticationClient client = createClient(); + client.start(mCallback); + + verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture()); + OperationContext opContext = mOperationContextCaptor.getValue(); + + // fake an update to the context + verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture()); + mContextInjector.getValue().accept(opContext); + verify(mHal).onContextChanged(eq(opContext)); + + client.stopHalOperation(); + verify(mBiometricContext).unsubscribe(same(opContext)); + } + + @Test public void showHideOverlay_cancel() throws RemoteException { showHideOverlay(c -> c.cancel()); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java index 295fe4766a85..93cbef19aca9 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java @@ -16,8 +16,11 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -42,6 +45,7 @@ import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -51,7 +55,6 @@ import org.mockito.junit.MockitoRule; public class FingerprintDetectClientTest { private static final int USER_ID = 8; - private static final boolean HAS_AOD = true; @Rule public final TestableContext mContext = new TestableContext( @@ -81,7 +84,8 @@ public class FingerprintDetectClientTest { @Before public void setup() { - when(mBiometricContext.isAoD()).thenReturn(HAS_AOD); + when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer( + i -> i.getArgument(0)); } @Test @@ -100,7 +104,10 @@ public class FingerprintDetectClientTest { client.start(mCallback); - verify(mHal).detectInteractionWithContext(mOperationContextCaptor.capture()); + InOrder order = inOrder(mHal, mBiometricContext); + order.verify(mBiometricContext).updateContext( + mOperationContextCaptor.capture(), anyBoolean()); + order.verify(mHal).detectInteractionWithContext(same(mOperationContextCaptor.getValue())); verify(mHal, never()).detectInteraction(); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java index 7e44eab0a556..5a96f5cca52a 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java @@ -18,11 +18,15 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.same; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -44,14 +48,18 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.log.CallbackWithProbe; +import com.android.server.biometrics.log.Probe; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -96,14 +104,26 @@ public class FingerprintEnrollClientTest { private ClientMonitorCallback mCallback; @Mock private Sensor.HalSessionCallback mHalSessionCallback; + @Mock + private Probe mLuxProbe; @Captor private ArgumentCaptor<OperationContext> mOperationContextCaptor; @Captor private ArgumentCaptor<PointerContext> mPointerContextCaptor; + @Captor + private ArgumentCaptor<Consumer<OperationContext>> mContextInjector; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + @Before + public void setup() { + when(mBiometricLogger.createALSCallback(anyBoolean())).thenAnswer(i -> + new CallbackWithProbe<>(mLuxProbe, i.getArgument(0))); + when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer( + i -> i.getArgument(0)); + } + @Test public void enrollNoContext_v1() throws RemoteException { final FingerprintEnrollClient client = createClient(1); @@ -120,7 +140,10 @@ public class FingerprintEnrollClientTest { client.start(mCallback); - verify(mHal).enrollWithContext(any(), mOperationContextCaptor.capture()); + InOrder order = inOrder(mHal, mBiometricContext); + order.verify(mBiometricContext).updateContext( + mOperationContextCaptor.capture(), anyBoolean()); + order.verify(mHal).enrollWithContext(any(), same(mOperationContextCaptor.getValue())); verify(mHal, never()).enroll(any()); } @@ -172,6 +195,41 @@ public class FingerprintEnrollClientTest { } @Test + public void luxProbeWhenFingerDown() throws RemoteException { + final FingerprintEnrollClient client = createClient(); + client.start(mCallback); + + client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR); + verify(mLuxProbe).enable(); + + client.onAcquired(2, 0); + verify(mLuxProbe, never()).disable(); + + client.onPointerUp(); + verify(mLuxProbe).disable(); + + client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR); + verify(mLuxProbe, times(2)).enable(); + } + + @Test + public void notifyHalWhenContextChanges() throws RemoteException { + final FingerprintEnrollClient client = createClient(); + client.start(mCallback); + + verify(mHal).enrollWithContext(any(), mOperationContextCaptor.capture()); + OperationContext opContext = mOperationContextCaptor.getValue(); + + // fake an update to the context + verify(mBiometricContext).subscribe(eq(opContext), mContextInjector.capture()); + mContextInjector.getValue().accept(opContext); + verify(mHal).onContextChanged(eq(opContext)); + + client.stopHalOperation(); + verify(mBiometricContext).unsubscribe(same(opContext)); + } + + @Test public void showHideOverlay_cancel() throws RemoteException { showHideOverlay(c -> c.cancel()); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java index 73f1516562bc..5a1a02eb39c8 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java @@ -40,6 +40,7 @@ import androidx.annotation.NonNull; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.LockoutResetDispatcher; @@ -71,6 +72,8 @@ public class FingerprintProviderTest { private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; @Mock private FingerprintStateCallback mFingerprintStateCallback; + @Mock + private BiometricContext mBiometricContext; private SensorProps[] mSensorProps; private LockoutResetDispatcher mLockoutResetDispatcher; @@ -105,7 +108,7 @@ public class FingerprintProviderTest { mFingerprintProvider = new TestableFingerprintProvider(mDaemon, mContext, mFingerprintStateCallback, mSensorProps, TAG, mLockoutResetDispatcher, - mGestureAvailabilityDispatcher); + mGestureAvailabilityDispatcher, mBiometricContext); } @SuppressWarnings("rawtypes") @@ -157,9 +160,10 @@ public class FingerprintProviderTest { @NonNull SensorProps[] props, @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull BiometricContext biometricContext) { super(context, fingerprintStateCallback, props, halInstanceName, lockoutResetDispatcher, - gestureAvailabilityDispatcher); + gestureAvailabilityDispatcher, biometricContext); mDaemon = daemon; } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java index f6b92097a9fb..529f994f2773 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java @@ -39,6 +39,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.android.internal.R; +import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback; @@ -70,6 +71,8 @@ public class Fingerprint21Test { private BiometricScheduler mScheduler; @Mock private FingerprintStateCallback mFingerprintStateCallback; + @Mock + private BiometricContext mBiometricContext; private LockoutResetDispatcher mLockoutResetDispatcher; private Fingerprint21 mFingerprint21; @@ -101,7 +104,7 @@ public class Fingerprint21Test { mFingerprint21 = new TestableFingerprint21(mContext, mFingerprintStateCallback, sensorProps, mScheduler, new Handler(Looper.getMainLooper()), mLockoutResetDispatcher, - mHalResultController); + mHalResultController, mBiometricContext); } @Test @@ -126,9 +129,10 @@ public class Fingerprint21Test { @NonNull FingerprintSensorPropertiesInternal sensorProps, @NonNull BiometricScheduler scheduler, @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull HalResultController controller) { + @NonNull HalResultController controller, + @NonNull BiometricContext biometricContext) { super(context, fingerprintStateCallback, sensorProps, scheduler, handler, - lockoutResetDispatcher, controller); + lockoutResetDispatcher, controller, biometricContext); } @Override diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index ceb723a407f9..33540c874c0a 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -160,11 +160,11 @@ public class VirtualDeviceManagerServiceTest { mDeviceImpl.onVirtualDisplayCreatedLocked(displayId); verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(), nullable(String.class), nullable(String.class), nullable(WorkSource.class), - nullable(String.class), anyInt()); + nullable(String.class), anyInt(), eq(null)); TestableLooper.get(this).processAllMessages(); verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(), nullable(String.class), nullable(String.class), nullable(WorkSource.class), - nullable(String.class), eq(displayId)); + nullable(String.class), eq(displayId), eq(null)); } @Test @@ -177,7 +177,7 @@ public class VirtualDeviceManagerServiceTest { TestableLooper.get(this).processAllMessages(); verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(any(Binder.class), anyInt(), nullable(String.class), nullable(String.class), nullable(WorkSource.class), - nullable(String.class), eq(displayId)); + nullable(String.class), eq(displayId), eq(null)); } @Test @@ -196,7 +196,7 @@ public class VirtualDeviceManagerServiceTest { verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(), anyInt(), nullable(String.class), nullable(String.class), nullable(WorkSource.class), - nullable(String.class), eq(displayId)); + nullable(String.class), eq(displayId), eq(null)); IBinder wakeLock = wakeLockCaptor.getValue(); mDeviceImpl.onVirtualDisplayRemovedLocked(displayId); @@ -212,7 +212,7 @@ public class VirtualDeviceManagerServiceTest { verify(mIPowerManagerMock, Mockito.times(1)).acquireWakeLock(wakeLockCaptor.capture(), anyInt(), nullable(String.class), nullable(String.class), nullable(WorkSource.class), - nullable(String.class), eq(displayId)); + nullable(String.class), eq(displayId), eq(null)); IBinder wakeLock = wakeLockCaptor.getValue(); // Close the VirtualDevice without first notifying it of the VirtualDisplay removal. diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java index 0f3742f8577d..ac97911027bf 100644 --- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java +++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java @@ -59,6 +59,8 @@ import java.util.List; @RunWith(JUnit4.class) public final class AmbientLuxTest { + + private static final float ALLOWED_ERROR_DELTA = 0.001f; private static final int AMBIENT_COLOR_TYPE = 20705; private static final String AMBIENT_COLOR_TYPE_STR = "colorSensoryDensoryDoc"; private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5432.1f; @@ -78,6 +80,8 @@ public final class AmbientLuxTest { @Mock private TypedArray mHighLightBiases; @Mock private TypedArray mAmbientColorTemperatures; @Mock private TypedArray mDisplayColorTemperatures; + @Mock private TypedArray mStrongAmbientColorTemperatures; + @Mock private TypedArray mStrongDisplayColorTemperatures; @Mock private ColorDisplayService.ColorDisplayServiceInternal mColorDisplayServiceInternalMock; @Before @@ -110,6 +114,12 @@ public final class AmbientLuxTest { when(mResourcesSpy.obtainTypedArray( R.array.config_displayWhiteBalanceDisplayColorTemperatures)) .thenReturn(mDisplayColorTemperatures); + when(mResourcesSpy.obtainTypedArray( + R.array.config_displayWhiteBalanceStrongAmbientColorTemperatures)) + .thenReturn(mStrongAmbientColorTemperatures); + when(mResourcesSpy.obtainTypedArray( + R.array.config_displayWhiteBalanceStrongDisplayColorTemperatures)) + .thenReturn(mStrongDisplayColorTemperatures); when(mResourcesSpy.obtainTypedArray( R.array.config_displayWhiteBalanceLowLightAmbientBrightnesses)) @@ -375,6 +385,43 @@ public final class AmbientLuxTest { } @Test + public void testStrongMode() { + final float lowerBrightness = 10.0f; + final float upperBrightness = 50.0f; + setBrightnesses(lowerBrightness, upperBrightness); + setBiases(0.0f, 1.0f); + final int ambientColorTempLow = 6000; + final int ambientColorTempHigh = 8000; + final int displayColorTempLow = 6400; + final int displayColorTempHigh = 7400; + setStrongAmbientColorTemperatures(ambientColorTempLow, ambientColorTempHigh); + setStrongDisplayColorTemperatures(displayColorTempLow, displayColorTempHigh); + + DisplayWhiteBalanceController controller = + DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy); + controller.setStrongModeEnabled(true); + controller.mBrightnessFilter = spy(new AmbientFilterStubber()); + + for (float ambientTempFraction = 0.0f; ambientTempFraction <= 1.0f; + ambientTempFraction += 0.1f) { + final float ambientTemp = + (ambientColorTempHigh - ambientColorTempLow) * ambientTempFraction + + ambientColorTempLow; + setEstimatedColorTemperature(controller, ambientTemp); + for (float brightnessFraction = 0.0f; brightnessFraction <= 1.0f; + brightnessFraction += 0.1f) { + setEstimatedBrightnessAndUpdate(controller, + mix(lowerBrightness, upperBrightness, brightnessFraction)); + assertEquals(controller.mPendingAmbientColorTemperature, + mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, + mix(displayColorTempLow, displayColorTempHigh, ambientTempFraction), + brightnessFraction), + ALLOWED_ERROR_DELTA); + } + } + } + + @Test public void testLowLight_DefaultAmbient() throws Exception { final float lowerBrightness = 10.0f; final float upperBrightness = 50.0f; @@ -486,6 +533,14 @@ public final class AmbientLuxTest { setFloatArrayResource(mDisplayColorTemperatures, vals); } + private void setStrongAmbientColorTemperatures(float... vals) { + setFloatArrayResource(mStrongAmbientColorTemperatures, vals); + } + + private void setStrongDisplayColorTemperatures(float... vals) { + setFloatArrayResource(mStrongDisplayColorTemperatures, vals); + } + private void setFloatArrayResource(TypedArray array, float[] vals) { when(array.length()).thenReturn(vals.length); for (int i = 0; i < vals.length; i++) { diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java index b621a4408f40..869ac8877d6d 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerTests.java @@ -70,7 +70,7 @@ import androidx.test.filters.SmallTest; import androidx.test.filters.Suppress; import com.android.frameworks.servicestests.R; -import com.android.internal.content.PackageHelper; +import com.android.internal.content.InstallLocationUtils; import com.android.server.pm.pkg.parsing.ParsingPackage; import com.android.server.pm.pkg.parsing.ParsingPackageUtils; @@ -106,11 +106,11 @@ public class PackageManagerTests extends AndroidTestCase { private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec"; - private static final int APP_INSTALL_AUTO = PackageHelper.APP_INSTALL_AUTO; + private static final int APP_INSTALL_AUTO = InstallLocationUtils.APP_INSTALL_AUTO; - private static final int APP_INSTALL_DEVICE = PackageHelper.APP_INSTALL_INTERNAL; + private static final int APP_INSTALL_DEVICE = InstallLocationUtils.APP_INSTALL_INTERNAL; - private static final int APP_INSTALL_SDCARD = PackageHelper.APP_INSTALL_EXTERNAL; + private static final int APP_INSTALL_SDCARD = InstallLocationUtils.APP_INSTALL_EXTERNAL; void failStr(String errMsg) { Log.w(TAG, "errMsg=" + errMsg); @@ -1214,7 +1214,7 @@ public class PackageManagerTests extends AndroidTestCase { int origDefaultLoc = getDefaultInstallLoc(); InstallParams ip = null; try { - setInstallLoc(PackageHelper.APP_INSTALL_AUTO); + setInstallLoc(InstallLocationUtils.APP_INSTALL_AUTO); // Install first ip = installFromRawResource("install.apk", rawResId, installFlags, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); @@ -1303,7 +1303,7 @@ public class PackageManagerTests extends AndroidTestCase { InstallParams ip = null; try { PackageManager pm = getPm(); - setInstallLoc(PackageHelper.APP_INSTALL_AUTO); + setInstallLoc(InstallLocationUtils.APP_INSTALL_AUTO); // Install first ip = installFromRawResource("install.apk", R.raw.install, installFlags, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); @@ -1517,11 +1517,11 @@ public class PackageManagerTests extends AndroidTestCase { int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; boolean enable = getUserSettingSetInstallLocation(); if (enable) { - if (userSetting == PackageHelper.APP_INSTALL_AUTO) { + if (userSetting == InstallLocationUtils.APP_INSTALL_AUTO) { iloc = PackageInfo.INSTALL_LOCATION_AUTO; - } else if (userSetting == PackageHelper.APP_INSTALL_EXTERNAL) { + } else if (userSetting == InstallLocationUtils.APP_INSTALL_EXTERNAL) { iloc = PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL; - } else if (userSetting == PackageHelper.APP_INSTALL_INTERNAL) { + } else if (userSetting == InstallLocationUtils.APP_INSTALL_INTERNAL) { iloc = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY; } } @@ -1552,7 +1552,7 @@ public class PackageManagerTests extends AndroidTestCase { } @LargeTest public void testExistingIUserI() throws Exception { - int userSetting = PackageHelper.APP_INSTALL_INTERNAL; + int userSetting = InstallLocationUtils.APP_INSTALL_INTERNAL; int iFlags = PackageManager.INSTALL_INTERNAL; setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); } @@ -1564,14 +1564,14 @@ public class PackageManagerTests extends AndroidTestCase { return; } - int userSetting = PackageHelper.APP_INSTALL_EXTERNAL; + int userSetting = InstallLocationUtils.APP_INSTALL_EXTERNAL; int iFlags = PackageManager.INSTALL_INTERNAL; setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); } @LargeTest public void testExistingIUserA() throws Exception { - int userSetting = PackageHelper.APP_INSTALL_AUTO; + int userSetting = InstallLocationUtils.APP_INSTALL_AUTO; int iFlags = PackageManager.INSTALL_INTERNAL; setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); } @@ -1616,7 +1616,7 @@ public class PackageManagerTests extends AndroidTestCase { } @LargeTest public void testUserI() throws Exception { - int userSetting = PackageHelper.APP_INSTALL_INTERNAL; + int userSetting = InstallLocationUtils.APP_INSTALL_INTERNAL; int iloc = getExpectedInstallLocation(userSetting); setUserX(true, userSetting, iloc); } @@ -1628,14 +1628,14 @@ public class PackageManagerTests extends AndroidTestCase { return; } - int userSetting = PackageHelper.APP_INSTALL_EXTERNAL; + int userSetting = InstallLocationUtils.APP_INSTALL_EXTERNAL; int iloc = getExpectedInstallLocation(userSetting); setUserX(true, userSetting, iloc); } @LargeTest public void testUserA() throws Exception { - int userSetting = PackageHelper.APP_INSTALL_AUTO; + int userSetting = InstallLocationUtils.APP_INSTALL_AUTO; int iloc = getExpectedInstallLocation(userSetting); setUserX(true, userSetting, iloc); } @@ -1646,7 +1646,7 @@ public class PackageManagerTests extends AndroidTestCase { */ @LargeTest public void testUserPrefOffUserI() throws Exception { - int userSetting = PackageHelper.APP_INSTALL_INTERNAL; + int userSetting = InstallLocationUtils.APP_INSTALL_INTERNAL; int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; setUserX(false, userSetting, iloc); } @@ -1658,14 +1658,14 @@ public class PackageManagerTests extends AndroidTestCase { return; } - int userSetting = PackageHelper.APP_INSTALL_EXTERNAL; + int userSetting = InstallLocationUtils.APP_INSTALL_EXTERNAL; int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; setUserX(false, userSetting, iloc); } @LargeTest public void testUserPrefOffA() throws Exception { - int userSetting = PackageHelper.APP_INSTALL_AUTO; + int userSetting = InstallLocationUtils.APP_INSTALL_AUTO; int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; setUserX(false, userSetting, iloc); } diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 51dbd97c183b..827349ad433a 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -64,6 +64,7 @@ import android.os.BatterySaverPolicyConfig; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.IWakeLockCallback; import android.os.Looper; import android.os.PowerManager; import android.os.PowerSaveState; @@ -102,6 +103,7 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; @@ -479,21 +481,21 @@ public class PowerManagerServiceTest { // First, ensure that a normal full wake lock does not cause a wakeup int flags = PowerManager.FULL_WAKE_LOCK; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, - null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY); + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); // Ensure that the flag does *NOT* work with a partial wake lock. flags = PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, - null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY); + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); // Verify that flag forces a wakeup when paired to a FULL_WAKE_LOCK flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, - null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY); + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); } @@ -661,12 +663,12 @@ public class PowerManagerServiceTest { wakelockMap.put((String) inv.getArguments()[1], (int) inv.getArguments()[0]); return null; }).when(mNotifierMock).onWakeLockAcquired(anyInt(), anyString(), anyString(), anyInt(), - anyInt(), any(), any()); + anyInt(), any(), any(), any()); doAnswer(inv -> { wakelockMap.remove((String) inv.getArguments()[1]); return null; }).when(mNotifierMock).onWakeLockReleased(anyInt(), anyString(), anyString(), anyInt(), - anyInt(), any(), any()); + anyInt(), any(), any(), any()); // // TEST STARTS HERE @@ -679,7 +681,7 @@ public class PowerManagerServiceTest { // Create a wakelock mService.getBinderServiceInstance().acquireWakeLock(new Binder(), flags, tag, pkg, - null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY); + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); assertThat(wakelockMap.get(tag)).isEqualTo(flags); // Verify wakelock is active. // Confirm that the wakelocks have been disabled when the forceSuspend is in flight. @@ -737,7 +739,7 @@ public class PowerManagerServiceTest { // Take a nap and verify we no longer hold the blocker int flags = PowerManager.DOZE_WAKE_LOCK; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, - null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY); + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); when(mDreamManagerInternalMock.isDreaming()).thenReturn(true); mService.getBinderServiceInstance().goToSleep(mClock.now(), PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0); @@ -893,7 +895,7 @@ public class PowerManagerServiceTest { mService.getBinderServiceInstance().acquireWakeLock(token, PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg, - null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY); + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); advanceTime(60); @@ -919,7 +921,7 @@ public class PowerManagerServiceTest { mService.getBinderServiceInstance().acquireWakeLock(token, PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, tag, pkg, - null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY); + null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY, null); advanceTime(1500); mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); @@ -995,7 +997,7 @@ public class PowerManagerServiceTest { mService.getBinderServiceInstance().acquireWakeLock(token, PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg, - null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY); + null /* workSource */, null /* historyTag */, Display.DEFAULT_DISPLAY, null); assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo( @@ -1035,7 +1037,7 @@ public class PowerManagerServiceTest { mService.getBinderServiceInstance().acquireWakeLock(token, PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg, - null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY); + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo( @@ -1076,7 +1078,7 @@ public class PowerManagerServiceTest { mService.getBinderServiceInstance().acquireWakeLock(token, PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg, - null /* workSource */, null /* historyTag */, nonDefaultDisplay); + null /* workSource */, null /* historyTag */, nonDefaultDisplay, null); assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo( WAKEFULNESS_AWAKE); @@ -1640,7 +1642,65 @@ public class PowerManagerServiceTest { IBinder token = new Binder(); String packageName = "pkg.name"; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, - null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY); + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, + null /* callback */); return mService.findWakeLockLocked(token); } + + /** + * Test IPowerManager.acquireWakeLock() with a IWakeLockCallback. + */ + @Test + public void testNotifyWakeLockCallback() { + createService(); + startSystem(); + final String tag = "wakelock1"; + final String packageName = "pkg.name"; + final IBinder token = new Binder(); + final int flags = PowerManager.PARTIAL_WAKE_LOCK; + final IWakeLockCallback callback = Mockito.mock(IWakeLockCallback.class); + final IBinder callbackBinder = Mockito.mock(Binder.class); + when(callback.asBinder()).thenReturn(callbackBinder); + mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, callback); + verify(mNotifierMock).onWakeLockAcquired(anyInt(), eq(tag), eq(packageName), anyInt(), + anyInt(), any(), any(), same(callback)); + + mService.getBinderServiceInstance().releaseWakeLock(token, 0); + verify(mNotifierMock).onWakeLockReleased(anyInt(), eq(tag), eq(packageName), anyInt(), + anyInt(), any(), any(), same(callback)); + } + + /** + * Test IPowerManager.updateWakeLockCallback() with a new IWakeLockCallback. + */ + @Test + public void testNotifyWakeLockCallbackChange() { + createService(); + startSystem(); + final String tag = "wakelock1"; + final String packageName = "pkg.name"; + final IBinder token = new Binder(); + int flags = PowerManager.PARTIAL_WAKE_LOCK; + final IWakeLockCallback callback1 = Mockito.mock(IWakeLockCallback.class); + final IBinder callbackBinder1 = Mockito.mock(Binder.class); + when(callback1.asBinder()).thenReturn(callbackBinder1); + mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, callback1); + verify(mNotifierMock).onWakeLockAcquired(anyInt(), eq(tag), eq(packageName), anyInt(), + anyInt(), any(), any(), same(callback1)); + + final IWakeLockCallback callback2 = Mockito.mock(IWakeLockCallback.class); + final IBinder callbackBinder2 = Mockito.mock(Binder.class); + when(callback2.asBinder()).thenReturn(callbackBinder2); + mService.getBinderServiceInstance().updateWakeLockCallback(token, callback2); + verify(mNotifierMock).onWakeLockChanging(anyInt(), eq(tag), eq(packageName), + anyInt(), anyInt(), any(), any(), same(callback1), + anyInt(), eq(tag), eq(packageName), anyInt(), anyInt(), any(), any(), + same(callback2)); + + mService.getBinderServiceInstance().releaseWakeLock(token, 0); + verify(mNotifierMock).onWakeLockReleased(anyInt(), eq(tag), eq(packageName), anyInt(), + anyInt(), any(), any(), same(callback2)); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java index fb1508842c9d..0f18cc61a95a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java @@ -17,7 +17,6 @@ package com.android.server.notification; import static android.app.Notification.CATEGORY_CALL; -import static android.app.Notification.CATEGORY_MESSAGE; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE; import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT; @@ -25,6 +24,7 @@ import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES; +import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; import static android.provider.Settings.Global.ZEN_MODE_ALARMS; @@ -43,8 +43,10 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager.Policy; import android.media.AudioAttributes; +import android.os.Bundle; import android.os.UserHandle; import android.service.notification.StatusBarNotification; +import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -68,10 +70,15 @@ public class ZenModeFilteringTest extends UiServiceTestCase { private NotificationMessagingUtil mMessagingUtil; private ZenModeFiltering mZenModeFiltering; + @Mock private TelephonyManager mTelephonyManager; + @Before public void setUp() { MockitoAnnotations.initMocks(this); mZenModeFiltering = new ZenModeFiltering(mContext, mMessagingUtil); + + // for repeat callers / matchesCallFilter + mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager); } private NotificationRecord getNotificationRecord() { @@ -95,6 +102,23 @@ public class ZenModeFilteringTest extends UiServiceTestCase { return r; } + private Bundle makeExtrasBundleWithPeople(String[] people) { + Bundle extras = new Bundle(); + extras.putObject(Notification.EXTRA_PEOPLE_LIST, people); + return extras; + } + + private NotificationRecord getNotificationRecordWithPeople(String[] people) { + // set up notification record + NotificationRecord r = mock(NotificationRecord.class); + StatusBarNotification sbn = mock(StatusBarNotification.class); + Notification notification = mock(Notification.class); + notification.extras = makeExtrasBundleWithPeople(people); + when(sbn.getNotification()).thenReturn(notification); + when(r.getSbn()).thenReturn(sbn); + return r; + } + @Test public void testIsMessage() { NotificationRecord r = getNotificationRecord(); @@ -309,4 +333,111 @@ public class ZenModeFilteringTest extends UiServiceTestCase { assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r)); } + + @Test + public void testMatchesCallFilter_repeatCallers_directMatch() { + // after calls given an email with an exact string match, make sure that + // matchesCallFilter returns the right thing + String[] mailSource = new String[]{"mailto:hello.world"}; + mZenModeFiltering.recordCall(getNotificationRecordWithPeople(mailSource)); + + // set up policy to only allow repeat callers + Policy policy = new Policy( + PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE); + + // check whether matchesCallFilter returns the right thing + Bundle inputMatches = makeExtrasBundleWithPeople(new String[]{"mailto:hello.world"}); + Bundle inputWrong = makeExtrasBundleWithPeople(new String[]{"mailto:nope"}); + assertTrue(ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + inputMatches, null, 0, 0)); + assertFalse(ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + inputWrong, null, 0, 0)); + } + + @Test + public void testMatchesCallFilter_repeatCallers_telephoneVariants() { + // set up telephony manager behavior + when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us"); + + String[] telSource = new String[]{"tel:+1-617-555-1212"}; + mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource)); + + // set up policy to only allow repeat callers + Policy policy = new Policy( + PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE); + + // cases to test: + // - identical number + // - same number, different formatting + // - different number + // - garbage + Bundle identical = makeExtrasBundleWithPeople(new String[]{"tel:+1-617-555-1212"}); + Bundle same = makeExtrasBundleWithPeople(new String[]{"tel:16175551212"}); + Bundle different = makeExtrasBundleWithPeople(new String[]{"tel:123-456-7890"}); + Bundle garbage = makeExtrasBundleWithPeople(new String[]{"asdfghjkl;"}); + + assertTrue("identical numbers should match", + ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + identical, null, 0, 0)); + assertTrue("equivalent but non-identical numbers should match", + ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + same, null, 0, 0)); + assertFalse("non-equivalent numbers should not match", + ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + different, null, 0, 0)); + assertFalse("non-tel strings should not match", + ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + garbage, null, 0, 0)); + } + + @Test + public void testMatchesCallFilter_repeatCallers_urlEncodedTels() { + // this is not intended to be a supported case but is one that we have seen + // sometimes in the wild, so make sure we handle url-encoded telephone numbers correctly + // when somebody provides one. + + // set up telephony manager behavior + when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us"); + + String[] telSource = new String[]{"tel:%2B16175551212"}; + mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource)); + + // set up policy to only allow repeat callers + Policy policy = new Policy( + PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE); + + // test cases for various forms of the same phone number and different ones + Bundle same1 = makeExtrasBundleWithPeople(new String[]{"tel:+1-617-555-1212"}); + Bundle same2 = makeExtrasBundleWithPeople(new String[]{"tel:%2B1-617-555-1212"}); + Bundle same3 = makeExtrasBundleWithPeople(new String[]{"tel:6175551212"}); + Bundle different1 = makeExtrasBundleWithPeople(new String[]{"tel:%2B16175553434"}); + Bundle different2 = makeExtrasBundleWithPeople(new String[]{"tel:+16175553434"}); + + assertTrue("same number should match", + ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + same1, null, 0, 0)); + assertTrue("same number should match", + ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + same2, null, 0, 0)); + assertTrue("same number should match", + ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + same3, null, 0, 0)); + assertFalse("different number should not match", + ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + different1, null, 0, 0)); + assertFalse("different number should not match", + ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + different2, null, 0, 0)); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 7c340ecac2c7..8ada97147dd3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -191,7 +191,8 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { public void onFixedRotationFinished(int displayId) {} @Override - public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {} + public void onKeepClearAreasChanged(int displayId, List<Rect> restricted, + List<Rect> unrestricted) {} }; int[] displayIds = mAtm.mWindowManager.registerDisplayWindowListener(listener); for (int i = 0; i < displayIds.length; i++) { |